From 03bc0f264437197a5cb49795dc19c03e23e96cc7 Mon Sep 17 00:00:00 2001 From: ccocchi Date: Tue, 27 Mar 2012 18:35:36 +0200 Subject: [PATCH] Simplify compiler and document it --- lib/rabl-fast-json/compiler.rb | 95 ++++++++++++++++++++++++++++------ lib/rabl-fast-json/helpers.rb | 4 +- lib/rabl-fast-json/library.rb | 10 ++-- lib/rabl-fast-json/template.rb | 53 ++++++++----------- test/compiler_test.rb | 6 +-- 5 files changed, 110 insertions(+), 58 deletions(-) diff --git a/lib/rabl-fast-json/compiler.rb b/lib/rabl-fast-json/compiler.rb index 016a887..a905199 100644 --- a/lib/rabl-fast-json/compiler.rb +++ b/lib/rabl-fast-json/compiler.rb @@ -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 diff --git a/lib/rabl-fast-json/helpers.rb b/lib/rabl-fast-json/helpers.rb index 53b21b6..e105b67 100644 --- a/lib/rabl-fast-json/helpers.rb +++ b/lib/rabl-fast-json/helpers.rb @@ -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 diff --git a/lib/rabl-fast-json/library.rb b/lib/rabl-fast-json/library.rb index 4bcf258..7b73e9e 100644 --- a/lib/rabl-fast-json/library.rb +++ b/lib/rabl-fast-json/library.rb @@ -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 \ No newline at end of file diff --git a/lib/rabl-fast-json/template.rb b/lib/rabl-fast-json/template.rb index b79f703..d17987c 100644 --- a/lib/rabl-fast-json/template.rb +++ b/lib/rabl-fast-json/template.rb @@ -1,6 +1,5 @@ module RablFastJson class CompiledTemplate - attr_accessor :source, :data, :root_name, :context delegate :[], :[]=, :merge!, :to => :source @@ -8,38 +7,10 @@ module RablFastJson def initialize @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 @@ -85,6 +56,28 @@ module RablFastJson 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].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 \ No newline at end of file diff --git a/test/compiler_test.rb b/test/compiler_test.rb index 139211d..a1217fe 100644 --- a/test/compiler_test.rb +++ b/test/compiler_test.rb @@ -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