rabl-rails 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +2 -2
- data/CHANGELOG.md +10 -0
- data/Gemfile +5 -9
- data/README.md +5 -3
- data/Rakefile +2 -2
- data/lib/rabl-rails.rb +21 -74
- data/lib/rabl-rails/compiler.rb +28 -38
- data/lib/rabl-rails/configuration.rb +48 -0
- data/lib/rabl-rails/handler.rb +3 -1
- data/lib/rabl-rails/helpers.rb +7 -0
- data/lib/rabl-rails/library.rb +43 -16
- data/lib/rabl-rails/nodes.rb +6 -0
- data/lib/rabl-rails/nodes/attribute.rb +17 -0
- data/lib/rabl-rails/nodes/child.rb +12 -0
- data/lib/rabl-rails/nodes/code.rb +19 -0
- data/lib/rabl-rails/nodes/condition.rb +14 -0
- data/lib/rabl-rails/nodes/glue.rb +25 -0
- data/lib/rabl-rails/nodes/node.rb +9 -0
- data/lib/rabl-rails/railtie.rb +0 -2
- data/lib/rabl-rails/renderer.rb +15 -13
- data/lib/rabl-rails/renderers/hash.rb +85 -0
- data/lib/rabl-rails/renderers/json.rb +9 -5
- data/lib/rabl-rails/renderers/plist.rb +6 -4
- data/lib/rabl-rails/renderers/xml.rb +6 -3
- data/lib/rabl-rails/responder.rb +1 -1
- data/lib/rabl-rails/template.rb +11 -5
- data/lib/rabl-rails/version.rb +1 -1
- data/lib/rabl-rails/visitors.rb +2 -0
- data/lib/rabl-rails/visitors/to_hash.rb +131 -0
- data/lib/rabl-rails/visitors/visitor.rb +17 -0
- data/rabl-rails.gemspec +3 -5
- data/test/helper.rb +75 -0
- data/test/renderers/test_hash_renderer.rb +90 -0
- data/test/renderers/test_json_renderer.rb +46 -0
- data/test/renderers/test_plist_renderer.rb +42 -0
- data/test/renderers/test_xml_renderer.rb +37 -0
- data/test/test_compiler.rb +283 -0
- data/test/test_configuration.rb +31 -0
- data/test/test_hash_visitor.rb +224 -0
- data/test/test_library.rb +85 -0
- data/test/{render_test.rb → test_render.rb} +18 -24
- metadata +99 -108
- data/lib/rabl-rails/condition.rb +0 -10
- data/lib/rabl-rails/renderers/base.rb +0 -171
- data/test/base_renderer_test.rb +0 -67
- data/test/cache_templates_test.rb +0 -35
- data/test/compiler_test.rb +0 -233
- data/test/deep_nesting_test.rb +0 -56
- data/test/keyword_test.rb +0 -47
- data/test/non_restful_response_test.rb +0 -35
- data/test/renderers/json_renderer_test.rb +0 -189
- data/test/renderers/plist_renderer_test.rb +0 -135
- data/test/renderers/xml_renderer_test.rb +0 -137
- data/test/test_helper.rb +0 -68
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module RablRails
|
4
|
+
module Nodes
|
5
|
+
class Attribute
|
6
|
+
include Node
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@hash, :[]=, :each
|
10
|
+
attr_reader :hash
|
11
|
+
|
12
|
+
def initialize(hash = {})
|
13
|
+
@hash = hash
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RablRails
|
2
|
+
module Nodes
|
3
|
+
class Code
|
4
|
+
include Node
|
5
|
+
|
6
|
+
attr_reader :name, :block, :condition
|
7
|
+
|
8
|
+
def initialize(name, block, condition = nil)
|
9
|
+
@name = name
|
10
|
+
@block = block
|
11
|
+
@condition = condition
|
12
|
+
end
|
13
|
+
|
14
|
+
def merge?
|
15
|
+
!name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RablRails
|
2
|
+
module Nodes
|
3
|
+
class Glue
|
4
|
+
include Node
|
5
|
+
|
6
|
+
attr_reader :template
|
7
|
+
|
8
|
+
def initialize(template)
|
9
|
+
@template = template
|
10
|
+
end
|
11
|
+
|
12
|
+
def data
|
13
|
+
@template.data
|
14
|
+
end
|
15
|
+
|
16
|
+
def nodes
|
17
|
+
@template.nodes
|
18
|
+
end
|
19
|
+
|
20
|
+
def instance_variable_data?
|
21
|
+
@instance_variable_data ||= data.to_s.start_with?('@')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/rabl-rails/railtie.rb
CHANGED
data/lib/rabl-rails/renderer.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'rabl-rails/renderers/
|
1
|
+
require 'rabl-rails/renderers/hash'
|
2
2
|
require 'rabl-rails/renderers/json'
|
3
3
|
require 'rabl-rails/renderers/xml'
|
4
4
|
require 'rabl-rails/renderers/plist'
|
@@ -6,16 +6,14 @@ require 'rabl-rails/renderers/plist'
|
|
6
6
|
module RablRails
|
7
7
|
module Renderer
|
8
8
|
class TemplateNotFound < StandardError; end
|
9
|
-
|
10
|
-
mattr_reader :view_path
|
11
|
-
@@view_path = 'app/views'
|
9
|
+
class PartialError < StandardError; end
|
12
10
|
|
13
11
|
class LookupContext
|
14
12
|
T = Struct.new(:source)
|
15
13
|
|
16
14
|
def initialize(view_path, format)
|
17
|
-
@view_path = view_path ||
|
18
|
-
@
|
15
|
+
@view_path = view_path || 'app/views'
|
16
|
+
@format = format.downcase
|
19
17
|
end
|
20
18
|
|
21
19
|
#
|
@@ -24,8 +22,14 @@ module RablRails
|
|
24
22
|
# path is used
|
25
23
|
#
|
26
24
|
def find_template(name, opt, partial = false)
|
27
|
-
|
28
|
-
|
25
|
+
paths = Dir["#@view_path/#{name}{.#@format,}.rabl"]
|
26
|
+
file_path = paths.find { |path| File.exists?(path) }
|
27
|
+
|
28
|
+
if file_path
|
29
|
+
T.new(File.read(file_path))
|
30
|
+
else
|
31
|
+
raise TemplateNotFound
|
32
|
+
end
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
@@ -33,12 +37,12 @@ module RablRails
|
|
33
37
|
# Context class to emulate normal Rails view
|
34
38
|
# context
|
35
39
|
#
|
36
|
-
class
|
40
|
+
class ViewContext
|
37
41
|
attr_reader :format
|
38
42
|
|
39
43
|
def initialize(path, options)
|
40
44
|
@virtual_path = path
|
41
|
-
@format = options.delete(:format) ||
|
45
|
+
@format = options.delete(:format) || 'json'
|
42
46
|
@_assigns = {}
|
43
47
|
@options = options
|
44
48
|
|
@@ -80,11 +84,9 @@ module RablRails
|
|
80
84
|
def render(object, template, options = {})
|
81
85
|
object = options[:locals].delete(:object) if !object && options[:locals]
|
82
86
|
|
83
|
-
c =
|
87
|
+
c = ViewContext.new(template, options)
|
84
88
|
t = c.lookup_context.find_template(template, [], false)
|
85
89
|
|
86
|
-
raise TemplateNotFound unless t
|
87
|
-
|
88
90
|
Library.instance.get_rendered_template(t.source, c, resource: object)
|
89
91
|
end
|
90
92
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module RablRails
|
2
|
+
module Renderers
|
3
|
+
module Hash
|
4
|
+
include ::RablRails::Helpers
|
5
|
+
extend self
|
6
|
+
|
7
|
+
#
|
8
|
+
# Render a template.
|
9
|
+
# Uses the compiled template source to get a hash with the actual
|
10
|
+
# data and then format the result according to the `format_result`
|
11
|
+
# method defined by the renderer.
|
12
|
+
#
|
13
|
+
def render(template, context, locals = nil)
|
14
|
+
visitor = Visitors::ToHash.new(context)
|
15
|
+
|
16
|
+
collection_or_resource = if template.data
|
17
|
+
if context.respond_to?(template.data)
|
18
|
+
context.send(template.data)
|
19
|
+
else
|
20
|
+
visitor.instance_variable_get(template.data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
collection_or_resource ||= locals[:resource] if locals
|
24
|
+
|
25
|
+
render_with_cache(template.cache_key, collection_or_resource) do
|
26
|
+
output_hash = if collection?(collection_or_resource)
|
27
|
+
render_collection(collection_or_resource, template.nodes, visitor)
|
28
|
+
else
|
29
|
+
render_resource(collection_or_resource, template.nodes, visitor)
|
30
|
+
end
|
31
|
+
|
32
|
+
format_output(output_hash, root_name: template.root_name, params: context.params)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
#
|
39
|
+
# Format a hash into the desired output.
|
40
|
+
# Renderer subclasses must implement this method
|
41
|
+
#
|
42
|
+
def format_output(hash, options = {})
|
43
|
+
hash = { options[:root_name] => hash } if options[:root_name]
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
#
|
50
|
+
# Render a single resource as a hash, according to the compiled
|
51
|
+
# template source passed.
|
52
|
+
#
|
53
|
+
def render_resource(resource, nodes, visitor)
|
54
|
+
visitor.reset_for resource
|
55
|
+
visitor.visit nodes
|
56
|
+
visitor.result
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Call the render_resource mtehod on each object of the collection
|
61
|
+
# and return an array of the returned values.
|
62
|
+
#
|
63
|
+
def render_collection(collection, nodes, visitor)
|
64
|
+
collection.map { |o| render_resource(o, nodes, visitor) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def resolve_cache_key(key, data)
|
68
|
+
return data.cache_key unless key
|
69
|
+
key.is_a?(Proc) ? instance_exec(data, &key) : key
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def render_with_cache(key, collection_or_resource)
|
75
|
+
if !key.is_a?(FalseClass) && ActionController::Base.perform_caching
|
76
|
+
Rails.cache.fetch(resolve_cache_key(key, collection_or_resource)) do
|
77
|
+
yield
|
78
|
+
end
|
79
|
+
else
|
80
|
+
yield
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
module RablRails
|
2
2
|
module Renderers
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
json = MultiJson.encode(hash)
|
3
|
+
module JSON
|
4
|
+
include Renderers::Hash
|
5
|
+
extend self
|
7
6
|
|
8
|
-
|
7
|
+
def format_output(hash, options = {})
|
8
|
+
hash = { options[:root_name] => hash } if options[:root_name] && RablRails.configuration.include_json_root
|
9
|
+
json = RablRails.configuration.json_engine.dump(hash)
|
10
|
+
params = options.fetch(:params, {})
|
11
|
+
|
12
|
+
RablRails.configuration.enable_jsonp_callbacks && params.has_key?(:callback) ? "#{params[:callback]}(#{json})" : json
|
9
13
|
end
|
10
14
|
|
11
15
|
def resolve_cache_key(key, data)
|
@@ -1,10 +1,12 @@
|
|
1
1
|
module RablRails
|
2
2
|
module Renderers
|
3
|
-
|
3
|
+
module PLIST
|
4
|
+
include Renderers::Hash
|
5
|
+
extend self
|
4
6
|
|
5
|
-
def format_output(hash)
|
6
|
-
hash = {
|
7
|
-
RablRails.plist_engine.dump(hash)
|
7
|
+
def format_output(hash, options = {})
|
8
|
+
hash = { options[:root_name] => hash } if options[:root_name] && RablRails.configuration.include_plist_root
|
9
|
+
RablRails.configuration.plist_engine.dump(hash)
|
8
10
|
end
|
9
11
|
|
10
12
|
def resolve_cache_key(key, data)
|
@@ -2,9 +2,12 @@ require 'active_support/core_ext/hash/conversions'
|
|
2
2
|
|
3
3
|
module RablRails
|
4
4
|
module Renderers
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
module XML
|
6
|
+
include Renderers::Hash
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def format_output(hash, options = {})
|
10
|
+
xml_options = { root: options[:root_name] }.merge!(RablRails.configuration.xml_options)
|
8
11
|
hash.to_xml(xml_options)
|
9
12
|
end
|
10
13
|
|
data/lib/rabl-rails/responder.rb
CHANGED
@@ -31,7 +31,7 @@ module RablRails
|
|
31
31
|
template = if controller.respond_to?(:responder_default_template, true)
|
32
32
|
controller.send(:responder_default_template)
|
33
33
|
else
|
34
|
-
RablRails.responder_default_template
|
34
|
+
RablRails.configuration.responder_default_template
|
35
35
|
end
|
36
36
|
options[:prefixes] = controller._prefixes
|
37
37
|
options[:template] ||= template
|
data/lib/rabl-rails/template.rb
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
module RablRails
|
2
2
|
class CompiledTemplate
|
3
|
-
attr_accessor :
|
4
|
-
|
5
|
-
delegate :[], :[]=, :merge!, :to => :source
|
3
|
+
attr_accessor :nodes, :data, :root_name, :cache_key
|
6
4
|
|
7
5
|
def initialize
|
8
|
-
@
|
6
|
+
@nodes = []
|
9
7
|
@cache_key = false
|
10
8
|
end
|
11
9
|
|
12
10
|
def initialize_dup(other)
|
13
11
|
super
|
14
|
-
self.
|
12
|
+
self.nodes = other.nodes.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_node(n)
|
16
|
+
@nodes << n
|
17
|
+
end
|
18
|
+
|
19
|
+
def extends(template)
|
20
|
+
@nodes.concat template.nodes
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
data/lib/rabl-rails/version.rb
CHANGED
@@ -0,0 +1,131 @@
|
|
1
|
+
module Visitors
|
2
|
+
class ToHash < Visitor
|
3
|
+
include RablRails::Helpers
|
4
|
+
|
5
|
+
attr_reader :_resource
|
6
|
+
|
7
|
+
def initialize(view_context, resource = nil)
|
8
|
+
@_context = view_context
|
9
|
+
@_result = {}
|
10
|
+
@_resource = resource
|
11
|
+
|
12
|
+
copy_instance_variables_from_context
|
13
|
+
end
|
14
|
+
|
15
|
+
def reset_for(resource)
|
16
|
+
@_resource = resource
|
17
|
+
@_result = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_Array n
|
21
|
+
n.each { |i| visit i }
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_Attribute n
|
25
|
+
n.each { |k, v| @_result[k] = _resource.send(v) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def visit_Child n
|
29
|
+
object = object_from_data(_resource, n.data, n.instance_variable_data?)
|
30
|
+
|
31
|
+
@_result[n.name] = if object
|
32
|
+
collection?(object) ? object.map { |o| sub_visit(o, n.nodes) } : sub_visit(object, n.nodes)
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def visit_Code n
|
39
|
+
if !n.condition || instance_exec(_resource, &(n.condition))
|
40
|
+
result = instance_exec _resource, &(n.block)
|
41
|
+
|
42
|
+
if n.merge?
|
43
|
+
raise RablRails::Renderer::PartialError, '`merge` block should return a hash' unless result.is_a?(Hash)
|
44
|
+
@_result.merge!(result)
|
45
|
+
else
|
46
|
+
@_result[n.name] = result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def visit_Condition n
|
52
|
+
@_result.merge!(sub_visit(_resource, n.nodes)) if instance_exec _resource, &(n.condition)
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_Glue n
|
56
|
+
object = object_from_data(_resource, n.data, n.instance_variable_data?)
|
57
|
+
@_result.merge! sub_visit(object, n.template.nodes)
|
58
|
+
end
|
59
|
+
|
60
|
+
def result
|
61
|
+
case RablRails.configuration.result_flags
|
62
|
+
when 0
|
63
|
+
@_result
|
64
|
+
when 1
|
65
|
+
@_result.each { |k, v| @_result[k] = '' if v == nil }
|
66
|
+
when 2, 3
|
67
|
+
@_result.each { |k, v| @_result[k] = nil if v == '' }
|
68
|
+
when 4, 5
|
69
|
+
@_result.delete_if { |_, v| v == nil }
|
70
|
+
when 6
|
71
|
+
@_result.delete_if { |_, v| v == nil || v == '' }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
#
|
78
|
+
# If a method is called inside a 'node' property or a 'if' lambda
|
79
|
+
# it will be passed to context if it exists or treated as a standard
|
80
|
+
# missing method.
|
81
|
+
#
|
82
|
+
def method_missing(name, *args, &block)
|
83
|
+
@_context.respond_to?(name) ? @_context.send(name, *args, &block) : super
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Allow to use partial inside of node blocks (they are evaluated at
|
88
|
+
# rendering time).
|
89
|
+
#
|
90
|
+
def partial(template_path, options = {})
|
91
|
+
raise RablRails::Renderer::PartialError.new("No object was given to partial #{template_path}") unless options[:object]
|
92
|
+
object = options[:object]
|
93
|
+
|
94
|
+
return [] if object.respond_to?(:empty?) && object.empty?
|
95
|
+
|
96
|
+
template = RablRails::Library.instance.compile_template_from_path(template_path, @_context)
|
97
|
+
if object.respond_to?(:each)
|
98
|
+
object.map { |o| sub_visit o, template.nodes }
|
99
|
+
else
|
100
|
+
sub_visit object, template.nodes
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def copy_instance_variables_from_context
|
107
|
+
@_context.instance_variable_get(:@_assigns).each_pair { |k, v|
|
108
|
+
instance_variable_set("@#{k}", v) unless k.to_s.start_with?('_')
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
def sub_visit(resource, nodes)
|
113
|
+
old_result, old_resource, @_result = @_result, @_resource, {}
|
114
|
+
reset_for resource
|
115
|
+
visit nodes
|
116
|
+
result
|
117
|
+
ensure
|
118
|
+
@_result, @_resource = old_result, old_resource
|
119
|
+
end
|
120
|
+
|
121
|
+
def object_from_data(resource, symbol, is_variable)
|
122
|
+
return resource if symbol == nil
|
123
|
+
|
124
|
+
if is_variable
|
125
|
+
instance_variable_get(symbol)
|
126
|
+
else
|
127
|
+
resource.respond_to?(symbol) ? resource.send(symbol) : @_context.send(symbol)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|