Simplify compiler and document it
This commit is contained in:
parent
d9e93b2c53
commit
03bc0f2644
@ -1,24 +1,63 @@
|
||||
module RablFastJson
|
||||
#
|
||||
# Class that will compile RABL source code into a hash
|
||||
# representing data structure
|
||||
#
|
||||
class Compiler
|
||||
include Helpers
|
||||
|
||||
def initialize(context = nil)
|
||||
@context = context
|
||||
def initialize
|
||||
@glue_count = 0
|
||||
end
|
||||
|
||||
#
|
||||
# Compile from source code and return the CompiledTemplate
|
||||
# created.
|
||||
#
|
||||
def compile_source(source)
|
||||
@template = CompiledTemplate.new
|
||||
instance_eval(source)
|
||||
@template
|
||||
end
|
||||
|
||||
#
|
||||
# Same as compile_source but from a block
|
||||
#
|
||||
def compile_block(&block)
|
||||
@template = {}
|
||||
instance_eval(&block)
|
||||
@template
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the object to be used as the data for the template
|
||||
# Example:
|
||||
# object :@user
|
||||
# object :@user, :root => :author
|
||||
#
|
||||
def object(data, options = {})
|
||||
data, name = extract_data_and_name(data)
|
||||
@template.data = data
|
||||
@template.root_name = options[:root] || name
|
||||
end
|
||||
|
||||
#
|
||||
# Sets a collection to be used as data for the template
|
||||
# Example:
|
||||
# collection :@users
|
||||
# collection :@users, :root => :morons
|
||||
#
|
||||
def collection(data, options = {})
|
||||
object(data)
|
||||
@template.root_name = options[:root] if root_given?(options)
|
||||
end
|
||||
|
||||
#
|
||||
# Includes the attribute or method in the output
|
||||
# Example:
|
||||
# attributes :id, :name
|
||||
# attribute :email => :super_secret
|
||||
#
|
||||
def attribute(*args)
|
||||
if args.first.is_a?(Hash)
|
||||
args.first.each_pair { |k, v| @template[v] = k }
|
||||
@ -28,17 +67,31 @@ module RablFastJson
|
||||
end
|
||||
alias_method :attributes, :attribute
|
||||
|
||||
#
|
||||
# Creates a child node to be included in the output.
|
||||
# name_or data can be an object or collection or a method to call on the data. It
|
||||
# accepts :root and :partial options.
|
||||
# Notes that partial and blocks are not compatible
|
||||
# Example:
|
||||
# child(:@posts, :root => :posts) { attribute :id }
|
||||
# child(:posts, :partial => 'posts/base')
|
||||
#
|
||||
def child(name_or_data, options = {}, &block)
|
||||
data, name = extract_data_and_name(name_or_data)
|
||||
name = options[:root] if root_given?(options)
|
||||
if partial_given?(options)
|
||||
template = Library.instance.get(options[:partial], @context)
|
||||
template = Library.instance.get(options[:partial])
|
||||
@template[name] = template.merge!(:_data => data)
|
||||
else
|
||||
_compile_sub_template(name, data, &block)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Glues data from a child node to the output
|
||||
# Example:
|
||||
# glue(:@user) { attribute :name }
|
||||
#
|
||||
def glue(data, &block)
|
||||
return unless block_given?
|
||||
name = :"_glue#{@glue_count}"
|
||||
@ -46,6 +99,14 @@ module RablFastJson
|
||||
_compile_sub_template(name, data, &block)
|
||||
end
|
||||
|
||||
#
|
||||
# Creates an arbitrary node in the json output.
|
||||
# It accepts :if option to create conditionnal nodes. The current data will
|
||||
# be passed to the block so it is advised to use it instead of ivars.
|
||||
# Example:
|
||||
# node(:name) { |user| user.first_name + user.last_name }
|
||||
# node(:role, if: ->(u) { !u.admin? }) { |u| u.role }
|
||||
#
|
||||
def node(name, options = {}, &block)
|
||||
condition = options[:if]
|
||||
|
||||
@ -61,24 +122,24 @@ module RablFastJson
|
||||
end
|
||||
alias_method :code, :node
|
||||
|
||||
def collection(data, options = {})
|
||||
object(data)
|
||||
@template.root_name = options[:root] if root_given?(options)
|
||||
end
|
||||
|
||||
#
|
||||
# Extends an existing rabl template
|
||||
# Example:
|
||||
# extends 'users/base'
|
||||
#
|
||||
def extends(path)
|
||||
t = Library.instance.get(path, @context)
|
||||
t = Library.instance.get(path)
|
||||
@template.merge!(t.source)
|
||||
end
|
||||
|
||||
def object(data, options = {})
|
||||
data, name = extract_data_and_name(data)
|
||||
@template.data = data
|
||||
@template.root_name = options[:root] || name
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Extract data root_name and root name
|
||||
# Example:
|
||||
# :@users -> [:@users, nil]
|
||||
# :@users => :authors -> [:@users, :authors]
|
||||
#
|
||||
def extract_data_and_name(name_or_data)
|
||||
case name_or_data
|
||||
when Symbol
|
||||
@ -94,8 +155,8 @@ module RablFastJson
|
||||
end
|
||||
end
|
||||
|
||||
def _compile_sub_template(name, data, &block)
|
||||
compiler = Compiler.new(@context)
|
||||
def _compile_sub_template(name, data, &block) #:nodoc:
|
||||
compiler = Compiler.new
|
||||
template = compiler.compile_block(&block)
|
||||
@template[name] = template.merge!(:_data => data)
|
||||
end
|
||||
|
@ -1,10 +1,10 @@
|
||||
module RablFastJson
|
||||
module Helpers
|
||||
def root_given?(options)
|
||||
def root_given?(options) #:nodoc:
|
||||
options[:root].present?
|
||||
end
|
||||
|
||||
def partial_given?(options)
|
||||
def partial_given?(options) #:nodoc:
|
||||
options[:partial].present?
|
||||
end
|
||||
end
|
||||
|
@ -14,22 +14,22 @@ module RablFastJson
|
||||
path = context.instance_variable_get(:@virtual_path)
|
||||
@view_renderer = context.instance_variable_get(:@view_renderer)
|
||||
|
||||
compiled_template = get_compiled_template(path, source, context)
|
||||
compiled_template = get_compiled_template(path, source)
|
||||
compiled_template.context = context
|
||||
body = compiled_template.render
|
||||
ActiveSupport::JSON.encode(compiled_template.has_root_name? ? { compiled_template.root_name => body } : body)
|
||||
end
|
||||
|
||||
def get_compiled_template(path, source, context)
|
||||
def get_compiled_template(path, source)
|
||||
# @cached_templates[path] ||=
|
||||
Compiler.new(context).compile_source(source)
|
||||
Compiler.new.compile_source(source)
|
||||
end
|
||||
|
||||
def get(path, context)
|
||||
def get(path)
|
||||
template = @cached_templates[path]
|
||||
return template if !template.nil?
|
||||
t = @view_renderer.lookup_context.find_template(path, [], false)
|
||||
get_compiled_template(path, t.source, context)
|
||||
get_compiled_template(path, t.source)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,6 +1,5 @@
|
||||
module RablFastJson
|
||||
class CompiledTemplate
|
||||
|
||||
attr_accessor :source, :data, :root_name, :context
|
||||
|
||||
delegate :[], :[]=, :merge!, :to => :source
|
||||
@ -9,38 +8,10 @@ module RablFastJson
|
||||
@source = {}
|
||||
end
|
||||
|
||||
#
|
||||
# def data=(symbol)
|
||||
# raise "Data passed directly to template should be a symbol" if !symbol.is_a?(Symbol)
|
||||
# @data = symbol
|
||||
# end
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
def has_root_name?
|
||||
!@root_name.nil?
|
||||
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].blank?
|
||||
object = options[:object]
|
||||
template = Library.instance.get(template_path, @context)
|
||||
object.respond_to?(:each) ? template.render_collection(object) : template.render_resource(object)
|
||||
end
|
||||
|
||||
def render
|
||||
get_object_from_context
|
||||
get_assigns_from_context
|
||||
@ -86,5 +57,27 @@ module RablFastJson
|
||||
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].blank?
|
||||
object = options[:object]
|
||||
template = Library.instance.get(template_path, @context)
|
||||
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
|
@ -3,10 +3,8 @@ require 'test_helper'
|
||||
class CompilerTest < ActiveSupport::TestCase
|
||||
|
||||
setup do
|
||||
@context = Context.new
|
||||
@user = User.new
|
||||
@context.set_assign('user', @user)
|
||||
@compiler = RablFastJson::Compiler.new(@context)
|
||||
@compiler = RablFastJson::Compiler.new
|
||||
end
|
||||
|
||||
test "compiler return a compiled template" do
|
||||
@ -112,7 +110,7 @@ class CompilerTest < ActiveSupport::TestCase
|
||||
test "extends use other template source as itself" do
|
||||
template = mock('template', :source => { :id => :id })
|
||||
RablFastJson::Library.reset_instance
|
||||
RablFastJson::Library.instance.stub(:get).with('users/base', @context).and_return(template)
|
||||
RablFastJson::Library.instance.stub(:get).with('users/base').and_return(template)
|
||||
t = @compiler.compile_source(%{ extends 'users/base' })
|
||||
assert_equal({ :id => :id }, t.source)
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user