Testing version

This commit is contained in:
ccocchi 2012-02-27 14:49:34 +01:00
parent e24aca780d
commit bdc5148c42
10 changed files with 176 additions and 100 deletions

View File

@ -7,6 +7,7 @@ gemspec
# jquery-rails is used by the dummy application # jquery-rails is used by the dummy application
gem "jquery-rails" gem "jquery-rails"
gem 'rspec-mocks'
# Declare any dependencies that are still in development here instead of in # Declare any dependencies that are still in development here instead of in
# your gemspec. These might include edge Rails or gems from your path or # your gemspec. These might include edge Rails or gems from your path or

View File

@ -39,7 +39,7 @@ GEM
erubis (2.7.0) erubis (2.7.0)
hike (1.2.1) hike (1.2.1)
i18n (0.6.0) i18n (0.6.0)
journey (1.0.2) journey (1.0.3)
jquery-rails (2.0.0) jquery-rails (2.0.0)
railties (>= 3.2.0.beta, < 5.0) railties (>= 3.2.0.beta, < 5.0)
thor (~> 0.14) thor (~> 0.14)
@ -76,6 +76,7 @@ GEM
rake (0.9.2.2) rake (0.9.2.2)
rdoc (3.12) rdoc (3.12)
json (~> 1.4) json (~> 1.4)
rspec-mocks (2.8.0)
sprockets (2.1.2) sprockets (2.1.2)
hike (~> 1.2) hike (~> 1.2)
rack (~> 1.0) rack (~> 1.0)
@ -94,4 +95,5 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
jquery-rails jquery-rails
rabl-fast-json! rabl-fast-json!
rspec-mocks
sqlite3 sqlite3

View File

@ -7,6 +7,7 @@ require 'active_support/core_ext/class/attribute_accessors'
require 'rabl-fast-json/version' require 'rabl-fast-json/version'
require 'rabl-fast-json/template' require 'rabl-fast-json/template'
require 'rabl-fast-json/compiler' require 'rabl-fast-json/compiler'
require 'rabl-fast-json/library'
require 'rabl-fast-json/handler' require 'rabl-fast-json/handler'
require 'rabl-fast-json/railtie' require 'rabl-fast-json/railtie'

View File

@ -1,13 +1,9 @@
require 'singleton'
module RablFastJson module RablFastJson
class Compiler class Compiler
def initialize(context, assigns = nil) def initialize(context = nil, assigns = nil)
@context = context @context = context
@glue_count = 0 @glue_count = 0
_get_assigns_from_context(assigns)
_get_virtual_path_from_context
end end
def compile_source(source) def compile_source(source)
@ -41,6 +37,7 @@ module RablFastJson
return unless block_given? return unless block_given?
name = :"_glue#{@glue_count}" name = :"_glue#{@glue_count}"
@glue_count += 1 @glue_count += 1
Rails.logger.warn "[RABL] glue called with data = #{data.inspect}"
_compile_sub_template(name, data, &block) _compile_sub_template(name, data, &block)
end end
@ -50,7 +47,12 @@ module RablFastJson
alias_method :code, :node alias_method :code, :node
def collection(data, options = {}) def collection(data, options = {})
@data = data.to_a if data object(data)
end
def extends(path)
t = Library.instance.get(path)
@template.merge!(t.source)
end end
def object(data) def object(data)
@ -58,6 +60,10 @@ module RablFastJson
@template.data, @template.root_name = data, name @template.data, @template.root_name = data, name
end end
def method_missing(name, *args, &block)
@context.respond_to?(name) ? @context.send(name, *args, &block) : super
end
protected protected
def extract_data_and_name(name_or_data) def extract_data_and_name(name_or_data)
@ -76,16 +82,5 @@ module RablFastJson
template = compiler.compile_block(&block) template = compiler.compile_block(&block)
@template[name] = template.merge!(:_data => data) @template[name] = template.merge!(:_data => data)
end end
def _get_assigns_from_context(assigns)
source = assigns || @context.instance_variable_get(:@_assigns)
source.each_pair { |k, v|
instance_variable_set("@#{k}", v) unless k.start_with?('_')
}
end
def _get_virtual_path_from_context
@virtual_path = @context.instance_variable_get(:@virtual_path)
end
end end
end end

View File

@ -6,7 +6,8 @@ module RablFastJson
def self.call(template) def self.call(template)
%{ %{
RablFastJson::Compiler.instance.compile_source(#{template.source.inspect}, self) RablFastJson::Library.instance.
get_rendered_template(#{template.source.inspect}, self)
} }
end end
end end

View File

@ -0,0 +1,43 @@
require 'singleton'
module RablFastJson
class Library
include Singleton
def initialize
@cached_templates = {}
end
def get_rendered_template(source, context)
path = context.instance_variable_get(:@virtual_path)
@view_renderer = context.instance_variable_get(:@view_renderer)
start = Time.now
compiled_template = get_compiled_template(path, source, context)
compiled_time = Time.now
compiled_template.context = context
r = compiled_template.render
render_time = Time.now
res = ActiveSupport::JSON.encode(r)
final_time = Time.now
Rails.logger.warn "[BENCHMARK] Compilation:\t#{(compiled_time - start) * 1000.0}ms"
Rails.logger.warn "[BENCHMARK] Rendering:\t\t#{(render_time - compiled_time) * 1000.0}ms"
Rails.logger.warn "[BENCHMARK] JSON encoding:\t#{(final_time - render_time) * 1000.0}ms"
Rails.logger.warn "[BENCHMARK] Total:\t\t#{(final_time - start) * 1000.0}ms"
res
end
def get_compiled_template(path, source, context)
#@cached_templates[path] ||=
Compiler.new(context).compile_source(source)
end
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, nil)
end
end
end

View File

@ -1,45 +1,58 @@
module RablFastJson module RablFastJson
class CompiledTemplate class CompiledTemplate
attr_accessor :source, :data, :root_name attr_accessor :source, :data, :root_name, :context
delegate :[], :[]=, :to => :source delegate :[], :[]=, :merge!, :to => :source
def initialize def initialize
@source = {} @source = {}
end end
# def get_object_from_assigns
# def render(data = nil) @object = @context.instance_variable_get(@data)
# output = {} end
# @data = data if data
# def render
# return render_collection if @data.respond_to?(:each) get_object_from_assigns
# @object.respond_to?(:each) ? render_collection : render_resource
# @template.each_pair { |key, value| end
# out = case value
# when Symbol def render_resource(data = nil, source = nil)
# @data.send(value) output = {}
# end
# output[key] = out data ||= @object
# } source ||= @source
# output
# end source.each_pair { |key, value|
# out = case value
# def render_collection when Symbol
# output = [] data.send(value) # attributes
# @data.each { |o| when Proc
# object = {} value.call(data) # node
# @template.each_pair { |key, value| when Hash
# out = case value data_symbol = value.delete(:_data)
# when Symbol object = data_symbol.to_s.start_with?('@') ? @context.instance_variable_get(data_symbol) : @object.send(data_symbol)
# o.send(value) if key.to_s.start_with?('_') # glue
# end value.each_pair { |k, v|
# object[key] = out output[k] = object.send(v)
# } }
# output << object
# } next
# output else # child
# end object.respond_to?(:each) ? render_collection(object, value) : render_resource(object, value)
end
end
output[key] = out
}
output
end
def render_collection(collection = nil, source = nil)
output = []
collection ||= @object
collection.each { |o| output << render_resource(o, source) }
output
end
end end
end end

View File

@ -1,5 +1,12 @@
require 'test_helper' require 'test_helper'
class TestCompiledTemplate class TestCompiledTemplate < ActiveSupport::TestCase
setup do
@context = Context.new
@user = User.new
@context.set_assign('user', @user)
@template = RablFastJson::CompiledTemplate.new
@template.context = @context
end
end end

View File

