2012-02-22 18:14:00 +00:00
|
|
|
module RablFastJson
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
|
|
|
# Class that will compile RABL source code into a hash
|
|
|
|
# representing data structure
|
|
|
|
#
|
2012-02-22 18:14:00 +00:00
|
|
|
class Compiler
|
2012-03-02 13:39:20 +00:00
|
|
|
include Helpers
|
2012-02-22 18:14:00 +00:00
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
def initialize
|
2012-02-23 11:56:45 +00:00
|
|
|
@glue_count = 0
|
2012-02-22 18:14:00 +00:00
|
|
|
end
|
2012-04-08 21:26:18 +00:00
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
|
|
|
# Compile from source code and return the CompiledTemplate
|
|
|
|
# created.
|
|
|
|
#
|
2012-02-22 18:14:00 +00:00
|
|
|
def compile_source(source)
|
|
|
|
@template = CompiledTemplate.new
|
|
|
|
instance_eval(source)
|
|
|
|
@template
|
|
|
|
end
|
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
|
|
|
# Same as compile_source but from a block
|
|
|
|
#
|
2012-02-23 11:04:55 +00:00
|
|
|
def compile_block(&block)
|
|
|
|
@template = {}
|
|
|
|
instance_eval(&block)
|
|
|
|
@template
|
|
|
|
end
|
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
|
|
|
# Sets the object to be used as the data for the template
|
2012-04-08 21:26:18 +00:00
|
|
|
# Example:
|
2012-03-27 16:35:36 +00:00
|
|
|
# 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
|
2012-04-08 21:26:18 +00:00
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
|
|
|
# Includes the attribute or method in the output
|
|
|
|
# Example:
|
|
|
|
# attributes :id, :name
|
|
|
|
# attribute :email => :super_secret
|
|
|
|
#
|
2012-02-22 18:14:00 +00:00
|
|
|
def attribute(*args)
|
|
|
|
if args.first.is_a?(Hash)
|
|
|
|
args.first.each_pair { |k, v| @template[v] = k }
|
|
|
|
else
|
2012-04-08 21:26:18 +00:00
|
|
|
options = args.extract_options!
|
|
|
|
args.each { |name|
|
|
|
|
key = options[:as] || name
|
|
|
|
@template[key] = name
|
|
|
|
}
|
2012-02-22 18:14:00 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
alias_method :attributes, :attribute
|
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
|
|
|
# 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')
|
|
|
|
#
|
2012-02-23 11:04:55 +00:00
|
|
|
def child(name_or_data, options = {}, &block)
|
2012-02-23 11:56:45 +00:00
|
|
|
data, name = extract_data_and_name(name_or_data)
|
2012-03-02 13:39:20 +00:00
|
|
|
name = options[:root] if root_given?(options)
|
|
|
|
if partial_given?(options)
|
2012-03-27 16:35:36 +00:00
|
|
|
template = Library.instance.get(options[:partial])
|
2012-03-02 13:39:20 +00:00
|
|
|
@template[name] = template.merge!(:_data => data)
|
|
|
|
else
|
|
|
|
_compile_sub_template(name, data, &block)
|
|
|
|
end
|
2012-02-23 11:56:45 +00:00
|
|
|
end
|
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
|
|
|
# Glues data from a child node to the output
|
|
|
|
# Example:
|
|
|
|
# glue(:@user) { attribute :name }
|
|
|
|
#
|
2012-02-23 11:56:45 +00:00
|
|
|
def glue(data, &block)
|
|
|
|
return unless block_given?
|
|
|
|
name = :"_glue#{@glue_count}"
|
|
|
|
@glue_count += 1
|
|
|
|
_compile_sub_template(name, data, &block)
|
2012-02-23 11:04:55 +00:00
|
|
|
end
|
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
2012-04-08 21:26:18 +00:00
|
|
|
# Creates an arbitrary node in the json output.
|
2012-03-27 16:35:36 +00:00
|
|
|
# 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 }
|
|
|
|
#
|
2012-02-23 11:04:55 +00:00
|
|
|
def node(name, options = {}, &block)
|
2012-03-05 14:33:25 +00:00
|
|
|
condition = options[:if]
|
|
|
|
|
|
|
|
if condition.present?
|
|
|
|
if condition.is_a?(Proc)
|
|
|
|
@template[name] = [condition, block]
|
|
|
|
else
|
|
|
|
@template[name] = block if condition
|
|
|
|
end
|
|
|
|
else
|
|
|
|
@template[name] = block
|
|
|
|
end
|
2012-02-23 11:04:55 +00:00
|
|
|
end
|
|
|
|
alias_method :code, :node
|
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
#
|
|
|
|
# Extends an existing rabl template
|
|
|
|
# Example:
|
|
|
|
# extends 'users/base'
|
|
|
|
#
|
2012-02-27 13:49:34 +00:00
|
|
|
def extends(path)
|
2012-03-27 16:35:36 +00:00
|
|
|
t = Library.instance.get(path)
|
2012-02-27 13:49:34 +00:00
|
|
|
@template.merge!(t.source)
|
2012-02-22 18:14:00 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
2012-04-08 21:26:18 +00:00
|
|
|
#
|
2012-03-27 16:35:36 +00:00
|
|
|
# Extract data root_name and root name
|
|
|
|
# Example:
|
|
|
|
# :@users -> [:@users, nil]
|
|
|
|
# :@users => :authors -> [:@users, :authors]
|
|
|
|
#
|
2012-02-23 11:56:45 +00:00
|
|
|
def extract_data_and_name(name_or_data)
|
2012-02-23 11:04:55 +00:00
|
|
|
case name_or_data
|
|
|
|
when Symbol
|
2012-03-22 10:24:55 +00:00
|
|
|
if name_or_data.to_s.start_with?('@')
|
|
|
|
[name_or_data, nil]
|
|
|
|
else
|
|
|
|
[name_or_data, name_or_data]
|
|
|
|
end
|
2012-02-23 11:04:55 +00:00
|
|
|
when Hash
|
|
|
|
name_or_data.first
|
|
|
|
else
|
2012-02-23 11:56:45 +00:00
|
|
|
name_or_data
|
2012-02-23 11:04:55 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-03-27 16:35:36 +00:00
|
|
|
def _compile_sub_template(name, data, &block) #:nodoc:
|
|
|
|
compiler = Compiler.new
|
2012-02-23 11:56:45 +00:00
|
|
|
template = compiler.compile_block(&block)
|
|
|
|
@template[name] = template.merge!(:_data => data)
|
|
|
|
end
|
2012-02-22 18:14:00 +00:00
|
|
|
end
|
|
|
|
end
|