draper 1.0.0 → 1.1.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/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
|