lib/express_templates/expander.rb in express_templates-0.2.0 vs lib/express_templates/expander.rb in express_templates-0.2.2
- old
+ new
@@ -2,76 +2,91 @@
module ExpressTemplates
class Expander
cattr_accessor :module_search_space
- attr_accessor :stack
+ attr_accessor :stack, :handlers, :locals
- def self.expand(template, source)
- expanded = new(template).expand(source)
- compiled = expanded.map(&:compile)
- return compiled.join("+")
- end
-
- def initialize(template)
+ def initialize(template, handlers = {}, locals = {})
@template = template
@stack = Stack.new
+ @handlers = handlers
+ @locals = locals
end
def expand(source=nil, &block)
- if source
- modified = source.gsub(/(\W)(yield)(\.*)?/, '\1 (stack << ExpressTemplates::Components::Yielder.new(\3))')
- modified.gsub!(/(\W)(@\w+)(\W)?/, '\1 (stack << ExpressTemplates::Components::Wrapper.new("\2") )\3')
+ case
+ when block.nil? && source
+ modified = _wrap_instance_vars( _replace_yield_with_yielder(source) )
instance_eval(modified, @template.inspect)
- stack.current
+ when block
+ instance_exec &block
else
- instance_eval &block
- stack.current
+ raise ArgumentError
end
+ stack.current
end
+ def process_children!(parent, &block)
+ begin
+ stack.descend!
+ result = instance_exec &block
+ if stack.current.empty? && result.is_a?(String)
+ stack << result
+ end
+ parent.children += stack.current
+ stack.ascend!
+ end
+ stack.current
+ end
+
# define a "macro" method for a component
# these methods accept args which are passed to the
# initializer for the component
- # blocks supplied are evaluated and any returned objects are
- # added as children to the component
+ # blocks supplied are evaluated and children added to the "stack"
+ # are added as children to the component
def self.register_macros_for(*components)
components.each do |component|
define_method(component.macro_name.to_sym) do |*args, &block|
- stack << if block
- begin
- stack.descend!
- block.call
- # anything stored on stack.current or on stack.next is added as a child
- # this is a bit problematic in the case where we would have
- # blocks and helpers or locals mixed
- component.new(*(args.push(*(stack.current))))
- ensure
- stack.ascend!
- end
- else
- component.new(*(args))
- end
+ new_component = component.new(*(args.push(self)))
+ process_children!(new_component, &block) unless block.nil?
+ stack << new_component
end
end
end
- @module_search_space = [ExpressTemplates::Components]
+ @module_search_space = [ExpressTemplates::Markup, ExpressTemplates::Components]
@module_search_space.each do |mod|
register_macros_for(*
mod.constants.map { |const| [mod.to_s, const.to_s].join("::").constantize }.
- select { |klass| klass.ancestors.include? (ExpressTemplates::Component) }
+ select { |klass| klass.ancestors.include? (ExpressTemplates::Markup::Tag) }
)
end
- def method_missing(name, *args)
- stack.current << ExpressTemplates::Components::Wrapper.new(name.to_s, *args)
+ def method_missing(name, *args, &block)
+ return locals[name] if locals.keys.include?(name)
+
+ if handlers.keys.include?(name)
+ stack << handlers[name].send(name, *args)
+ else
+ stack << ExpressTemplates::Markup::Wrapper.new(name.to_s, *args, &block)
+ end
nil
end
+ private
+
+ def _replace_yield_with_yielder(source)
+ source.gsub(/(\W)(yield)(\([^\)]*\))?/, '\1 (stack << ExpressTemplates::Markup::Yielder.new\3)')
+ end
+
+ def _wrap_instance_vars(source)
+ source.gsub(/(\W)(@\w+)(\W)?/, '\1 (stack << ExpressTemplates::Markup::Wrapper.new("\2") )\3')
+ end
+
class Stack
def initialize
@stack = [[]]
@frame = 0
end
@@ -80,10 +95,11 @@
@stack
end
def dump
puts "Current frame: #{@frame}"
- puts all.map(&:inspect).join("\n")
+ require 'pp'
+ pp all
end
def <<(child)
current << child
child
\ No newline at end of file