draper 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/CHANGELOG.md +3 -0
  2. data/CONTRIBUTING.md +5 -1
  3. data/Gemfile +23 -9
  4. data/README.md +144 -52
  5. data/Rakefile +1 -0
  6. data/draper.gemspec +1 -1
  7. data/lib/draper.rb +9 -6
  8. data/lib/draper/decoratable.rb +3 -7
  9. data/lib/draper/decoratable/equality.rb +14 -0
  10. data/lib/draper/decorator.rb +4 -7
  11. data/lib/draper/helper_proxy.rb +22 -3
  12. data/lib/draper/test/devise_helper.rb +18 -22
  13. data/lib/draper/test/rspec_integration.rb +4 -0
  14. data/lib/draper/test_case.rb +20 -0
  15. data/lib/draper/version.rb +1 -1
  16. data/lib/draper/view_context.rb +75 -13
  17. data/lib/draper/view_context/build_strategy.rb +48 -0
  18. data/lib/draper/view_helpers.rb +2 -2
  19. data/spec/draper/collection_decorator_spec.rb +169 -196
  20. data/spec/draper/decoratable/equality_spec.rb +10 -0
  21. data/spec/draper/decoratable_spec.rb +107 -132
  22. data/spec/draper/decorated_association_spec.rb +99 -96
  23. data/spec/draper/decorator_spec.rb +408 -434
  24. data/spec/draper/finders_spec.rb +160 -126
  25. data/spec/draper/helper_proxy_spec.rb +38 -8
  26. data/spec/draper/view_context/build_strategy_spec.rb +116 -0
  27. data/spec/draper/view_context_spec.rb +154 -0
  28. data/spec/draper/view_helpers_spec.rb +4 -37
  29. data/spec/dummy/app/controllers/posts_controller.rb +7 -0
  30. data/spec/dummy/app/decorators/post_decorator.rb +26 -2
  31. data/spec/dummy/app/helpers/application_helper.rb +3 -0
  32. data/spec/dummy/app/mailers/post_mailer.rb +10 -0
  33. data/spec/dummy/app/models/admin.rb +5 -0
  34. data/spec/dummy/app/models/mongoid_post.rb +5 -0
  35. data/spec/dummy/app/models/user.rb +5 -0
  36. data/spec/dummy/app/views/posts/_post.html.erb +15 -0
  37. data/spec/dummy/bin/rails +4 -0
  38. data/spec/dummy/config/application.rb +9 -3
  39. data/spec/dummy/config/boot.rb +2 -7
  40. data/spec/dummy/config/environments/development.rb +2 -3
  41. data/spec/dummy/config/environments/production.rb +2 -0
  42. data/spec/dummy/config/environments/test.rb +3 -4
  43. data/spec/dummy/config/initializers/secret_token.rb +1 -0
  44. data/spec/dummy/config/mongoid.yml +80 -0
  45. data/spec/dummy/config/routes.rb +2 -0
  46. data/spec/dummy/fast_spec/post_decorator_spec.rb +38 -0
  47. data/spec/dummy/lib/tasks/test.rake +11 -5
  48. data/spec/dummy/spec/decorators/devise_spec.rb +64 -0
  49. data/spec/dummy/spec/decorators/helpers_spec.rb +21 -0
  50. data/spec/dummy/spec/decorators/post_decorator_spec.rb +26 -6
  51. data/spec/dummy/spec/decorators/spec_type_spec.rb +7 -0
  52. data/spec/dummy/spec/decorators/view_context_spec.rb +22 -0
  53. data/spec/dummy/spec/mailers/post_mailer_spec.rb +10 -6
  54. data/spec/dummy/spec/models/mongoid_post_spec.rb +10 -0
  55. data/spec/dummy/spec/models/post_spec.rb +5 -5
  56. data/spec/dummy/spec/spec_helper.rb +1 -0
  57. data/spec/dummy/test/decorators/minitest/devise_test.rb +64 -0
  58. data/spec/dummy/test/decorators/minitest/helpers_test.rb +21 -0
  59. data/spec/dummy/{mini_test/mini_test_integration_test.rb → test/decorators/minitest/spec_type_test.rb} +9 -3
  60. data/spec/dummy/test/decorators/minitest/view_context_test.rb +24 -0
  61. data/spec/dummy/test/decorators/test_unit/devise_test.rb +64 -0
  62. data/spec/dummy/test/decorators/test_unit/helpers_test.rb +21 -0
  63. data/spec/dummy/test/decorators/test_unit/view_context_test.rb +24 -0
  64. data/spec/dummy/test/minitest_helper.rb +4 -0
  65. data/spec/dummy/test/test_helper.rb +3 -0
  66. data/spec/generators/decorator/decorator_generator_spec.rb +1 -0
  67. data/spec/integration/integration_spec.rb +31 -6
  68. data/spec/spec_helper.rb +32 -25
  69. data/spec/support/shared_examples/decoratable_equality.rb +40 -0
  70. data/spec/support/shared_examples/view_helpers.rb +39 -0
  71. metadata +56 -44
  72. data/spec/dummy/README.rdoc +0 -261
  73. data/spec/dummy/spec/decorators/rspec_integration_spec.rb +0 -19
  74. data/spec/support/action_controller.rb +0 -12
  75. data/spec/support/active_model.rb +0 -7
  76. data/spec/support/active_record.rb +0 -9
  77. data/spec/support/decorators/decorator_with_application_helper.rb +0 -25
  78. data/spec/support/decorators/namespaced_product_decorator.rb +0 -5
  79. data/spec/support/decorators/non_active_model_product_decorator.rb +0 -2
  80. data/spec/support/decorators/product_decorator.rb +0 -23
  81. data/spec/support/decorators/products_decorator.rb +0 -10
  82. data/spec/support/decorators/some_thing_decorator.rb +0 -2
  83. data/spec/support/decorators/specific_product_decorator.rb +0 -2
  84. data/spec/support/decorators/widget_decorator.rb +0 -2
  85. data/spec/support/models/namespaced_product.rb +0 -49
  86. data/spec/support/models/non_active_model_product.rb +0 -3
  87. data/spec/support/models/product.rb +0 -95
  88. data/spec/support/models/some_thing.rb +0 -5
  89. data/spec/support/models/uninferrable_decorator_model.rb +0 -3
  90. data/spec/support/models/widget.rb +0 -2
@@ -1,286 +1,259 @@
1
1
  require 'spec_helper'
2
+ require 'support/shared_examples/view_helpers'
2
3
 
3
- describe Draper::CollectionDecorator do
4
- before { ApplicationController.new.view_context }
5
- subject { Draper::CollectionDecorator.new(source, with: ProductDecorator) }
6
- let(:source){ [Product.new, Product.new] }
7
- let(:non_active_model_source){ NonActiveModelProduct.new }
4
+ module Draper
5
+ describe CollectionDecorator do
6
+ it_behaves_like "view helpers", CollectionDecorator.new([])
8
7
 
9
- it "decorates a collection's items" do
10
- subject.each do |item|
11
- item.should be_decorated_with ProductDecorator
12
- end
13
- end
8
+ describe "#initialize" do
9
+ describe "options validation" do
14
10
 
15
- it "sets the decorated items' source models" do
16
- subject.map{|item| item.source}.should == source
17
- end
11
+ it "does not raise error on valid options" do
12
+ valid_options = {with: Decorator, context: {}}
13
+ expect{CollectionDecorator.new([], valid_options)}.not_to raise_error
14
+ end
18
15
 
19
- context "with context" do
20
- subject { Draper::CollectionDecorator.new(source, with: ProductDecorator, context: {some: 'context'}) }
16
+ it "raises error on invalid options" do
17
+ expect{CollectionDecorator.new([], foo: "bar")}.to raise_error ArgumentError, /Unknown key/
18
+ end
19
+ end
20
+ end
21
21
 