@ -1,26 +1,6 @@
require 'test_helper' require 'test_helper'
class CompilerTest < ActiveSupport::TestCase class CompilerTest < ActiveSupport::TestCase
class Context
attr_accessor :virtual_path
def initialize
@_assigns = {}
@virtual_path = '/users'
end
def set_assign(key, value)
@_assigns[key] = value
end
def get_assign(key)
@_assigns[key]
end
end
class User
attr_accessor :name
end
setup do setup do
@context = Context.new @context = Context.new
@ -29,20 +9,6 @@ class CompilerTest < ActiveSupport::TestCase
@compiler = RablFastJson::Compiler.new(@context) @compiler = RablFastJson::Compiler.new(@context)
end end
test "assigns are correctly imported from context" do
assert_equal @user, @compiler.instance_variable_get(:@user)
end
test "virtual path is correctly imported from context" do
assert_equal '/users', @compiler.instance_variable_get(:@virtual_path)
end
test "assigns are not imported if already passed to the compiler" do
compiler = RablFastJson::Compiler.new(@context, { 'other' => 'foo' })
assert_nil compiler.instance_variable_get(:@user)
assert_equal 'foo', compiler.instance_variable_get(:@other)
end
test "compiler return a compiled template" do test "compiler return a compiled template" do
assert_instance_of RablFastJson::CompiledTemplate, @compiler.compile_source("") assert_instance_of RablFastJson::CompiledTemplate, @compiler.compile_source("")
end end
@ -78,38 +44,56 @@ class CompilerTest < ActiveSupport::TestCase
end end
test "child with arbitrary source store the data with the template" do test "child with arbitrary source store the data with the template" do
t = @compiler.compile_source(%{ child @user => :author do attribute :name end }) t = @compiler.compile_source(%{ child :@user => :author do attribute :name end })
assert_equal({ :author => { :_data => @user, :name => :name } }, t.source) assert_equal({ :author => { :_data => :@user, :name => :name } }, t.source)
end end
test "glue is compiled as a child but with anonymous name" do test "glue is compiled as a child but with anonymous name" do
t = @compiler.compile_source(%{ glue @user do attribute :name end }) t = @compiler.compile_source(%{ glue :@user do attribute :name end })
assert_equal({ :_glue0 => { :_data => @user, :name => :name } }, t.source) assert_equal({ :_glue0 => { :_data => :@user, :name => :name } }, t.source)
end end
test "multiple glue don't come with name collisions" do test "multiple glue don't come with name collisions" do
t = @compiler.compile_source(%{ t = @compiler.compile_source(%{
glue @user do attribute :name end glue :@user do attribute :name end
glue @user do attribute :foo end glue :@user do attribute :foo end
}) })
assert_equal({ assert_equal({
:_glue0 => { :_data => @user, :name => :name}, :_glue0 => { :_data => :@user, :name => :name},
:_glue1 => { :_data => @user, :foo => :foo} :_glue1 => { :_data => :@user, :foo => :foo}
}, t.source) }, t.source)
end end
test "object set data for the template" do test "object set data for the template" do
t = @compiler.compile_source(%{ object @user }) t = @compiler.compile_source(%{ object :@user })
assert_equal @user, t.data assert_equal :@user, t.data
end end
test "object property can define root name" do test "object property can define root name" do
t = @compiler.compile_source(%{ object @user => :author }) t = @compiler.compile_source(%{ object :@user => :author })
assert_equal @user, t.data assert_equal :@user, t.data
assert_equal :author, t.root_name assert_equal :author, t.root_name
end end
test "collection set the data for the template" do
t = @compiler.compile_source(%{ collection :@user })
assert_equal :@user, t.data
end
test "collection property can define root name" do
t = @compiler.compile_source(%{ collection :@user => :users })
assert_equal :@user, t.data
assert_equal :users, t.root_name
end
test "extends use other template one's active" do
template = mock('template', :source => { :id => :id })
RablFastJson::Library.stub_chain(:instance, :get).with('users/base').and_return(template)
t = @compiler.compile_source(%{ extends 'users/base' })
assert_equal({ :id => :id }, t.source)
end
test "node are compiled without evaluating the block" do test "node are compiled without evaluating the block" do
t = @compiler.compile_source(%{ node(:foo) { bar } }) t = @compiler.compile_source(%{ node(:foo) { bar } })
assert_not_nil t.source[:foo] assert_not_nil t.source[:foo]

View File

@ -2,6 +2,35 @@
ENV["RAILS_ENV"] = "test" ENV["RAILS_ENV"] = "test"
$:.unshift File.expand_path('../../lib', __FILE__) $:.unshift File.expand_path('../../lib', __FILE__)
require 'rspec/mocks'
require 'minitest/autorun' require 'minitest/autorun'
require 'active_support/test_case' require 'active_support/test_case'
require 'rabl-fast-json' require 'rabl-fast-json'
module ActiveSupport
class TestCase
RSpec::Mocks::setup(self)
include RSpec::Mocks::ExampleMethods
end
end
class Context
attr_accessor :virtual_path
def initialize
@_assigns = {}
@virtual_path = '/users'
end
def set_assign(key, value)
@_assigns[key] = value
end
def get_assign(key)
@_assigns[key]
end
end
class User
attr_accessor :name
end