108 lines
3.6 KiB
Ruby
108 lines
3.6 KiB
Ruby
|
module RablFastJson
|
||
|
module Renderers
|
||
|
class Base
|
||
|
|
||
|
def initialize(context) # :nodoc:
|
||
|
@_context = context
|
||
|
setup_render_context
|
||
|
end
|
||
|
|
||
|
#
|
||
|
# Render a template.
|
||
|
# Uses the compiled template source to get a hash with the actual
|
||
|
# data and then format the result according to the `format_result`
|
||
|
# method defined by the renderer.
|
||
|
#
|
||
|
def render(template)
|
||
|
collection_or_resource = @_context.instance_variable_get(template.data) if template.data
|
||
|
output_hash = collection_or_resource.respond_to?(:each) ? render_collection(collection_or_resource, template.source) :
|
||
|
render_resource(collection_or_resource, template.source)
|
||
|
output_hash = { template.root_name => output_hash } if template.root_name
|
||
|
format_output(output_hash)
|
||
|
end
|
||
|
|
||
|
#
|
||
|
# Format a hash into the desired output.
|
||
|
# Renderer subclasses must implement this method
|
||
|
#
|
||
|
def format_output(hash)
|
||
|
raise "Muse be implemented by renderer"
|
||
|
end
|
||
|
|
||
|
protected
|
||
|
|
||
|
#
|
||
|
# Render a single resource as a hash, according to the compiled
|
||
|
# template source passed.
|
||
|
#
|
||
|
def render_resource(data, source)
|
||
|
source.inject({}) { |output, current|
|
||
|
key, value = current
|
||
|
|
||
|
out = case value
|
||
|
when Symbol
|
||
|
data.send(value) # attributes
|
||
|
when Proc
|
||
|
instance_exec data, &value # node
|
||
|
when Array # node with condition
|
||
|
next output if !instance_exec data, &(value.first)
|
||
|
instance_exec data, &(value.last)
|
||
|
when Hash
|
||
|
current_value = value.dup
|
||
|
data_symbol = current_value.delete(:_data)
|
||
|
object = data_symbol.nil? ? data : data_symbol.to_s.start_with?('@') ? @_context.instance_variable_get(data_symbol) : data.send(data_symbol)
|
||
|
|
||
|
if key.to_s.start_with?('_') # glue
|
||
|
current_value.each_pair { |k, v|
|
||
|
output[k] = object.send(v)
|
||
|
}
|
||
|
next output
|
||
|
else # child
|
||
|
object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value)
|
||
|
end
|
||
|
end
|
||
|
output[key] = out
|
||
|
output
|
||
|
}
|
||
|
end
|
||
|
|
||
|
#
|
||
|
# Call the render_resource mtehod on each object of the collection
|
||
|
# and return an array of the returned values.
|
||
|
#
|
||
|
def render_collection(collection, source)
|
||
|
collection.map { |o| render_resource(o, source) }
|
||
|
end
|
||
|
|
||
|
def partial(template_path, options = {})
|
||
|
raise "No object was given to partial" unless options[:object]
|
||
|
object = options[:object]
|
||
|
|
||
|
return [] if object.respond_to?(:empty?) && object.empty?
|
||
|
|
||
|
template = Library.instance.get(template_path)
|
||
|
object.respond_to?(:each) ? render_collection(object, template.source) : render_resource(object, template.source)
|
||
|
end
|
||
|
|
||
|
#
|
||
|
# If a method is called inside a 'node' property or a 'if' lambda
|
||
|
# it will be passed to context if it exists or treated as a standard
|
||
|
# missing method.
|
||
|
#
|
||
|
def method_missing(name, *args, &block)
|
||
|
@context.respond_to?(name) ? @context.send(name, *args, &block) : super
|
||
|
end
|
||
|
|
||
|
#
|
||
|
# Copy assigns from controller's context into this
|
||
|
# renderer context to include instances variables when
|
||
|
# evaluating 'node' properties.
|
||
|
#
|
||
|
def setup_render_context
|
||
|
@_context.instance_variable_get(:@_assigns).each_pair { |k, v|
|
||
|
instance_variable_set("@#{k}", v) unless k.start_with?('_') || k == @data
|
||
|
}
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|