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.
Files changed (54) hide show
  1. data/.travis.yml +2 -2
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile +5 -9
  4. data/README.md +5 -3
  5. data/Rakefile +2 -2
  6. data/lib/rabl-rails.rb +21 -74
  7. data/lib/rabl-rails/compiler.rb +28 -38
  8. data/lib/rabl-rails/configuration.rb +48 -0
  9. data/lib/rabl-rails/handler.rb +3 -1
  10. data/lib/rabl-rails/helpers.rb +7 -0
  11. data/lib/rabl-rails/library.rb +43 -16
  12. data/lib/rabl-rails/nodes.rb +6 -0
  13. data/lib/rabl-rails/nodes/attribute.rb +17 -0
  14. data/lib/rabl-rails/nodes/child.rb +12 -0
  15. data/lib/rabl-rails/nodes/code.rb +19 -0
  16. data/lib/rabl-rails/nodes/condition.rb +14 -0
  17. data/lib/rabl-rails/nodes/glue.rb +25 -0
  18. data/lib/rabl-rails/nodes/node.rb +9 -0
  19. data/lib/rabl-rails/railtie.rb +0 -2
  20. data/lib/rabl-rails/renderer.rb +15 -13
  21. data/lib/rabl-rails/renderers/hash.rb +85 -0
  22. data/lib/rabl-rails/renderers/json.rb +9 -5
  23. data/lib/rabl-rails/renderers/plist.rb +6 -4
  24. data/lib/rabl-rails/renderers/xml.rb +6 -3
  25. data/lib/rabl-rails/responder.rb +1 -1
  26. data/lib/rabl-rails/template.rb +11 -5
  27. data/lib/rabl-rails/version.rb +1 -1
  28. data/lib/rabl-rails/visitors.rb +2 -0
  29. data/lib/rabl-rails/visitors/to_hash.rb +131 -0
  30. data/lib/rabl-rails/visitors/visitor.rb +17 -0
  31. data/rabl-rails.gemspec +3 -5
  32. data/test/helper.rb +75 -0
  33. data/test/renderers/test_hash_renderer.rb +90 -0
  34. data/test/renderers/test_json_renderer.rb +46 -0
  35. data/test/renderers/test_plist_renderer.rb +42 -0
  36. data/test/renderers/test_xml_renderer.rb +37 -0
  37. data/test/test_compiler.rb +283 -0
  38. data/test/test_configuration.rb +31 -0
  39. data/test/test_hash_visitor.rb +224 -0
  40. data/test/test_library.rb +85 -0
  41. data/test/{render_test.rb → test_render.rb} +18 -24
  42. metadata +99 -108
  43. data/lib/rabl-rails/condition.rb +0 -10
  44. data/lib/rabl-rails/renderers/base.rb +0 -171
  45. data/test/base_renderer_test.rb +0 -67
  46. data/test/cache_templates_test.rb +0 -35
  47. data/test/compiler_test.rb +0 -233
  48. data/test/deep_nesting_test.rb +0 -56
  49. data/test/keyword_test.rb +0 -47
  50. data/test/non_restful_response_test.rb +0 -35
  51. data/test/renderers/json_renderer_test.rb +0 -189
  52. data/test/renderers/plist_renderer_test.rb +0 -135
  53. data/test/renderers/xml_renderer_test.rb +0 -137
  54. data/test/test_helper.rb +0 -68
