require 'facet/string/capitalized' require 'facet/string/camelize' require 'facet/string/underscore' require 'facet/dir/self/recurse' require 'facet/annotation' require 'glue/flexob' require 'glue/configuration' require 'glue/template' module Nitro # A programmatically generated element. Elements are a form # of macros to allow for cleaner templates. They are evaluated # at compile time, so there is no performance hit when you use # them (at the expense of slightly reduced functionality). # # Nitro provides an additional method of defining elements. # Instead of creating a lot of small classes, you can put # .xhtml templates in the Element template_root. These templates # are automatically converted into Element classes. # # For extra safety, you are advised to place your classes in the # Nitro::Element namespace. If your classes do not extend # Nitro::Element, the Nitro::ElementMixin is automatically # injected in the class. # # An element can have and access a hierarchy of sub-elements. # use #{content :sub_element_name} to access the render output # of the subelement. Additionaly you can access the whole # subelement object: _children[:sub_element_name] # # === Design # # An underscore is used for the standard attibutes to # avoid name clashes. #-- # TODO: # * separate 'view' template files. #++ module ElementMixin # The parent of this element. attr_accessor :_parent # The children of this element. attr_accessor :_children alias_method :children, :_children # The text of this element. attr_accessor :_text # The view of this element. attr_accessor :_view def initialize(*args) @_children = {} @_text = '' end def open end # If an optional name parameter is passed renders # the content of the named child element. def content(cname = nil) if cname @_children[cname].content else @_text end end def close end # Renders the actual content of this Element. def render "#{open}#{content}#{close}" end def render_children str = '' for c in @_children.values str << c.render end return str end def add_child(child) child._parent = self @_children[Element.class_to_name(child.class)] = child end alias_method :children, :_children end # A programmatically generated element. # # === Design # # An underscore is used for the standard attibutes to # avoid name clashes. #-- # TODO: # * separate 'view' template files. #++ class Element include ElementMixin # The prefix for element tags (in xhtml compatibility mode) setting :prefix, :default => 'x', :doc => 'The prefix for element tags' # Allow auto extension of element classes? setting :auto_extend, :default => true, :doc => 'Allow auto extension of element classes?' # The directory where element templates reside. The default # dir is #{Template.root}/element if File.exist?(File.join(Template.root, 'element')) default_root = File.join(Template.root, 'element') else default_root = 'element' end if File.exist?('src/element') default_root = 'src/element' elsif File.exist?('src/template/element') default_root = 'src/template/element' else default_root = 'element' end setting :template_root, :default => default_root, :doc => 'The directory where element templates reside' class << self # Return the name for an Element class. def class_to_name(klass) klass.to_s.underscore.to_sym end # Compile the element templates into element classes. # Typically called at startup. def compile_template_elements if File.exist? Element.template_root Dir.recurse(Element.template_root) do |filename| if filename =~ /\.#{Template.extension}$/ name = File.basename(filename).split('.').first.camelize Nitro::Element.module_eval %{ class #{name} < Nitro::Element def render <<-END_OF_TEMPLATE #{File.read(filename)} END_OF_TEMPLATE end end } end end end end end end # Compile the element templates into element classes. Element.compile_template_elements end # * George Moschovitis