Extract rendering from template into renderers
This commit is contained in:
parent
1f36248f13
commit
52941e6232
@ -8,20 +8,25 @@ require 'rabl-fast-json/version'
|
||||
require 'rabl-fast-json/helpers'
|
||||
require 'rabl-fast-json/template'
|
||||
require 'rabl-fast-json/compiler'
|
||||
|
||||
require 'rabl-fast-json/renderer'
|
||||
|
||||
require 'rabl-fast-json/library'
|
||||
require 'rabl-fast-json/handler'
|
||||
require 'rabl-fast-json/railtie'
|
||||
|
||||
|
||||
|
||||
module RablFastJson
|
||||
extend self
|
||||
|
||||
|
||||
mattr_accessor :cache_templates
|
||||
@@cache_templates = true
|
||||
|
||||
def configure
|
||||
yield self
|
||||
end
|
||||
|
||||
|
||||
def cache_templates?
|
||||
ActionController::Base.perform_caching && @@cache_templates
|
||||
end
|
||||
|
@ -4,7 +4,7 @@ module RablFastJson
|
||||
cattr_accessor :default_format
|
||||
self.default_format = 'application/json'
|
||||
|
||||
def self.call(template)
|
||||
def self.call(template)
|
||||
%{
|
||||
RablFastJson::Library.instance.
|
||||
get_rendered_template(#{template.source.inspect}, self)
|
||||
|
@ -12,12 +12,12 @@ module RablFastJson
|
||||
|
||||
def get_rendered_template(source, context)
|
||||
path = context.instance_variable_get(:@virtual_path)
|
||||
@view_renderer = context.instance_variable_get(:@view_renderer)
|
||||
@lookup_context = context.lookup_context
|
||||
|
||||
compiled_template = get_compiled_template(path, source)
|
||||
compiled_template.context = context
|
||||
body = compiled_template.render
|
||||
ActiveSupport::JSON.encode(compiled_template.root_name ? { compiled_template.root_name => body } : body)
|
||||
|
||||
format = context.params[:format] || 'json'
|
||||
Renderers.const_get(format.upcase!).new(context).render(compiled_template)
|
||||
end
|
||||
|
||||
def get_compiled_template(path, source)
|
||||
@ -32,7 +32,7 @@ module RablFastJson
|
||||
def get(path)
|
||||
template = @cached_templates[path]
|
||||
return template unless template.nil?
|
||||
t = @view_renderer.lookup_context.find_template(path, [], false)
|
||||
t = @lookup_context.find_template(path, [], false)
|
||||
get_compiled_template(path, t.source)
|
||||
end
|
||||
end
|
||||
|
2
lib/rabl-fast-json/renderer.rb
Normal file
2
lib/rabl-fast-json/renderer.rb
Normal file
@ -0,0 +1,2 @@
|
||||
require 'rabl-fast-json/renderers/base'
|
||||
require 'rabl-fast-json/renderers/json'
|
108
lib/rabl-fast-json/renderers/base.rb
Normal file
108
lib/rabl-fast-json/renderers/base.rb
Normal file
@ -0,0 +1,108 @@
|
||||
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
|
9
lib/rabl-fast-json/renderers/json.rb
Normal file
9
lib/rabl-fast-json/renderers/json.rb
Normal file
@ -0,0 +1,9 @@
|
||||
module RablFastJson
|
||||
module Renderers
|
||||
class JSON < Base
|
||||
def format_output(hash)
|
||||
ActiveSupport::JSON.encode(hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,86 +1,11 @@
|
||||
module RablFastJson
|
||||
class CompiledTemplate
|
||||
attr_accessor :source, :data, :root_name, :context
|
||||
attr_accessor :source, :data, :root_name
|
||||
|
||||
delegate :[], :[]=, :merge!, :to => :source
|
||||
|
||||
def initialize
|
||||
@source = {}
|
||||
end
|
||||
|
||||
def has_root_name?
|
||||
!@root_name.nil?
|
||||
end
|
||||
|
||||
def render
|
||||
get_object_from_context
|
||||
get_assigns_from_context
|
||||
@object.respond_to?(:each) ? render_collection : render_resource
|
||||
end
|
||||
|
||||
def render_resource(data = nil, source = nil)
|
||||
data ||= @object
|
||||
source ||= @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) #value.first.call(data)
|
||||
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
|
||||
|
||||
def render_collection(collection = nil, source = nil)
|
||||
collection ||= @object
|
||||
collection.inject([]) { |output, o| output << render_resource(o, source) }
|
||||
end
|
||||
|
||||
def method_missing(name, *args, &block)
|
||||
@context.respond_to?(name) ? @context.send(name, *args, &block) : super
|
||||
end
|
||||
|
||||
def partial(template_path, options = {})
|
||||
raise "No object was given to partial" if options[:object].nil?
|
||||
object = options[:object]
|
||||
|
||||
return [] if object.respond_to?(:empty?) && object.empty?
|
||||
|
||||
template = Library.instance.get(template_path)
|
||||
object.respond_to?(:each) ? template.render_collection(object) : template.render_resource(object)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def get_object_from_context
|
||||
@object = @context.instance_variable_get(@data) if @data
|
||||
end
|
||||
|
||||
def get_assigns_from_context
|
||||
@context.instance_variable_get(:@_assigns).each_pair { |k, v|
|
||||
instance_variable_set("@#{k}", v) unless k.start_with?('_') || k == @data
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
@ -4,31 +4,31 @@ class CacheTemplatesTest < ActiveSupport::TestCase
|
||||
|
||||
setup do
|
||||
RablFastJson::Library.reset_instance
|
||||
@library = RablFastJson::Library.instance
|
||||
end
|
||||
|
||||
test "cache templates if perform_caching is active and cache_templates is enabled" do
|
||||
@library = RablFastJson::Library.instance
|
||||
RablFastJson.cache_templates = true
|
||||
ActionController::Base.stub(:perform_caching).and_return(true)
|
||||
end
|
||||
|
||||
test "cache templates if perform_caching is active and cache_templates is enabled" do
|
||||
ActionController::Base.stub(:perform_caching).and_return(true)
|
||||
@library.get_compiled_template('some/path', "")
|
||||
t = @library.get_compiled_template('some/path', "attribute :id")
|
||||
|
||||
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
|
||||
test "cached templates should not be modifiable in place" do
|
||||
RablFastJson.cache_templates = true
|
||||
ActionController::Base.stub(:perform_caching).and_return(true)
|
||||
t = @library.get_compiled_template('some/path', "")
|
||||
assert_nil t.context
|
||||
t.context = "foobar"
|
||||
assert_nil @library.get_compiled_template('some/path', "").context
|
||||
end
|
||||
|
||||
|
||||
test "don't cache templates cache_templates is enabled but perform_caching is not active" do
|
||||
RablFastJson.cache_templates = true
|
||||
ActionController::Base.stub(:perform_caching).and_return(false)
|
||||
|
||||
refute_equal @library.get_compiled_template('some/path', ""), @library.get_compiled_template('some/path', "")
|
||||
ActionController::Base.stub(:perform_caching).and_return(false)
|
||||
@library.get_compiled_template('some/path', "")
|
||||
t = @library.get_compiled_template('some/path', "attribute :id")
|
||||
|
||||
assert_equal({ :id => :id }, t.source)
|
||||
end
|
||||
end
|
@ -13,6 +13,10 @@ class TestCompiledTemplate < ActiveSupport::TestCase
|
||||
@template.data = :@data
|
||||
end
|
||||
|
||||
def render_json_output
|
||||
RablFastJson::Renderers::JSON.new(@context).render(@template)
|
||||
end
|
||||
|
||||
test "render object wth empty template" do
|
||||
@template.source = {}
|
||||
assert_equal({}, @template.render)
|
||||
|
Loading…
Reference in New Issue
Block a user