Merge branch 'master' into fragment-cache
This commit is contained in:
commit
4cf10fb8f6
2
Gemfile
2
Gemfile
@ -1,6 +1,6 @@
|
||||
source "http://rubygems.org"
|
||||
|
||||
# Declare your gem's dependencies in rabl-fast-json.gemspec.
|
||||
# Declare your gem's dependencies in rabl-rails.gemspec.
|
||||
# Bundler will treat runtime dependencies like base dependencies, and
|
||||
# development dependencies will be added by default to the :development group.
|
||||
gemspec
|
||||
|
@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
rabl-fast-json (0.1.0)
|
||||
rabl-rails (0.1.0)
|
||||
activesupport (~> 3.2.1)
|
||||
|
||||
GEM
|
||||
@ -61,7 +61,7 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
actionpack (~> 3.2.1)
|
||||
rabl-fast-json!
|
||||
rabl-rails!
|
||||
railties (~> 3.2.1)
|
||||
rspec-mocks
|
||||
sqlite3
|
||||
|
85
README.md
85
README.md
@ -1,3 +1,84 @@
|
||||
= RablFastJson
|
||||
# RABL for Rails #
|
||||
|
||||
This project rocks and uses MIT-LICENSE.
|
||||
RABL (Ruby API Builder Language) is a ruby templating system for rendering resources in different format (JSON, XML, BSON, ...). You can find documentation [here](http://github.com/nesquena/rabl).
|
||||
|
||||
RABL-rails only target Rails 3+ application because Rails 2 applications are becoming less and less present and will be obsolete with Rails 4. So let's look to the future !
|
||||
|
||||
So now you ask why used `rabl-rails` if `rabl` already exists and supports Rails. Rabl-rails is *faster* and uses * less memory* than standard rabl gem while letting you access same features. Of course, there are some slight changes to do on your templates to get this gem to work but it should't take you more than 5 minutes.
|
||||
|
||||
## Installation
|
||||
|
||||
Install as a gem :
|
||||
|
||||
```
|
||||
gem install rabl-rails
|
||||
```
|
||||
|
||||
or add directly to your `Gemfile`
|
||||
|
||||
```
|
||||
gem 'rabl'
|
||||
```
|
||||
|
||||
And that's it !
|
||||
|
||||
## Overview
|
||||
|
||||
Once you have installed RABL, you can directly used RABL templates to render your resources without changing anything to you controller. As example,
|
||||
assuming you have a `Post` model filled with blog posts, and a `PostController` that look like this :
|
||||
|
||||
```ruby
|
||||
class PostController < ApplicationController
|
||||
respond_to :html, :json, :xml
|
||||
|
||||
def index
|
||||
@posts = Post.order('created_at DESC')
|
||||
respond_with(@posts)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
You can create the following RABL-rails template to express the API output of `@posts`
|
||||
|
||||
```ruby
|
||||
# app/views/post/index.rabl
|
||||
collection :@posts
|
||||
attributes :id, :title, :subject
|
||||
child(:user) { attributes :full_name }
|
||||
node(:read) { |post| post.read_by?(@user) }
|
||||
```
|
||||
|
||||
This would output the following JSON when visiting `http://localhost:3000/posts.json`
|
||||
|
||||
```js
|
||||
[{
|
||||
"id" : 5, title: "...", subject: "...",
|
||||
"user" : { full_name : "..." },
|
||||
"read" : true
|
||||
}]
|
||||
```
|
||||
|
||||
That's a basic overview but there is a lot more to see such as partials, inheritance or fragment caching.
|
||||
|
||||
## How it works
|
||||
|
||||
As opposed to standard RABL gem, this gem separate compiling (a.k.a transforming a RABL-rails template into a Ruby hash) and the actual rendering of the object or collection. This allow to only compile the template once and only Ruby hashes.
|
||||
|
||||
The fact of compiling the template outside of any rendering context prevent us to use any instances variables (with the exception of node) in the template because they are rendering objects. So instead, you'll have to use symbols of these variables. For example, to render the collection `@posts` inside your `PostController`, you need to use `:@posts` inside of the template.
|
||||
|
||||
The only places where you can actually used instance variables are into Proc (or lambda) or into custom node (because they are treated as Proc).
|
||||
|
||||
```ruby
|
||||
# We reference the @posts varibles that will be used at rendering time
|
||||
collection :@posts
|
||||
|
||||
# Here you can use directly the instance variable because it
|
||||
# will be evaluated when rendering the object
|
||||
node(:read) { |post| post.read_by?(@user) }
|
||||
```
|
||||
|
||||
The same rule applies for view helpers such as `current_user`
|
||||
|
||||
After the template is compiled into a hash, Rabl-rails will use a renderer to do the actual output. Actually, only JSON and XML formats are supported.
|
||||
|
||||
## Usage
|
2
Rakefile
Normal file → Executable file
2
Rakefile
Normal file → Executable file
@ -14,7 +14,7 @@
|
||||
#
|
||||
# RDoc::Task.new(:rdoc) do |rdoc|
|
||||
# rdoc.rdoc_dir = 'rdoc'
|
||||
# rdoc.title = 'RablFastJson'
|
||||
# rdoc.title = 'RablRails'
|
||||
# rdoc.options << '--line-numbers'
|
||||
# rdoc.rdoc_files.include('README.rdoc')
|
||||
# rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
|
@ -1,11 +0,0 @@
|
||||
module RablFastJson
|
||||
module Helpers
|
||||
def root_given?(options) #:nodoc:
|
||||
options[:root].present?
|
||||
end
|
||||
|
||||
def partial_given?(options) #:nodoc:
|
||||
options[:partial].present?
|
||||
end
|
||||
end
|
||||
end
|
@ -1,86 +0,0 @@
|
||||
module RablFastJson
|
||||
class CompiledTemplate
|
||||
attr_accessor :source, :data, :root_name, :context
|
||||
|
||||
delegate :[], :[]=, :merge!, :to => :source
|
||||
|
||||
def initialize
|
||||
@source = {}
|
||||
end
|
||||
|
||||
def has_root_name?
|
||||
!@root_name.nil?
|
||||
end
|
||||
|
||||
def render
|
||||
get_object_from_context
|
||||
get_assigns_from_context
|
||||
@object.respond_to?(:each) ? render_collection : render_resource
|
||||
end
|
||||
|
||||
def render_resource(data = nil, source = nil)
|
||||
data ||= @object
|
||||
source ||= @source
|
||||
|
||||
source.inject({}) { |output, current|
|
||||
key, value = current
|
||||
|
||||
out = case value
|
||||
when Symbol
|
||||
data.send(value) # attributes
|
||||
when Proc
|
||||
instance_exec data, &value # node
|
||||
when Array # node with condition
|
||||
next output if !instance_exec data, &(value.first) #value.first.call(data)
|
||||
instance_exec data, &(value.last)
|
||||
when Hash
|
||||
current_value = value.dup
|
||||
data_symbol = current_value.delete(:_data)
|
||||
object = data_symbol.nil? ? data : data_symbol.to_s.start_with?('@') ? @context.instance_variable_get(data_symbol) : data.send(data_symbol)
|
||||
|
||||
if key.to_s.start_with?('_') # glue
|
||||
current_value.each_pair { |k, v|
|
||||
output[k] = object.send(v)
|
||||
}
|
||||
next output
|
||||
else # child
|
||||
object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value)
|
||||
end
|
||||
end
|
||||
output[key] = out
|
||||
output
|
||||
}
|
||||
end
|
||||
|
||||
def render_collection(collection = nil, source = nil)
|
||||
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].nil?
|
||||
object = options[:object]
|
||||
|
||||
return [] if object.respond_to?(:empty?) && object.empty?
|
||||
|
||||
template = Library.instance.get(template_path)
|
||||
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
|
@ -4,24 +4,28 @@ require 'active_support'
|
||||
require 'active_support/json'
|
||||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
|
||||
require 'rabl-fast-json/version'
|
||||
require 'rabl-fast-json/helpers'
|
||||
require 'rabl-fast-json/template'
|
||||
require 'rabl-fast-json/compiler'
|
||||
require 'rabl-fast-json/library'
|
||||
require 'rabl-fast-json/handler'
|
||||
require 'rabl-fast-json/railtie'
|
||||
require 'rabl-rails/version'
|
||||
require 'rabl-rails/template'
|
||||
require 'rabl-rails/compiler'
|
||||
|
||||
module RablFastJson
|
||||
require 'rabl-rails/renderer'
|
||||
|
||||
require 'rabl-rails/library'
|
||||
require 'rabl-rails/handler'
|
||||
require 'rabl-rails/railtie'
|
||||
|
||||
|
||||
|
||||
module RablRails
|
||||
extend self
|
||||
|
||||
|
||||
mattr_accessor :cache_templates
|
||||
@@cache_templates = true
|
||||
|
||||
def configure
|
||||
yield self
|
||||
end
|
||||
|
||||
|
||||
def cache_templates?
|
||||
ActionController::Base.perform_caching && @@cache_templates
|
||||
end
|
@ -1,11 +1,9 @@
|
||||
module RablFastJson
|
||||
module RablRails
|
||||
#
|
||||
# Class that will compile RABL source code into a hash
|
||||
# representing data structure
|
||||
#
|
||||
class Compiler
|
||||
include Helpers
|
||||
|
||||
def initialize
|
||||
@glue_count = 0
|
||||
end
|
||||
@ -20,15 +18,6 @@ module RablFastJson
|
||||
@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:
|
||||
@ -49,7 +38,7 @@ module RablFastJson
|
||||
#
|
||||
def collection(data, options = {})
|
||||
object(data)
|
||||
@template.root_name = options[:root] if root_given?(options)
|
||||
@template.root_name = options[:root] if options[:root]
|
||||
end
|
||||
|
||||
#
|
||||
@ -80,14 +69,14 @@ module RablFastJson
|
||||
# child(:@posts, :root => :posts) { attribute :id }
|
||||
# child(:posts, :partial => 'posts/base')
|
||||
#
|
||||
def child(name_or_data, options = {}, &block)
|
||||
def child(name_or_data, options = {})
|
||||
data, name = extract_data_and_name(name_or_data)
|
||||
name = options[:root] if root_given?(options)
|
||||
if partial_given?(options)
|
||||
name = options[:root] if options[:root]
|
||||
if options[:partial]
|
||||
template = Library.instance.get(options[:partial])
|
||||
@template[name] = template.merge!(:_data => data)
|
||||
else
|
||||
_compile_sub_template(name, data, &block)
|
||||
elsif block_given?
|
||||
@template[name] = sub_compile(data) { yield }
|
||||
end
|
||||
end
|
||||
|
||||
@ -96,11 +85,11 @@ module RablFastJson
|
||||
# Example:
|
||||
# glue(:@user) { attribute :name }
|
||||
#
|
||||
def glue(data, &block)
|
||||
def glue(data)
|
||||
return unless block_given?
|
||||
name = :"_glue#{@glue_count}"
|
||||
@glue_count += 1
|
||||
_compile_sub_template(name, data, &block)
|
||||
@template[name] = sub_compile(data) { yield }
|
||||
end
|
||||
|
||||
#
|
||||
@ -158,11 +147,14 @@ module RablFastJson
|
||||
name_or_data
|
||||
end
|
||||
end
|
||||
|
||||
def _compile_sub_template(name, data, &block) #:nodoc:
|
||||
compiler = Compiler.new
|
||||
template = compiler.compile_block(&block)
|
||||
@template[name] = template.merge!(:_data => data)
|
||||
|
||||
def sub_compile(data)
|
||||
return {} unless block_given?
|
||||
old_template, @template = @template, {}
|
||||
yield
|
||||
@template.merge!(:_data => data)
|
||||
ensure
|
||||
@template = old_template
|
||||
end
|
||||
end
|
||||
end
|
@ -1,12 +1,12 @@
|
||||
module RablFastJson
|
||||
module RablRails
|
||||
module Handlers
|
||||
class Rabl
|
||||
cattr_accessor :default_format
|
||||
self.default_format = 'application/json'
|
||||
|
||||
def self.call(template)
|
||||
def self.call(template)
|
||||
%{
|
||||
RablFastJson::Library.instance.
|
||||
RablRails::Library.instance.
|
||||
get_rendered_template(#{template.source.inspect}, self)
|
||||
}
|
||||
end
|
@ -1,27 +1,25 @@
|
||||
require 'singleton'
|
||||
|
||||
module RablFastJson
|
||||
module RablRails
|
||||
class Library
|
||||
include Singleton
|
||||
|
||||
attr_accessor :view_renderer
|
||||
|
||||
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)
|
||||
@lookup_context = context.lookup_context
|
||||
|
||||
compiled_template = get_compiled_template(path, source)
|
||||
compiled_template.context = context
|
||||
body = compiled_template.render
|
||||
ActiveSupport::JSON.encode(compiled_template.root_name ? { compiled_template.root_name => body } : body)
|
||||
|
||||
format = context.params[:format] || 'json'
|
||||
Renderers.const_get(format.upcase!).new(context).render(compiled_template)
|
||||
end
|
||||
|
||||
def get_compiled_template(path, source)
|
||||
if path && RablFastJson.cache_templates?
|
||||
if path && RablRails.cache_templates?
|
||||
@cached_templates[path] ||= Compiler.new.compile_source(source)
|
||||
@cached_templates[path].dup
|
||||
else
|
||||
@ -31,8 +29,8 @@ module RablFastJson
|
||||
|
||||
def get(path)
|
||||
template = @cached_templates[path]
|
||||
return template unless template.nil?
|
||||
t = @view_renderer.lookup_context.find_template(path, [], false)
|
||||
return template if template
|
||||
t = @lookup_context.find_template(path, [], false)
|
||||
get_compiled_template(path, t.source)
|
||||
end
|
||||
end
|
@ -1,8 +1,8 @@
|
||||
module RablFastJson
|
||||
module RablRails
|
||||
class Railtie < Rails::Railtie
|
||||
initializer "rabl.initialize" do |app|
|
||||
ActiveSupport.on_load(:action_view) do
|
||||
ActionView::Template.register_template_handler :rabl, RablFastJson::Handlers::Rabl
|
||||
ActionView::Template.register_template_handler :rabl, RablRails::Handlers::Rabl
|
||||
end
|
||||
end
|
||||
end
|
2
lib/rabl-rails/renderer.rb
Normal file
2
lib/rabl-rails/renderer.rb
Normal file
@ -0,0 +1,2 @@
|
||||
require 'rabl-rails/renderers/base'
|
||||
require 'rabl-rails/renderers/json'
|
114
lib/rabl-rails/renderers/base.rb
Normal file
114
lib/rabl-rails/renderers/base.rb
Normal file
@ -0,0 +1,114 @@
|
||||
module RablRails
|
||||
module Renderers
|
||||
class PartialError < StandardError; end
|
||||
|
||||
class Base
|
||||
|
||||
def initialize(context) # :nodoc:
|
||||
@_context = context
|
||||
setup_render_context
|
||||
end
|
||||
|
||||
#
|
||||
# Render a template.
|
||||
# Uses the compiled template source to get a hash with the actual
|
||||
# data and then format the result according to the `format_result`
|
||||
# method defined by the renderer.
|
||||
#
|
||||
def render(template)
|
||||
collection_or_resource = @_context.instance_variable_get(template.data) if template.data
|
||||
output_hash = collection_or_resource.respond_to?(:each) ? render_collection(collection_or_resource, template.source) :
|
||||
render_resource(collection_or_resource, template.source)
|
||||
output_hash = { template.root_name => output_hash } if template.root_name
|
||||
format_output(output_hash)
|
||||
end
|
||||
|
||||
#
|
||||
# Format a hash into the desired output.
|
||||
# Renderer subclasses must implement this method
|
||||
#
|
||||
def format_output(hash)
|
||||
raise "Muse be implemented by renderer"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Render a single resource as a hash, according to the compiled
|
||||
# template source passed.
|
||||
#
|
||||
def render_resource(data, source)
|
||||
source.inject({}) { |output, current|
|
||||
key, value = current
|
||||
|
||||
out = case value
|
||||
when Symbol
|
||||
data.send(value) # attributes
|
||||
when Proc
|
||||
instance_exec data, &value # node
|
||||
when Array # node with condition
|
||||
next output if !instance_exec data, &(value.first)
|
||||
instance_exec data, &(value.last)
|
||||
when Hash
|
||||
current_value = value.dup
|
||||
data_symbol = current_value.delete(:_data)
|
||||
object = data_symbol.nil? ? data : data_symbol.to_s.start_with?('@') ? @_context.instance_variable_get(data_symbol) : data.send(data_symbol)
|
||||
|
||||
if key.to_s.start_with?('_') # glue
|
||||
current_value.each_pair { |k, v|
|
||||
output[k] = object.send(v)
|
||||
}
|
||||
next output
|
||||
else # child
|
||||
object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value)
|
||||
end
|
||||
end
|
||||
output[key] = out
|
||||
output
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Call the render_resource mtehod on each object of the collection
|
||||
# and return an array of the returned values.
|
||||
#
|
||||
def render_collection(collection, source)
|
||||
collection.map { |o| render_resource(o, source) }
|
||||
end
|
||||
|
||||
#
|
||||
# Allow to use partial inside of node blocks (they are evaluated at)
|
||||
# rendering time.
|
||||
#
|
||||
def partial(template_path, options = {})
|
||||
raise PartialError.new("No object was given to partial #{template_path}") unless options[:object]
|
||||
object = options[:object]
|
||||
|
||||
return [] if object.respond_to?(:empty?) && object.empty?
|
||||
|
||||
template = Library.instance.get(template_path)
|
||||
object.respond_to?(:each) ? render_collection(object, template.source) : render_resource(object, template.source)
|
||||
end
|
||||
|
||||
#
|
||||
# If a method is called inside a 'node' property or a 'if' lambda
|
||||
# it will be passed to context if it exists or treated as a standard
|
||||
# missing method.
|
||||
#
|
||||
def method_missing(name, *args, &block)
|
||||
@_context.respond_to?(name) ? @_context.send(name, *args, &block) : super
|
||||
end
|
||||
|
||||
#
|
||||
# Copy assigns from controller's context into this
|
||||
# renderer context to include instances variables when
|
||||
# evaluating 'node' properties.
|
||||
#
|
||||
def setup_render_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
|
9
lib/rabl-rails/renderers/json.rb
Normal file
9
lib/rabl-rails/renderers/json.rb
Normal file
@ -0,0 +1,9 @@
|
||||
module RablRails
|
||||
module Renderers
|
||||
class JSON < Base
|
||||
def format_output(hash)
|
||||
ActiveSupport::JSON.encode(hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
11
lib/rabl-rails/template.rb
Normal file
11
lib/rabl-rails/template.rb
Normal file
@ -0,0 +1,11 @@
|
||||
module RablRails
|
||||
class CompiledTemplate
|
||||
attr_accessor :source, :data, :root_name
|
||||
|
||||
delegate :[], :[]=, :merge!, :to => :source
|
||||
|
||||
def initialize
|
||||
@source = {}
|
||||
end
|
||||
end
|
||||
end
|
@ -1,3 +1,3 @@
|
||||
module RablFastJson
|
||||
module RablRails
|
||||
VERSION = '0.1.0'
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
# desc "Explaining what the task does"
|
||||
# task :rabl-fast-json do
|
||||
# task :rabl-rails do
|
||||
# # Task goes here
|
||||
# end
|
@ -1,17 +1,17 @@
|
||||
$:.push File.expand_path("../lib", __FILE__)
|
||||
|
||||
# Maintain your gem's version:
|
||||
require "rabl-fast-json/version"
|
||||
require "rabl-rails/version"
|
||||
|
||||
# Describe your gem and declare its dependencies:
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "rabl-fast-json"
|
||||
s.version = RablFastJson::VERSION
|
||||
s.name = "rabl-rails"
|
||||
s.version = RablRails::VERSION
|
||||
s.authors = ["TODO: Your name"]
|
||||
s.email = ["TODO: Your email"]
|
||||
s.homepage = "TODO"
|
||||
s.summary = "TODO: Summary of RablFastJson."
|
||||
s.description = "TODO: Description of RablFastJson."
|
||||
s.summary = "TODO: Summary of RablRails."
|
||||
s.description = "TODO: Description of RablRails."
|
||||
|
||||
s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.md"]
|
||||
s.test_files = Dir["test/**/*"]
|
@ -3,32 +3,32 @@ require 'test_helper'
|
||||
class CacheTemplatesTest < ActiveSupport::TestCase
|
||||
|
||||
setup do
|
||||
RablFastJson::Library.reset_instance
|
||||
@library = RablFastJson::Library.instance
|
||||
RablRails::Library.reset_instance
|
||||
@library = RablRails::Library.instance
|
||||
RablRails.cache_templates = true
|
||||
end
|
||||
|
||||
|
||||
test "cache templates if perform_caching is active and cache_templates is enabled" do
|
||||
RablFastJson.cache_templates = true
|
||||
ActionController::Base.stub(:perform_caching).and_return(true)
|
||||
ActionController::Base.stub(:perform_caching).and_return(true)
|
||||
@library.get_compiled_template('some/path', "")
|
||||
t = @library.get_compiled_template('some/path', "attribute :id")
|
||||
|
||||
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
|
||||
test "cached templates should not be modifiable in place" do
|
||||
RablFastJson.cache_templates = true
|
||||
ActionController::Base.stub(:perform_caching).and_return(true)
|
||||
t = @library.get_compiled_template('some/path', "")
|
||||
assert_nil t.context
|
||||
t.context = "foobar"
|
||||
assert_nil @library.get_compiled_template('some/path', "").context
|
||||
@library.get_compiled_template('some/path', "")
|
||||
t = @library.get_compiled_template('some/path', "attribute :id")
|
||||
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
|
||||
test "don't cache templates cache_templates is enabled but perform_caching is not active" do
|
||||
RablFastJson.cache_templates = true
|
||||
ActionController::Base.stub(:perform_caching).and_return(false)
|
||||
|
||||
refute_equal @library.get_compiled_template('some/path', ""), @library.get_compiled_template('some/path', "")
|
||||
ActionController::Base.stub(:perform_caching).and_return(false)
|
||||
@library.get_compiled_template('some/path', "")
|
||||
t = @library.get_compiled_template('some/path', "attribute :id")
|
||||
|
||||
assert_equal({ :id => :id }, t.source)
|
||||
end
|
||||
end
|
@ -4,13 +4,47 @@ class CompilerTest < ActiveSupport::TestCase
|
||||
|
||||
setup do
|
||||
@user = User.new
|
||||
@compiler = RablFastJson::Compiler.new
|
||||
@compiler = RablRails::Compiler.new
|
||||
end
|
||||
|
||||
test "compiler return a compiled template" do
|
||||
assert_instance_of RablFastJson::CompiledTemplate, @compiler.compile_source("")
|
||||
assert_instance_of RablRails::CompiledTemplate, @compiler.compile_source("")
|
||||
end
|
||||
|
||||
test "object set data for the template" do
|
||||
t = @compiler.compile_source(%{ object :@user })
|
||||
assert_equal :@user, t.data
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
test "object property can define root name" do
|
||||
t = @compiler.compile_source(%{ object :@user => :author })
|
||||
assert_equal :@user, t.data
|
||||
assert_equal :author, t.root_name
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
test "collection set the data for the template" do
|
||||
t = @compiler.compile_source(%{ collection :@user })
|
||||
assert_equal :@user, t.data
|
||||
assert_equal({}, t.source)
|
||||
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
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
test "collection property can define root name via options" do
|
||||
t = @compiler.compile_source(%{ collection :@user, :root => :users })
|
||||
assert_equal :@user, t.data
|
||||
assert_equal :users, t.root_name
|
||||
end
|
||||
|
||||
# Compilation
|
||||
|
||||
test "simple attributes are compiled to hash" do
|
||||
t = @compiler.compile_source(%{ attributes :id, :name })
|
||||
assert_equal({ :id => :id, :name => :name}, t.source)
|
||||
@ -57,11 +91,10 @@ class CompilerTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test "child with succint partial notation" do
|
||||
@view_renderer = mock()
|
||||
@view_renderer.stub_chain(:lookup_context, :find_template).with('users/base', [], false).and_return(
|
||||
mock(:source => %{ attribute :id }))
|
||||
RablFastJson::Library.reset_instance
|
||||
RablFastJson::Library.instance.view_renderer = @view_renderer
|
||||
mock_template = RablRails::CompiledTemplate.new
|
||||
mock_template.source = { :id => :id }
|
||||
RablRails::Library.reset_instance
|
||||
RablRails::Library.instance.stub(:get).with('users/base').and_return(mock_template)
|
||||
|
||||
t = @compiler.compile_source(%{child(:user, :partial => 'users/base') })
|
||||
assert_equal( {:user => { :_data => :user, :id => :id } }, t.source)
|
||||
@ -84,42 +117,10 @@ class CompilerTest < ActiveSupport::TestCase
|
||||
}, t.source)
|
||||
end
|
||||
|
||||
test "object set data for the template" do
|
||||
t = @compiler.compile_source(%{ object :@user })
|
||||
assert_equal :@user, t.data
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
test "object property can define root name" do
|
||||
t = @compiler.compile_source(%{ object :@user => :author })
|
||||
assert_equal :@user, t.data
|
||||
assert_equal :author, t.root_name
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
test "collection set the data for the template" do
|
||||
t = @compiler.compile_source(%{ collection :@user })
|
||||
assert_equal :@user, t.data
|
||||
assert_equal({}, t.source)
|
||||
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
|
||||
assert_equal({}, t.source)
|
||||
end
|
||||
|
||||
test "collection property can define root name via options" do
|
||||
t = @compiler.compile_source(%{ collection :@user, :root => :users })
|
||||
assert_equal :@user, t.data
|
||||
assert_equal :users, t.root_name
|
||||
end
|
||||
|
||||
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').and_return(template)
|
||||
RablRails::Library.reset_instance
|
||||
RablRails::Library.instance.stub(:get).with('users/base').and_return(template)
|
||||
t = @compiler.compile_source(%{ extends 'users/base' })
|
||||
assert_equal({ :id => :id }, t.source)
|
||||
end
|
||||
|
@ -15,21 +15,17 @@ class DeepNestingTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
setup do
|
||||
RablFastJson::Library.reset_instance
|
||||
RablRails::Library.reset_instance
|
||||
@post = Post.new(42, 'I rock !')
|
||||
@user = User.new(1, 'foobar', 'male')
|
||||
@user.stub(:posts).and_return([@post])
|
||||
@user.stub(:respond_to?).with(:each).and_return(false)
|
||||
|
||||
@view_renderer = mock()
|
||||
@view_renderer.stub_chain(:lookup_context, :find_template).with('comments/show', [], false).and_return(
|
||||
mock(:source => %{ object :@comment\n attribute :content }))
|
||||
|
||||
@context = Context.new
|
||||
@context.stub(:instance_variable_get).with(:@user).and_return(@user)
|
||||
@context.stub(:instance_variable_get).with(:@view_renderer).and_return(@view_renderer)
|
||||
@context.stub(:instance_variable_get).with(:@virtual_path).and_return('users/show')
|
||||
@context.stub(:instance_variable_get).with(:@_assigns).and_return({})
|
||||
@context.stub(:lookup_context).and_return(mock(:find_template => mock(:source => %{ object :@comment\n attribute :content })))
|
||||
end
|
||||
|
||||
test "compile and render deep nesting template" do
|
||||
@ -54,7 +50,7 @@ class DeepNestingTest < ActiveSupport::TestCase
|
||||
{ :content => 'second' }
|
||||
]
|
||||
}]
|
||||
}), RablFastJson::Library.instance.get_rendered_template(source, @context))
|
||||
}), RablRails::Library.instance.get_rendered_template(source, @context))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -2,19 +2,19 @@ require 'test_helper'
|
||||
|
||||
class NonRestfulResponseTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
RablFastJson::Library.reset_instance
|
||||
|
||||
RablRails::Library.reset_instance
|
||||
|
||||
@user = User.new(1, 'foo', 'male')
|
||||
@user.stub_chain(:posts, :count).and_return(10)
|
||||
@user.stub(:respond_to?).with(:each).and_return(false)
|
||||
|
||||
|
||||
@context = Context.new
|
||||
@context.stub(:instance_variable_get).with(:@user).and_return(@user)
|
||||
@context.stub(:instance_variable_get).with(:@view_renderer).and_return(mock())
|
||||
@context.stub(:instance_variable_get).with(:@virtual_path).and_return('user/show')
|
||||
@context.stub(:instance_variable_get).with(:@_assigns).and_return({'user' => @user})
|
||||
@context.stub(:lookup_context)
|
||||
end
|
||||
|
||||
|
||||
test "compile and render non restful resource" do
|
||||
source = %{
|
||||
object false
|
||||
@ -23,13 +23,13 @@ class NonRestfulResponseTest < ActiveSupport::TestCase
|
||||
attributes :id, :name
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
assert_equal(ActiveSupport::JSON.encode({
|
||||
:post_count => 10,
|
||||
:user => {
|
||||
:id => 1,
|
||||
:name => 'foo'
|
||||
}
|
||||
}), RablFastJson::Library.instance.get_rendered_template(source, @context))
|
||||
}), RablRails::Library.instance.get_rendered_template(source, @context))
|
||||
end
|
||||
end
|
@ -1,72 +1,88 @@
|
||||
require 'test_helper'
|
||||
|
||||
class TestCompiledTemplate < ActiveSupport::TestCase
|
||||
class TestJsonRenderer < ActiveSupport::TestCase
|
||||
|
||||
setup do
|
||||
@context = Context.new
|
||||
@data = User.new(1, 'foobar', 'male')
|
||||
@data.stub(:respond_to?).with(:each).and_return(false)
|
||||
|
||||
@context = Context.new
|
||||
@context.stub(:instance_variable_get).with(:@data).and_return(@data)
|
||||
@context.stub(:instance_variable_get).with(:@_assigns).and_return({})
|
||||
@template = RablFastJson::CompiledTemplate.new
|
||||
@template.context = @context
|
||||
|
||||
@template = RablRails::CompiledTemplate.new
|
||||
@template.data = :@data
|
||||
end
|
||||
|
||||
def render_json_output
|
||||
RablRails::Renderers::JSON.new(@context).render(@template).to_s
|
||||
end
|
||||
|
||||
test "render object wth empty template" do
|
||||
@template.source = {}
|
||||
assert_equal({}, @template.render)
|
||||
assert_equal %q({}), render_json_output
|
||||
end
|
||||
|
||||
test "render collection with empty template" do
|
||||
@context.stub(:instance_variable_get).with(:@data).and_return([@data])
|
||||
@template.source = {}
|
||||
assert_equal([{}], @template.render)
|
||||
assert_equal %q([{}]), render_json_output
|
||||
end
|
||||
|
||||
test "render single object attributes" do
|
||||
@template.source = { :id => :id, :name => :name }
|
||||
assert_equal({ :id => 1, :name => 'foobar'}, @template.render)
|
||||
assert_equal %q({"id":1,"name":"foobar"}), render_json_output
|
||||
end
|
||||
|
||||
test "render object as a child" do
|
||||
test "render child with object association" do
|
||||
@data.stub(:address).and_return(mock(:city => 'Paris'))
|
||||
@template.source = { :address => { :_data => :address, :city => :city } }
|
||||
assert_equal %q({"address":{"city":"Paris"}}), render_json_output
|
||||
end
|
||||
|
||||
test "render child with arbitrary data source" do
|
||||
@template.source = { :author => { :_data => :@data, :name => :name } }
|
||||
assert_equal({ :author => { :name => 'foobar' } }, @template.render)
|
||||
assert_equal %q({"author":{"name":"foobar"}}), render_json_output
|
||||
end
|
||||
|
||||
test "render glued attributes from single object" do
|
||||
@template.source = { :_glue0 => { :_data => :@data, :name => :name } }
|
||||
assert_equal({ :name => 'foobar' }, @template.render)
|
||||
assert_equal %q({"name":"foobar"}), render_json_output
|
||||
end
|
||||
|
||||
test "render collection with attributes" do
|
||||
@data = [User.new(1, 'foo', 'male'), User.new(2, 'bar', 'female')]
|
||||
@context.stub(:instance_variable_get).with(:@data).and_return(@data)
|
||||
@template.source = { :uid => :id, :name => :name, :gender => :sex }
|
||||
assert_equal([
|
||||
{ :uid => 1, :name => 'foo', :gender => 'male'},
|
||||
{ :uid => 2, :name => 'bar', :gender => 'female'}
|
||||
], @template.render)
|
||||
assert_equal %q([{"uid":1,"name":"foo","gender":"male"},{"uid":2,"name":"bar","gender":"female"}]), render_json_output
|
||||
end
|
||||
|
||||
test "render node property" do
|
||||
proc = lambda { |object| object.sex }
|
||||
@template.source = { :sex => proc }
|
||||
assert_equal({ :sex => 'male' }, @template.render)
|
||||
proc = lambda { |object| object.name }
|
||||
@template.source = { :name => proc }
|
||||
assert_equal %q({"name":"foobar"}), render_json_output
|
||||
end
|
||||
|
||||
test "render node property with true condition" do
|
||||
condition = lambda { |u| true }
|
||||
proc = lambda { |object| object.name }
|
||||
@template.source = { :name => [condition, proc] }
|
||||
assert_equal({ :name => 'foobar' }, @template.render)
|
||||
assert_equal %q({"name":"foobar"}), render_json_output
|
||||
end
|
||||
|
||||
test "render node property with false condition" do
|
||||
condition = lambda { |u| false }
|
||||
proc = lambda { |object| object.name }
|
||||
@template.source = { :name => [condition, proc] }
|
||||
assert_equal({}, @template.render)
|
||||
assert_equal %q({}), render_json_output
|
||||
end
|
||||
|
||||
test "node with context method call" do
|
||||
@context.stub(:respond_to?).with(:context_method).and_return(true)
|
||||
@context.stub(:context_method).and_return('marty')
|
||||
proc = lambda { |object| context_method }
|
||||
@template.source = { :name => proc }
|
||||
assert_equal %q({"name":"marty"}), render_json_output
|
||||
end
|
||||
|
||||
test "partial should be evaluated at rendering time" do
|
||||
@ -75,28 +91,28 @@ class TestCompiledTemplate < ActiveSupport::TestCase
|
||||
@data.stub(:respond_to?).with(:empty?).and_return(false)
|
||||
|
||||
# Stub Library#get
|
||||
t = RablFastJson::CompiledTemplate.new
|
||||
t.source, t.context = { :name => :name }, @context
|
||||
RablFastJson::Library.reset_instance
|
||||
RablFastJson::Library.instance.should_receive(:get).with('users/base').and_return(t)
|
||||
t = RablRails::CompiledTemplate.new
|
||||
t.source = { :name => :name }
|
||||
RablRails::Library.reset_instance
|
||||
RablRails::Library.instance.should_receive(:get).with('users/base').and_return(t)
|
||||
|
||||
@template.data = false
|
||||
@template.source = { :user => ->(s) { partial('users/base', :object => @user) } }
|
||||
|
||||
assert_equal({ :user => { :name => 'foobar' } }, @template.render)
|
||||
assert_equal %q({"user":{"name":"foobar"}}), render_json_output
|
||||
end
|
||||
|
||||
test "partial with nil values should raise an error" do
|
||||
test "partial with no values should raise an error" do
|
||||
@template.data = false
|
||||
@template.source = { :user => ->(s) { partial('users/base') } }
|
||||
|
||||
assert_raises(RuntimeError) { @template.render }
|
||||
assert_raises(RablRails::Renderers::PartialError) { render_json_output }
|
||||
end
|
||||
|
||||
test "partial with empty values should not raise an error" do
|
||||
@template.data = false
|
||||
@template.source = { :users => ->(s) { partial('users/base', :object => []) } }
|
||||
|
||||
assert_equal({ :users => [] }, @template.render)
|
||||
assert_equal %q({"users":[]}), render_json_output
|
||||
end
|
||||
end
|
@ -21,7 +21,7 @@ class <<Singleton
|
||||
alias_method_chain :included, :reset
|
||||
end
|
||||
|
||||
require 'rabl-fast-json'
|
||||
require 'rabl-rails'
|
||||
|
||||
module ActiveSupport
|
||||
class TestCase
|
||||
@ -35,16 +35,11 @@ class Context
|
||||
|
||||
def initialize
|
||||
@_assigns = {}
|
||||
@virtual_path = '/users'
|
||||
end
|
||||
|
||||
def set_assign(key, value)
|
||||
@_assigns[key] = value
|
||||
end
|
||||
|
||||
def get_assign(key)
|
||||
@_assigns[key]
|
||||
def params
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
User = Struct.new(:id, :name, :sex)
|
Loading…
Reference in New Issue
Block a user