Simplify compiler and document it
This commit is contained in:
parent
d9e93b2c53
commit
03bc0f2644
|
@ -1,24 +1,63 @@
|
||||||
module RablFastJson
|
module RablFastJson
|
||||||
|
#
|
||||||
|
# Class that will compile RABL source code into a hash
|
||||||
|
# representing data structure
|
||||||
|
#
|
||||||
class Compiler
|
class Compiler
|
||||||
include Helpers
|
include Helpers
|
||||||
|
|
||||||
def initialize(context = nil)
|
def initialize
|
||||||
@context = context
|
|
||||||
@glue_count = 0
|
@glue_count = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Compile from source code and return the CompiledTemplate
|
||||||
|
# created.
|
||||||
|
#
|
||||||
def compile_source(source)
|
def compile_source(source)
|
||||||
@template = CompiledTemplate.new
|
@template = CompiledTemplate.new
|
||||||
instance_eval(source)
|
instance_eval(source)
|
||||||
@template
|
@template
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Same as compile_source but from a block
|
||||||
|
#
|
||||||
def compile_block(&block)
|
def compile_block(&block)
|
||||||
@template = {}
|
@template = {}
|
||||||
instance_eval(&block)
|
instance_eval(&block)
|
||||||
@template
|
@template
|
||||||
end
|
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)
|
def attribute(*args)
|
||||||
if args.first.is_a?(Hash)
|
if args.first.is_a?(Hash)
|
||||||
args.first.each_pair { |k, v| @template[v] = k }
|
args.first.each_pair { |k, v| @template[v] = k }
|
||||||
|
@ -28,17 +67,31 @@ module RablFastJson
|
||||||
end
|
end
|
||||||
alias_method :attributes, :attribute
|
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)
|
def child(name_or_data, options = {}, &block)
|
||||||
data, name = extract_data_and_name(name_or_data)
|
data, name = extract_data_and_name(name_or_data)
|
||||||
name = options[:root] if root_given?(options)
|
name = options[:root] if root_given?(options)
|
||||||
if partial_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)
|
@template[name] = template.merge!(:_data => data)
|
||||||
else
|
else
|
||||||
_compile_sub_template(name, data, &block)
|
_compile_sub_template(name, data, &block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Glues data from a child node to the output
|
||||||
|
# Example:
|
||||||
|
# glue(:@user) { attribute :name }
|
||||||
|
#
|
||||||
def glue(data, &block)
|
def glue(data, &block)
|
||||||
return unless block_given?
|
return unless block_given?
|
||||||
name = :"_glue#{@glue_count}"
|
name = :"_glue#{@glue_count}"
|
||||||
|
@ -46,6 +99,14 @@ module RablFastJson
|
||||||
_compile_sub_template(name, data, &block)
|
_compile_sub_template(name, data, &block)
|
||||||
end
|
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)
|
def node(name, options = {}, &block)
|
||||||
condition = options[:if]
|
condition = options[:if]
|
||||||
|
|
||||||
|
@ -61,24 +122,24 @@ module RablFastJson
|
||||||
end
|
end
|
||||||
alias_method :code, :node
|
alias_method :code, :node
|
||||||
|
|
||||||
def collection(data, options = {})
|
#
|
||||||
object(data)
|
# Extends an existing rabl template
|
||||||
@template.root_name = options[:root] if root_given?(options)
|
# Example:
|
||||||
end
|
# extends 'users/base'
|
||||||
|
#
|
||||||
def extends(path)
|
def extends(path)
|
||||||
t = Library.instance.get(path, @context)
|
t = Library.instance.get(path)
|
||||||
@template.merge!(t.source)
|
@template.merge!(t.source)
|
||||||
end
|
end
|
||||||
|
|
||||||
def object(data, options = {})
|
|
||||||
data, name = extract_data_and_name(data)
|
|
||||||
@template.data = data
|
|
||||||
@template.root_name = options[:root] || name
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
#
|
||||||
|
# Extract data root_name and root name
|
||||||
|
# Example:
|
||||||
|
# :@users -> [:@users, nil]
|
||||||
|
# :@users => :authors -> [:@users, :authors]
|
||||||
|
#
|
||||||
def extract_data_and_name(name_or_data)
|
def extract_data_and_name(name_or_data)
|
||||||
case name_or_data
|
case name_or_data
|
||||||
when Symbol
|
when Symbol
|
||||||
|
@ -94,8 +155,8 @@ module RablFastJson
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def _compile_sub_template(name, data, &block)
|
def _compile_sub_template(name, data, &block) #:nodoc:
|
||||||
compiler = Compiler.new(@context)
|
compiler = Compiler.new
|
||||||
template = compiler.compile_block(&block)
|
template = compiler.compile_block(&block)
|
||||||
@template[name] = template.merge!(:_data => data)
|
@template[name] = template.merge!(:_data => data)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
module RablFastJson
|
module RablFastJson
|
||||||
module Helpers
|
module Helpers
|
||||||
def root_given?(options)
|
def root_given?(options) #:nodoc:
|
||||||
options[:root].present?
|
options[:root].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def partial_given?(options)
|
def partial_given?(options) #:nodoc:
|
||||||
options[:partial].present?
|
options[:partial].present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,22 +14,22 @@ module RablFastJson
|
||||||
path = context.instance_variable_get(:@virtual_path)
|
path = context.instance_variable_get(:@virtual_path)
|
||||||
@view_renderer = context.instance_variable_get(:@view_renderer)
|
@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
|
compiled_template.context = context
|
||||||
body = compiled_template.render
|
body = compiled_template.render
|
||||||
ActiveSupport::JSON.encode(compiled_template.has_root_name? ? { compiled_template.root_name => body } : body)
|
ActiveSupport::JSON.encode(compiled_template.has_root_name? ? { compiled_template.root_name => body } : body)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_compiled_template(path, source, context)
|
def get_compiled_template(path, source)
|
||||||
# @cached_templates[path] ||=
|
# @cached_templates[path] ||=
|
||||||
Compiler.new(context).compile_source(source)
|
Compiler.new.compile_source(source)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(path, context)
|
def get(path)
|
||||||
template = @cached_templates[path]
|
template = @cached_templates[path]
|
||||||
return template if !template.nil?
|
return template if !template.nil?
|
||||||
t = @view_renderer.lookup_context.find_template(path, [], false)
|
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
|
end
|
||||||
end
|
end
|
|
@ -1,6 +1,5 @@
|
||||||
module RablFastJson
|
module RablFastJson
|
||||||
class CompiledTemplate
|
class CompiledTemplate
|
||||||
|
|
||||||
attr_accessor :source, :data, :root_name, :context
|
attr_accessor :source, :data, :root_name, :context
|
||||||
|
|
||||||
delegate :[], :[]=, :merge!, :to => :source
|
delegate :[], :[]=, :merge!, :to => :source
|
||||||
|
@ -9,38 +8,10 @@ module RablFastJson
|
||||||
@source = {}
|
@source = {}
|
||||||
end
|
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?
|
def has_root_name?
|
||||||
!@root_name.nil?
|
!@root_name.nil?
|
||||||
end
|
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
|
def render
|
||||||
get_object_from_context
|
get_object_from_context
|
||||||
get_assigns_from_context
|
get_assigns_from_context
|
||||||
|
@ -86,5 +57,27 @@ module RablFastJson
|
||||||
collection.inject([]) { |output, o| output << render_resource(o, source) }
|
collection.inject([]) { |output, o| output << render_resource(o, source) }
|
||||||
end
|
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
|
||||||
end
|
end
|
|
@ -3,10 +3,8 @@ require 'test_helper'
|
||||||
class CompilerTest < ActiveSupport::TestCase
|
class CompilerTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
@context = Context.new
|
|
||||||
@user = User.new
|
@user = User.new
|
||||||
@context.set_assign('user', @user)
|
@compiler = RablFastJson::Compiler.new
|
||||||
@compiler = RablFastJson::Compiler.new(@context)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "compiler return a compiled template" do
|
test "compiler return a compiled template" do
|
||||||
|
@ -112,7 +110,7 @@ class CompilerTest < ActiveSupport::TestCase
|
||||||
test "extends use other template source as itself" do
|
test "extends use other template source as itself" do
|
||||||
template = mock('template', :source => { :id => :id })
|
template = mock('template', :source => { :id => :id })
|
||||||
RablFastJson::Library.reset_instance
|
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' })
|
t = @compiler.compile_source(%{ extends 'users/base' })
|
||||||
assert_equal({ :id => :id }, t.source)
|
assert_equal({ :id => :id }, t.source)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue