draper 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +3 -0
- data/CONTRIBUTING.md +5 -1
- data/Gemfile +23 -9
- data/README.md +144 -52
- data/Rakefile +1 -0
- data/draper.gemspec +1 -1
- data/lib/draper.rb +9 -6
- data/lib/draper/decoratable.rb +3 -7
- data/lib/draper/decoratable/equality.rb +14 -0
- data/lib/draper/decorator.rb +4 -7
- data/lib/draper/helper_proxy.rb +22 -3
- data/lib/draper/test/devise_helper.rb +18 -22
- data/lib/draper/test/rspec_integration.rb +4 -0
- data/lib/draper/test_case.rb +20 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +75 -13
- data/lib/draper/view_context/build_strategy.rb +48 -0
- data/lib/draper/view_helpers.rb +2 -2
- data/spec/draper/collection_decorator_spec.rb +169 -196
- data/spec/draper/decoratable/equality_spec.rb +10 -0
- data/spec/draper/decoratable_spec.rb +107 -132
- data/spec/draper/decorated_association_spec.rb +99 -96
- data/spec/draper/decorator_spec.rb +408 -434
- data/spec/draper/finders_spec.rb +160 -126
- data/spec/draper/helper_proxy_spec.rb +38 -8
- data/spec/draper/view_context/build_strategy_spec.rb +116 -0
- data/spec/draper/view_context_spec.rb +154 -0
- data/spec/draper/view_helpers_spec.rb +4 -37
- data/spec/dummy/app/controllers/posts_controller.rb +7 -0
- data/spec/dummy/app/decorators/post_decorator.rb +26 -2
- data/spec/dummy/app/helpers/application_helper.rb +3 -0
- data/spec/dummy/app/mailers/post_mailer.rb +10 -0
- data/spec/dummy/app/models/admin.rb +5 -0
- data/spec/dummy/app/models/mongoid_post.rb +5 -0
- data/spec/dummy/app/models/user.rb +5 -0
- data/spec/dummy/app/views/posts/_post.html.erb +15 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/config/application.rb +9 -3
- data/spec/dummy/config/boot.rb +2 -7
- data/spec/dummy/config/environments/development.rb +2 -3
- data/spec/dummy/config/environments/production.rb +2 -0
- data/spec/dummy/config/environments/test.rb +3 -4
- data/spec/dummy/config/initializers/secret_token.rb +1 -0
- data/spec/dummy/config/mongoid.yml +80 -0
- data/spec/dummy/config/routes.rb +2 -0
- data/spec/dummy/fast_spec/post_decorator_spec.rb +38 -0
- data/spec/dummy/lib/tasks/test.rake +11 -5
- data/spec/dummy/spec/decorators/devise_spec.rb +64 -0
- data/spec/dummy/spec/decorators/helpers_spec.rb +21 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +26 -6
- data/spec/dummy/spec/decorators/spec_type_spec.rb +7 -0
- data/spec/dummy/spec/decorators/view_context_spec.rb +22 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +10 -6
- data/spec/dummy/spec/models/mongoid_post_spec.rb +10 -0
- data/spec/dummy/spec/models/post_spec.rb +5 -5
- data/spec/dummy/spec/spec_helper.rb +1 -0
- data/spec/dummy/test/decorators/minitest/devise_test.rb +64 -0
- data/spec/dummy/test/decorators/minitest/helpers_test.rb +21 -0
- data/spec/dummy/{mini_test/mini_test_integration_test.rb → test/decorators/minitest/spec_type_test.rb} +9 -3
- data/spec/dummy/test/decorators/minitest/view_context_test.rb +24 -0
- data/spec/dummy/test/decorators/test_unit/devise_test.rb +64 -0
- data/spec/dummy/test/decorators/test_unit/helpers_test.rb +21 -0
- data/spec/dummy/test/decorators/test_unit/view_context_test.rb +24 -0
- data/spec/dummy/test/minitest_helper.rb +4 -0
- data/spec/dummy/test/test_helper.rb +3 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +1 -0
- data/spec/integration/integration_spec.rb +31 -6
- data/spec/spec_helper.rb +32 -25
- data/spec/support/shared_examples/decoratable_equality.rb +40 -0
- data/spec/support/shared_examples/view_helpers.rb +39 -0
- metadata +56 -44
- data/spec/dummy/README.rdoc +0 -261
- data/spec/dummy/spec/decorators/rspec_integration_spec.rb +0 -19
- data/spec/support/action_controller.rb +0 -12
- data/spec/support/active_model.rb +0 -7
- data/spec/support/active_record.rb +0 -9
- data/spec/support/decorators/decorator_with_application_helper.rb +0 -25
- data/spec/support/decorators/namespaced_product_decorator.rb +0 -5
- data/spec/support/decorators/non_active_model_product_decorator.rb +0 -2
- data/spec/support/decorators/product_decorator.rb +0 -23
- data/spec/support/decorators/products_decorator.rb +0 -10
- data/spec/support/decorators/some_thing_decorator.rb +0 -2
- data/spec/support/decorators/specific_product_decorator.rb +0 -2
- data/spec/support/decorators/widget_decorator.rb +0 -2
- data/spec/support/models/namespaced_product.rb +0 -49
- data/spec/support/models/non_active_model_product.rb +0 -3
- data/spec/support/models/product.rb +0 -95
- data/spec/support/models/some_thing.rb +0 -5
- data/spec/support/models/uninferrable_decorator_model.rb +0 -3
- 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
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
10
|
-
|
11
|
-
item.should be_decorated_with ProductDecorator
|
12
|
-
end
|
13
|
-
end
|
8
|
+
describe "#initialize" do
|
9
|
+
describe "options validation" do
|
14
10
|
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
40
|
-
|
41
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
57
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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 "
|
93
|
-
|
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
|
-
|
96
|
-
|
97
|
+
expect(*decorator).to be_decorated_with OtherDecorator
|
98
|
+
end
|
97
99
|
end
|
98
|
-
end
|
99
100
|
|
100
|
-
|
101
|
-
|
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
|
-
|
104
|
-
|
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 "
|
113
|
-
|
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
|
-
|
116
|
-
|
115
|
+
expect(*decorator).to be_decorated_with OtherDecorator
|
116
|
+
end
|
117
117
|
end
|
118
|
-
end
|
119
118
|
|
120
|
-
|
121
|
-
|
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
|
-
|
124
|
-
|
123
|
+
expect(*decorator).to be :inferred_decorator
|
124
|
+
end
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
128
|
-
end
|
129
128
|
|
130
|
-
|
131
|
-
|
129
|
+
describe ".delegate" do
|
130
|
+
protect_class ProductsDecorator
|
132
131
|
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
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
|
-
|
179
|
-
|
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
|
-
|
182
|
-
|
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
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
191
|
-
|
192
|
-
|
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 "
|
196
|
-
|
197
|
-
end
|
198
|
-
end
|
174
|
+
it "delegates array methods to the decorated collection" do
|
175
|
+
decorator = CollectionDecorator.new([])
|
199
176
|
|
200
|
-
|
201
|
-
|
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
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
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
|
-
|
214
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
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
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
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
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
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
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
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
|
-
|
252
|
-
|
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
|
-
|
262
|
-
|
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
|
-
|
265
|
-
|
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
|
-
|
270
|
-
|
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
|
-
|
273
|
-
|
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
|
-
|
278
|
-
|
249
|
+
context "for a custom subclass" do
|
250
|
+
it "uses the custom class name" do
|
251
|
+
decorator = ProductsDecorator.new([])
|
279
252
|
|
280
|
-
|
281
|
-
|
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
|