22
- its(:context) { should == {some: 'context'} }
22
+ context "with context" do
23
+ it "stores the context itself" do
24
+ context = {some: "context"}
25
+ decorator = CollectionDecorator.new([], context: context)
23
26
 
24
- it "passes context to the individual decorators" do
25
- subject.each do |item|
26
- item.context.should == {some: 'context'}
27
+ expect(decorator.context).to be context
27
28
  end
28
- end
29
29
 
30
- it "does not tie the individual decorators' contexts together" do
31
- subject.each do |item|
32
- item.context.should == {some: 'context'}
33
- item.context = {alt: 'context'}
34
- item.context.should == {alt: 'context'}
30
+ it "passes context to the individual decorators" do
31
+ context = {some: "context"}
32
+ decorator = CollectionDecorator.new([Product.new, Product.new], context: context)
33
+
34
+ decorator.each do |item|
35
+ expect(item.context).to be context
36
+ end
35
37
  end
36
38
  end
37
39
 
38
40
  describe "#context=" do
39
- it "updates the collection decorator's context" do
40
- subject.context = {other: 'context'}
41
- subject.context.should == {other: 'context'}
41
+ it "updates the stored context" do
42
+ decorator = CollectionDecorator.new([], context: {some: "context"})
43
+ new_context = {other: "context"}
44
+
45
+ decorator.context = new_context
46
+ expect(decorator.context).to be new_context
42
47
  end
43
48
 
44
49
  context "when the collection is already decorated" do
45
50
  it "updates the items' context" do
46
- subject.decorated_collection
47
- subject.context = {other: 'context'}
48
- subject.each do |item|
49
- item.context.should == {other: 'context'}
51
+ decorator = CollectionDecorator.new([Product.new, Product.new], context: {some: "context"})
52
+ decorator.decorated_collection # trigger decoration
53
+ new_context = {other: "context"}
54
+
55
+ decorator.context = new_context
56
+ decorator.each do |item|
57
+ expect(item.context).to be new_context
50
58
  end
51
59
  end
52
60
  end
53
61
 
54
62
  context "when the collection has not yet been decorated" do
55
63
  it "does not trigger decoration" do
56
- subject.should_not_receive(:decorated_collection)
57
- subject.context = {other: 'context'}
64
+ decorator = CollectionDecorator.new([])
65
+
66
+ decorator.should_not_receive(:decorated_collection)
67
+ decorator.context = {other: "context"}
58
68
  end
59
69
 
60
70
  it "sets context after decoration is triggered" do
61
- subject.context = {other: 'context'}
62
- subject.each do |item|
63
- item.context.should == {other: 'context'}
71
+ decorator = CollectionDecorator.new([Product.new, Product.new], context: {some: "context"})
72
+ new_context = {other: "context"}
73
+
74
+ decorator.context = new_context
75
+ decorator.each do |item|
76
+ expect(item.context).to be new_context
64
77
  end
65
78
  end
66
79
  end
67
80
  end
68
- end
69
81
 
70
- describe "#initialize" do
71
- describe "options validation" do
72
- let(:valid_options) { {with: ProductDecorator, context: {}} }
82
+ describe "item decoration" do
83
+ it "sets decorated items' source models" do
84
+ collection = [Product.new, Product.new]
85
+ decorator = CollectionDecorator.new(collection)
73
86
 