@@ -1,10 +0,0 @@
1
- module RablRails
2
- class Condition
3
- attr_reader :proc, :source
4
-
5
- def initialize(proc, source)
6
- @proc = proc
7
- @source = source
8
- end
9
- end
10
- end
@@ -1,171 +0,0 @@
1
- module RablRails
2
- module Renderers
3
- class PartialError < StandardError; end
4
-
5
- class Base
6
- attr_accessor :_options
7
-
8
- def initialize(context, locals = nil) # :nodoc:
9
- @_context = context
10
- @_options = {}
11
- @_resource = locals[:resource] if locals
12
- setup_render_context
13
- end
14
-
15
- #
16
- # Render a template.
17
- # Uses the compiled template source to get a hash with the actual
18
- # data and then format the result according to the `format_result`
19
- # method defined by the renderer.
20
- #
21
- def render(template)
22
- collection_or_resource = if template.data
23
- if @_context.respond_to?(template.data)
24
- @_context.send(template.data)
25
- else
26
- instance_variable_get(template.data)
27
- end
28
- end
29
- collection_or_resource ||= @_resource
30
-
31
- render_with_cache(template.cache_key, collection_or_resource) do
32
- output_hash = collection_or_resource.respond_to?(:each) ? render_collection(collection_or_resource, template.source)
33
- : render_resource(collection_or_resource, template.source)
34
- _options[:root_name] = template.root_name
35
- format_output(output_hash)
36
- end
37
- end
38
-
39
- #
40
- # Format a hash into the desired output.
41
- # Renderer subclasses must implement this method
42
- #
43
- def format_output(hash)
44
- raise "Muse be implemented by renderer"
45
- end
46
-
47
- protected
48
-
49
- def render_with_cache(key, collection_or_resource, &block)
50
- if !key.is_a?(FalseClass) && ActionController::Base.perform_caching
51
- Rails.cache.fetch(resolve_cache_key(key, collection_or_resource)) do
52
- yield
53
- end
54
- else
55
- yield
56
- end
57
- end
58
-
59
- #
60
- # Render a single resource as a hash, according to the compiled
61
- # template source passed.
62
- #
63
- def render_resource(data, source)
64
- source.inject({}) { |output, (key, value)|
65
-
66
- out = case value
67
- when Symbol
68
- data.send(value) # attributes
69
- when Proc
70
- result = instance_exec data, &value
71
-
72
- if key.to_s.start_with?('_') # merge
73
- raise PartialError, '`merge` block should return a hash' unless result.is_a?(Hash)
74
- output.merge!(result)
75
- next output
76
- else # node
77
- result
78
- end
79
- when Array # node with condition
80
- next output if !instance_exec data, &(value.first)
81
- instance_exec data, &(value.last)
82
- when Hash
83
- current_value = value.dup
84
- object = object_from_data(data, current_value.delete(:_data))
85
-
86
- if key.to_s.start_with?('_') # glue
87
- output.merge!(render_resource(object, current_value))
88
- next output
89
- else # child
90
- if object
91
- object.respond_to?(:each) ? render_collection(object, current_value) : render_resource(object, current_value)
92
- else
93
- nil
94
- end
95
- end
96
- when Condition
97
- if instance_exec data, &(value.proc)
98
- output.merge!(render_resource(data, value.source))
99
- end
100
- next output
101
- end
102
- output[key] = out
103
- output
104
- }
105
- end
106
-
107
- def params
108
- @_context.params
109
- end
110
-
111
- #
112
- # Call the render_resource mtehod on each object of the collection
113
- # and return an array of the returned values.
114
- #
115
- def render_collection(collection, source)
116
- collection.map { |o| render_resource(o, source) }
117
- end
118
-
119
- #
120
- # Allow to use partial inside of node blocks (they are evaluated at)
121
- # rendering time.
122
- #
123
- def partial(template_path, options = {})
124
- raise PartialError.new("No object was given to partial #{template_path}") unless options[:object]
125
- object = options[:object]
126
-
127
- return [] if object.respond_to?(:empty?) && object.empty?
128
-
129
- template = Library.instance.compile_template_from_path(template_path)
130
- object.respond_to?(:each) ? render_collection(object, template.source) : render_resource(object, template.source)
131
- end
132
-
133
- #
134
- # If a method is called inside a 'node' property or a 'if' lambda
135
- # it will be passed to context if it exists or treated as a standard
136
- # missing method.
137
- #
138
- def method_missing(name, *args, &block)
139
- @_context.respond_to?(name) ? @_context.send(name, *args, &block) : super
140
- end
141
-
142
- def resolve_cache_key(key, data)
143
- return data.cache_key unless key
144
- key.is_a?(Proc) ? instance_exec(data, &key) : key
145
- end
146
-
147
- private
148
-
149
- def object_from_data(data, symbol)
150
- return data if symbol == nil
151
-
152
- if symbol.to_s.start_with?('@')
153
- instance_variable_get(symbol)
154
- else
155
- data.respond_to?(symbol) ? data.send(symbol) : send(symbol)
156
- end
157
- end
158
-
159
- #
160
- # Copy assigns from controller's context into this
161
- # renderer context to include instances variables when
162
- # evaluating 'node' properties.
163
- #
164
- def setup_render_context
165
- @_context.instance_variable_get(:@_assigns).each_pair { |k, v|
166
- instance_variable_set("@#{k}", v) unless k.to_s.start_with?('_')
167
- }
168
- end
169
- end
170
- end
171
- end
@@ -1,67 +0,0 @@
1
- require 'test_helper'
2
-
3
- class TestBaseRenderer < ActiveSupport::TestCase
4
-
5
- RablRails::Renderers::Base.class_eval do
6
- def format_output(hash)
7
- hash
8
- end
9
- end
10
-
11
- setup do
12
- @data = User.new(1, 'foobar', 'male')
13
-
14
- @context = Context.new
15
- @context.assigns['data'] = @data
16
-
17
- @template = RablRails::CompiledTemplate.new
18
- @template.source = {}
19
- @template.data = :@data
20
-
21
- @cache = double
22
- Rails.stub(:cache).and_return(@cache)
23
- end
24
-
25
- def render_hash
26
- RablRails::Renderers::Base.new(@context).render(@template)
27
- end
28
-
29
- test "child with nil data should render nil" do
30
- @template.source = { :author => { :_data => :@nil, :name => :name } }
31
- assert_equal({ :author => nil }, render_hash)
32
- end
33
-
34
- test "properly handle assigns with symbol keys" do
35
- @context.assigns[:foo] = 'bar'
36
- assert_nothing_raised { render_hash }
37
- end
38
-
39
- test "cache should be applied if no cache key is given" do
40
- @cache.should_not_receive(:fetch)
41
- render_hash
42
- end
43
-
44
- test "cache should not be used if disabled in Rails configuration" do
45
- ActionController::Base.stub(:perform_caching).and_return(false)
46
- @cache.should_not_receive(:fetch)
47
- @template.cache_key = 'something'
48
- render_hash
49
- end
50
-
51
- test "cache shoud use #cache_key as default" do
52
- ActionController::Base.stub(:perform_caching).and_return(true)
53
- @data.stub(:cache_key).and_return('data_cache_key')
54
- @cache.should_receive(:fetch).with('data_cache_key').and_return({ some: 'hash' })
55
- @template.cache_key = nil
56
-
57
- assert_equal({ some: 'hash' }, render_hash)
58
- end
59
-
60
- test "cache should use the proc if given" do
61
- ActionController::Base.stub(:perform_caching).and_return(true)
62
- @template.cache_key = ->(u) { 'proc_cache_key' }
63
- @cache.should_receive(:fetch).with('proc_cache_key').and_return({ some: 'hash' })
64
-
65
- assert_equal({ some: 'hash' }, render_hash)
66
- end
67
- end
@@ -1,35 +0,0 @@
1
- require 'test_helper'
2
-
3
- class CacheTemplatesTest < ActiveSupport::TestCase
4
-
5
- setup do
6
- RablRails::Library.reset_instance
7
- @library = RablRails::Library.instance
8
- RablRails.cache_templates = true
9
- end
10
-
11
- test "cache templates if perform_caching is active and cache_templates is enabled" do
12
- ActionController::Base.stub(:perform_caching).and_return(true)
13
- @library.compile_template_from_source('', 'some/path')
14
- t = @library.compile_template_from_source("attribute :id", 'some/path')
15
-
16
- assert_equal({}, t.source)
17
- end
18
-
19
- test "cached templates should not be modifiable in place" do
20
- ActionController::Base.stub(:perform_caching).and_return(true)
21
- t = @library.compile_template_from_source('', 'some/path')
22
-
23
- t.merge!(:_data => :foo)
24
-
25
- assert_equal({}, @library.compile_template_from_path('some/path').source)
26
- end
27
-
28
- test "don't cache templates cache_templates is enabled but perform_caching is not active" do
29
- ActionController::Base.stub(:perform_caching).and_return(false)
30
- @library.compile_template_from_source('', 'some/path')
31
- t = @library.compile_template_from_source("attribute :id", 'some/path')
32
-
33
- assert_equal({ :id => :id }, t.source)
34
- end
35
- end
@@ -1,233 +0,0 @@
1
- require 'test_helper'
2
-
3
- class CompilerTest < ActiveSupport::TestCase
4
-
5
- setup do
6
- @user = User.new
7
- @compiler = RablRails::Compiler.new
8
- end
9
-
10
- test "compiler return a compiled template" do
11
- assert_instance_of RablRails::CompiledTemplate, @compiler.compile_source("")
12
- end
13
-
14
- test "object set data for the template" do
15
- t = @compiler.compile_source(%{ object :@user })
16
- assert_equal :@user, t.data
17
- assert_equal({}, t.source)
18
- end
19
-
20
- test "object property can define root name" do
21
- t = @compiler.compile_source(%{ object :@user => :author })
22
- assert_equal :@user, t.data
23
- assert_equal :author, t.root_name
24
- assert_equal({}, t.source)
25
- end
26
-
27
- test "root can be defined via keyword" do
28
- t = @compiler.compile_source(%{ root :author })
29
- assert_equal :author, t.root_name
30
- end
31
-
32
- test "root keyword override object root" do
33
- t = @compiler.compile_source(%{ object :@user ; root :author })
34
- assert_equal :author, t.root_name
35
- end
36
-
37
- test "collection set the data for the template" do
38
- t = @compiler.compile_source(%{ collection :@user })
39
- assert_equal :@user, t.data
40
- assert_equal({}, t.source)
41
- end
42
-
43
- test "collection property can define root name" do
44
- t = @compiler.compile_source(%{ collection :@user => :users })
45
- assert_equal :@user, t.data
46
- assert_equal :users, t.root_name
47
- assert_equal({}, t.source)
48
- end
49
-
50
- test "collection property can define root name via options" do
51
- t = @compiler.compile_source(%{ collection :@user, :root => :users })
52
- assert_equal :@user, t.data
53
- assert_equal :users, t.root_name
54
- end
55
-
56
- test "root can be set to false via options" do
57
- t = @compiler.compile_source(%( object :@user, root: false))
58
- assert_equal false, t.root_name
59
- end
60
-
61
- test "template should not have a cache key if cache is not enable" do
62
- t = @compiler.compile_source('')
63
- assert_equal false, t.cache_key
64
- end
65
-
66
- test "cache can take no argument" do
67
- t = @compiler.compile_source(%{ cache })
68
- assert_nil t.cache_key
69
- end
70
-
71
- test "cache can take a block" do
72
- t = @compiler.compile_source(%( cache { 'foo' }))
73
- assert_instance_of Proc, t.cache_key
74
- end
75
-
76
- # Compilation
77
-
78
- test "simple attributes are compiled to hash" do
79
- t = @compiler.compile_source(%{ attributes :id, :name })
80
- assert_equal({ :id => :id, :name => :name}, t.source)
81
- end
82
-
83
- test "attributes appeared only once even if called mutiple times" do
84
- t = @compiler.compile_source(%{ attribute :id ; attribute :id })
85
- assert_equal({ :id => :id }, t.source)
86
- end
87
-
88
- test "attribute can be aliased through :as option" do
89
- t = @compiler.compile_source(%{ attribute :foo, :as => :bar })
90
- assert_equal({ :bar => :foo}, t.source)
91
- end
92
-
93
- test "attribute can be aliased through hash" do
94
- t = @compiler.compile_source(%{ attribute :foo => :bar })
95
- assert_equal({ :bar => :foo }, t.source)
96
- end
97
-
98
- test "multiple attributes can be aliased" do
99
- t = @compiler.compile_source(%{ attributes :foo => :bar, :id => :uid })
100
- assert_equal({ :bar => :foo, :uid => :id }, t.source)
101
- end
102
-
103
- test "child with association use association name as data" do
104
- t = @compiler.compile_source(%{ child :address do attributes :foo end})
105
- assert_equal({ :address => { :_data => :address, :foo => :foo } }, t.source)
106
- end
107
-
108
- test "child with association can be aliased" do
109
- t = @compiler.compile_source(%{ child :address => :bar do attributes :foo end})
110
- assert_equal({ :bar => { :_data => :address, :foo => :foo } }, t.source)
111
- end
112
-
113
- test "child with root name defined as option" do
114
- t = @compiler.compile_source(%{ child(:user, :root => :author) do attributes :foo end })
115
- assert_equal({ :author => { :_data => :user, :foo => :foo } }, t.source)
116
- end
117
-
118
- test "child with arbitrary source store the data with the template" do
119
- t = @compiler.compile_source(%{ child :@user => :author do attribute :name end })
120
- assert_equal({ :author => { :_data => :@user, :name => :name } }, t.source)
121
- end
122
-
123
- test "child with succint partial notation" do
124
- mock_template = RablRails::CompiledTemplate.new
125
- mock_template.source = { :id => :id }
126
- RablRails::Library.reset_instance
127
- RablRails::Library.instance.stub(:compile_template_from_path).with('users/base').and_return(mock_template)
128
-
129
- t = @compiler.compile_source(%{child(:user, :partial => 'users/base') })
130
- assert_equal({:user => { :_data => :user, :id => :id } }, t.source)
131
- end
132
-
133
- test "glue is compiled as a child but with anonymous name" do
134
- t = @compiler.compile_source(%{ glue(:@user) do attribute :name end })
135
- assert_equal({ :_glue0 => { :_data => :@user, :name => :name } }, t.source)
136
- end
137
-
138
- test "multiple glue don't come with name collisions" do
139
- t = @compiler.compile_source(%{
140
- glue :@user do attribute :name end
141
- glue :@user do attribute :foo end
142
- })
143
-
144
- assert_equal({
145
- :_glue0 => { :_data => :@user, :name => :name},
146
- :_glue1 => { :_data => :@user, :foo => :foo}
147
- }, t.source)
148
- end
149
-
150
- test "glue accepts all dsl in its body" do
151
- t = @compiler.compile_source(%{
152
- glue :@user do node(:foo) { |u| u.name } end
153
- })
154
-
155
- assert_not_nil(t.source[:_glue0])
156
- s = t.source[:_glue0]
157
-
158
- assert_equal(:@user, s[:_data])
159
- assert_instance_of(Proc, s[:foo])
160
- end
161
-
162
- test "extends use other template source as itself" do
163
- template = double('template', :source => { :id => :id })
164
- RablRails::Library.reset_instance
165
- RablRails::Library.instance.stub(:compile_template_from_path).with('users/base').and_return(template)
166
- t = @compiler.compile_source(%{ extends 'users/base' })
167
- assert_equal({ :id => :id }, t.source)
168
- end
169
-
170
- test "extends should not overwrite nodes previously defined" do
171
- skip('Bug reported by @abrisse')
172
-
173
- template = mock('file_template', :source => %(condition(-> { true }) { 'foo' }))
174
- lookup_context = mock
175
- lookup_context.stub(:find_template).with('users/xtnd', [], false).and_return(template)
176
- RablRails::Library.reset_instance
177
- RablRails::Library.instance.instance_variable_set(:@lookup_context, lookup_context)
178
-
179
- t = @compiler.compile_source(%{
180
- condition(-> { false }) { 'bar' }
181
- extends('users/xtnd')
182
- })
183
-
184
- assert_equal 2, t.source.keys.size
185
- end
186
-
187
- test "node are compiled without evaluating the block" do
188
- t = @compiler.compile_source(%{ node(:foo) { bar } })
189
- assert_not_nil t.source[:foo]
190
- assert_instance_of Proc, t.source[:foo]
191
- end
192
-
193
- test "node with condition are compiled as an array of procs" do
194
- t = @compiler.compile_source(%{ node(:foo, :if => lambda { |m| m.foo.present? }) do |m| m.foo end })
195
- assert_not_nil t.source[:foo]
196
- assert_instance_of Array, t.source[:foo]
197
- assert_equal 2, t.source[:foo].size
198
- end
199
-
200
- test "node can take no arguments and behave like a merge" do
201
- t = @compiler.compile_source(%{ node do |m| m.foo end })
202
- assert_instance_of Proc, t.source[:_merge0]
203
- end
204
-
205
- test "merge compile like a node but with a reserved keyword as name" do
206
- t = @compiler.compile_source(%{ merge do |m| m.foo end })
207
- assert_instance_of Proc, t.source[:_merge0]
208
- end
209
-
210
- test "conditionnal block compile nicely" do
211
- t = @compiler.compile_source(%{ condition(->(u) {}) do attributes :secret end })
212
- assert_instance_of RablRails::Condition, t.source[:_if0]
213
- assert_equal({ :secret => :secret }, t.source[:_if0].source)
214
- end
215
-
216
- test "compile with no object" do
217
- t = @compiler.compile_source(%{
218
- object false
219
- child(:@user => :user) do
220
- attribute :id
221
- end
222
- })
223
-
224
- assert_equal({ :user => { :_data => :@user, :id => :id } }, t.source)
225
- assert_equal false, t.data
226
- end
227
-
228
- test "name extraction from argument" do
229
- assert_equal [:@users, 'users'], @compiler.send(:extract_data_and_name, :@users)
230
- assert_equal [:users, :users], @compiler.send(:extract_data_and_name, :users)
231
- assert_equal [:@users, :authors], @compiler.send(:extract_data_and_name, :@users => :authors)
232
- end
233
- end