74
- it "does not raise error on valid options" do
75
- expect { Draper::CollectionDecorator.new(source, valid_options) }.to_not raise_error
76
- end
77
-
78
- it "raises error on invalid options" do
79
- expect { Draper::CollectionDecorator.new(source, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError, /Unknown key/)
87
+ decorator.zip collection do |item, source|
88
+ expect(item.source).to be source
89
+ end
80
90
  end
81
- end
82
- end
83
-
84
- describe "item decoration" do
85
- subject { subject_class.new(source, options) }
86
- let(:decorator_classes) { subject.decorated_collection.map(&:class) }
87
- let(:source) { [Product.new, Widget.new] }
88
-
89
- context "when the :with option was given" do
90
- let(:options) { {with: SpecificProductDecorator} }
91
91
 
92
- context "and the decorator can't be inferred from the class" do
93
- let(:subject_class) { Draper::CollectionDecorator }
92
+ context "when the item decorator is inferrable from the collection decorator" do
93
+ context "when the :with option was given" do
94
+ it "uses the :with option" do
95
+ decorator = ProductsDecorator.new([Product.new], with: OtherDecorator)
94
96
 
95
- it "uses the :with option" do
96
- decorator_classes.should == [SpecificProductDecorator, SpecificProductDecorator]
97
+ expect(*decorator).to be_decorated_with OtherDecorator
98
+ end
97
99
  end
98
- end
99
100
 
100
- context "and the decorator is inferrable from the class" do
101
- let(:subject_class) { ProductsDecorator }
101
+ context "when the :with option was not given" do
102
+ it "infers the item decorator from the collection decorator" do
103
+ decorator = ProductsDecorator.new([Product.new])
102
104
 
103
- it "uses the :with option" do
104
- decorator_classes.should == [SpecificProductDecorator, SpecificProductDecorator]
105
+ expect(*decorator).to be_decorated_with ProductDecorator
106
+ end
105
107
  end
106
108
  end
107
- end
108
-
109
- context "when the :with option was not given" do
110
- let(:options) { {} }
111
109
 
112
- context "and the decorator can't be inferred from the class" do
113
- let(:subject_class) { Draper::CollectionDecorator }
110
+ context "when the item decorator is not inferrable from the collection decorator" do
111
+ context "when the :with option was given" do
112
+ it "uses the :with option" do
113
+ decorator = CollectionDecorator.new([Product.new], with: OtherDecorator)
114
114
 
115
- it "infers the decorator from each item" do
116
- decorator_classes.should == [ProductDecorator, WidgetDecorator]
115
+ expect(*decorator).to be_decorated_with OtherDecorator
116
+ end
117
117
  end
118
- end
119
118
 
120
- context "and the decorator is inferrable from the class" do
121
- let(:subject_class) { ProductsDecorator}
119
+ context "when the :with option was not given" do
120
+ it "infers the item decorator from each item" do
121
+ decorator = CollectionDecorator.new([double(decorate: :inferred_decorator)])
122
122
 
123
- it "infers the decorator" do
124
- decorator_classes.should == [ProductDecorator, ProductDecorator]
123
+ expect(*decorator).to be :inferred_decorator
124
+ end
125
125
  end
126
126
  end
127
127
  end
128
- end
129
128
 
130
- describe ".delegate" do
131
- subject { Class.new(Draper::CollectionDecorator) }
129
+ describe ".delegate" do
130
+ protect_class ProductsDecorator
132
131
 
133
- it "defaults the :to option to :source" do
134
- Draper::CollectionDecorator.superclass.should_receive(:delegate).with(:foo, :bar, to: :source)
135
- subject.delegate :foo, :bar
136
- end
137
-
138
- it "does not overwrite the :to option if supplied" do
139
- Draper::CollectionDecorator.superclass.should_receive(:delegate).with(:foo, :bar, to: :baz)
140
- subject.delegate :foo, :bar, to: :baz
141
- end
142
- end
143
-
144
- describe "#find" do
145
- context "with a block" do
146
- it "decorates Enumerable#find" do
147
- subject.decorated_collection.should_receive(:find)
148
- subject.find {|p| p.title == "title" }
132
+ it "defaults the :to option to :source" do
133
+ Object.should_receive(:delegate).with(:foo, :bar, to: :source)
134
+ ProductsDecorator.delegate :foo, :bar
149
135
  end
150
- end
151
136
 
152
- context "without a block" do
153
- it "decorates Model.find" do
154
- source.should_not_receive(:find)
155
- Product.should_receive(:find).with(1).and_return(:product)
156
- subject.find(1).should == ProductDecorator.new(:product)
137
+ it "does not overwrite the :to option if supplied" do
138
+ Object.should_receive(:delegate).with(:foo, :bar, to: :baz)
139
+ ProductsDecorator.delegate :foo, :bar, to: :baz
157
140
  end
158
141
  end
159
- end
160
142
 
161
- describe "#helpers" do
162
- it "returns a HelperProxy" do
163
- subject.helpers.should be_a Draper::HelperProxy
164
- end
165
-
166
- it "is aliased to #h" do
167
- subject.h.should be subject.helpers
168
- end
143
+ describe "#find" do
144
+ context "with a block" do
145
+ it "decorates Enumerable#find" do
146
+ decorator = CollectionDecorator.new([])
169
147
 
170
- it "initializes the wrapper only once" do
171
- helper_proxy = subject.helpers
172
- helper_proxy.stub(:test_method) { "test_method" }
173
- subject.helpers.test_method.should == "test_method"
174
- subject.helpers.test_method.should == "test_method"
175
- end
176
- end
148
+ decorator.decorated_collection.should_receive(:find).and_return(:delegated)
149
+ expect(decorator.find{|p| p.title == "title"}).to be :delegated
150
+ end
151
+ end
177
152
 
178
- describe "#localize" do
179
- before { subject.helpers.should_receive(:localize).with(:an_object, {some: 'parameter'}) }
153
+ context "without a block" do
154
+ it "decorates Model.find" do
155
+ item_decorator = Class.new
156
+ decorator = CollectionDecorator.new([], with: item_decorator)
180
157
 
181
- it "delegates to helpers" do
182
- subject.localize(:an_object, some: 'parameter')
158
+ item_decorator.should_receive(:find).with(1).and_return(:delegated)
159
+ expect(decorator.find(1)).to be :delegated
160
+ end
161
+ end
183
162
  end
184
163
 
185
- it "is aliased to #l" do
186
- subject.l(:an_object, some: 'parameter')
187
- end
188
- end
164
+ describe "#to_ary" do
165
+ # required for `render @collection` in Rails
166
+ it "delegates to the decorated collection" do
167
+ decorator = CollectionDecorator.new([])
189
168
 
190
- describe ".helpers" do
191
- it "returns a HelperProxy" do
192
- subject.class.helpers.should be_a Draper::HelperProxy
169
+ decorator.decorated_collection.should_receive(:to_ary).and_return(:delegated)
170
+ expect(decorator.to_ary).to be :delegated
171
+ end
193
172
  end
194
173
 
195
- it "is aliased to .h" do
196
- subject.class.h.should be subject.class.helpers
197
- end
198
- end
174
+ it "delegates array methods to the decorated collection" do
175
+ decorator = CollectionDecorator.new([])
199
176
 
200
- describe "#to_ary" do
201
- # required for `render @collection` in Rails
202
- it "delegates to the decorated collection" do
203
- subject.decorated_collection.stub to_ary: :an_array
204
- subject.to_ary.should == :an_array
177
+ decorator.decorated_collection.should_receive(:[]).with(42).and_return(:delegated)
178
+ expect(decorator[42]).to be :delegated
205
179
  end
206
- end
207
180
 
208
- it "delegates array methods to the decorated collection" do
209
- subject.decorated_collection.should_receive(:[]).with(42).and_return(:the_answer)
210
- subject[42].should == :the_answer
211
- end
181
+ describe "#==" do
182
+ context "when comparing to a collection decorator with the same source" do
183
+ it "returns true" do
184
+ source = [Product.new, Product.new]
185
+ decorator = CollectionDecorator.new(source)
186
+ other = ProductsDecorator.new(source)
212
187
 
213
- describe "#==" do
214
- context "when comparing to a collection decorator with the same source" do
215
- it "returns true" do
216
- a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
217
- b = ProductsDecorator.new(source)
218
- a.should == b
188
+ expect(decorator == other).to be_true
189
+ end
219
190
  end
220
- end
221
191
 
222
- context "when comparing to a collection decorator with a different source" do
223
- it "returns false" do
224
- a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
225
- b = ProductsDecorator.new([Product.new])
226
- a.should_not == b
192
+ context "when comparing to a collection decorator with a different source" do
193
+ it "returns false" do
194
+ decorator = CollectionDecorator.new([Product.new, Product.new])
195
+ other = ProductsDecorator.new([Product.new, Product.new])
196
+
197
+ expect(decorator == other).to be_false
198
+ end
227
199
  end
228
- end
229
200
 
230
- context "when comparing to a collection of the same items" do
231
- it "returns true" do
232
- a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
233
- b = source.dup
234
- a.should == b
201
+ context "when comparing to a collection of the same items" do
202
+ it "returns true" do
203
+ source = [Product.new, Product.new]
204
+ decorator = CollectionDecorator.new(source)
205
+ other = source.dup
206
+
207
+ expect(decorator == other).to be_true
208
+ end
235
209
  end
236
- end
237
210
 
238
- context "when comparing to a collection of different items" do
239
- it "returns true" do
240
- a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
241
- b = [Product.new]
242
- a.should_not == b
211
+ context "when comparing to a collection of different items" do
212
+ it "returns false" do
213
+ decorator = CollectionDecorator.new([Product.new, Product.new])
214
+ other = [Product.new, Product.new]
215
+
216
+ expect(decorator == other).to be_false
217
+ end
243
218
  end
244
- end
245
219
 
246
- context "when the decorated collection has been modified" do
247
- it "is no longer equal to the source" do
248
- a = Draper::CollectionDecorator.new(source, with: ProductDecorator)
249
- b = source.dup
220
+ context "when the decorated collection has been modified" do
221
+ it "is no longer equal to the source" do
222
+ source = [Product.new, Product.new]
223
+ decorator = CollectionDecorator.new(source)
224
+ other = source.dup
250
225
 
251
- a << Product.new.decorate
252
- a.should_not == b
226
+ decorator << Product.new.decorate
227
+ expect(decorator == other).to be_false
228
+ end
253
229
  end
254
230
  end
255
- end
256
-
257
- describe "#to_s" do
258
- subject { Draper::CollectionDecorator.new(source, options) }
259
- let(:source) { ["a", "b", "c"] }
260
231
 
261
- context "when :with option was given" do
262
- let(:options) { {with: ProductDecorator} }
232
+ describe "#to_s" do
233
+ context "when :with option was given" do
234
+ it "returns a string representation of the collection decorator" do
235
+ decorator = CollectionDecorator.new(["a", "b", "c"], with: ProductDecorator)
263
236
 
264
- it "returns a string representation of the CollectionDecorator" do
265
- subject.to_s.should == '#<Draper::CollectionDecorator of ProductDecorator for ["a", "b", "c"]>'
237
+ expect(decorator.to_s).to eq '#<Draper::CollectionDecorator of ProductDecorator for ["a", "b", "c"]>'
238
+ end
266
239
  end
267
- end
268
240
 
269
- context "when :with option was not given" do
270
- let(:options) { {} }
241
+ context "when :with option was not given" do
242
+ it "returns a string representation of the collection decorator" do
243
+ decorator = CollectionDecorator.new(["a", "b", "c"])
271
244
 
272
- it "returns a string representation of the CollectionDecorator" do
273
- subject.to_s.should == '#<Draper::CollectionDecorator of inferred decorators for ["a", "b", "c"]>'
245
+ expect(decorator.to_s).to eq '#<Draper::CollectionDecorator of inferred decorators for ["a", "b", "c"]>'
246
+ end
274
247
  end
275
- end
276
248
 
277
- context "for a custom subclass" do
278
- subject { ProductsDecorator.new(source) }
249
+ context "for a custom subclass" do
250
+ it "uses the custom class name" do
251
+ decorator = ProductsDecorator.new([])
279
252
 
280
- it "uses the custom class name" do
281
- subject.to_s.should =~ /ProductsDecorator/
253
+ expect(decorator.to_s).to match /ProductsDecorator/
254
+ end
282
255
  end
283
256
  end
284
- end
285
257
 
258
+ end
286
259
  end