draper 0.18.0 → 1.0.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/.gitignore +3 -0
- data/.travis.yml +11 -0
- data/.yardopts +1 -1
- data/CHANGELOG.md +161 -0
- data/CONTRIBUTING.md +11 -0
- data/Gemfile +20 -0
- data/LICENSE +7 -0
- data/README.md +324 -0
- data/Rakefile +52 -31
- data/draper.gemspec +11 -9
- data/lib/draper/automatic_delegation.rb +50 -0
- data/lib/draper/collection_decorator.rb +107 -0
- data/lib/draper/decoratable.rb +83 -0
- data/lib/draper/decorated_association.rb +74 -0
- data/lib/draper/decorator.rb +238 -0
- data/lib/draper/delegation.rb +13 -0
- data/lib/draper/finders.rb +37 -0
- data/lib/draper/helper_proxy.rb +17 -0
- data/lib/draper/lazy_helpers.rb +10 -6
- data/lib/draper/railtie.rb +27 -21
- data/lib/draper/tasks/test.rake +22 -0
- data/lib/draper/test/devise_helper.rb +34 -0
- data/lib/draper/test/minitest_integration.rb +2 -3
- data/lib/draper/test/rspec_integration.rb +3 -13
- data/lib/draper/test_case.rb +33 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +12 -17
- data/lib/draper/view_helpers.rb +37 -0
- data/lib/draper.rb +47 -8
- data/lib/generators/decorator/decorator_generator.rb +10 -2
- data/lib/generators/decorator/templates/decorator.rb +12 -27
- data/lib/generators/mini_test/decorator_generator.rb +20 -0
- data/lib/generators/mini_test/templates/decorator_spec.rb +4 -0
- data/lib/generators/mini_test/templates/decorator_test.rb +4 -0
- data/lib/generators/test_unit/templates/decorator_test.rb +1 -1
- data/spec/draper/collection_decorator_spec.rb +286 -0
- data/spec/draper/decoratable_spec.rb +192 -0
- data/spec/draper/decorated_association_spec.rb +142 -0
- data/spec/draper/decorator_spec.rb +624 -0
- data/spec/draper/finders_spec.rb +132 -0
- data/spec/draper/helper_proxy_spec.rb +12 -0
- data/spec/draper/view_helpers_spec.rb +41 -0
- data/spec/dummy/.rspec +2 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/localized_urls.rb +5 -0
- data/spec/dummy/app/controllers/posts_controller.rb +11 -0
- data/spec/dummy/app/decorators/post_decorator.rb +32 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +3 -0
- data/spec/dummy/app/mailers/post_mailer.rb +9 -0
- data/spec/dummy/app/models/post.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +11 -0
- data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
- data/spec/dummy/app/views/posts/_post.html.erb +19 -0
- data/spec/dummy/app/views/posts/show.html.erb +1 -0
- data/spec/dummy/config/application.rb +64 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +34 -0
- data/spec/dummy/config/environments/production.rb +55 -0
- data/spec/dummy/config/environments/test.rb +32 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +7 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
- data/spec/dummy/db/schema.rb +21 -0
- data/spec/dummy/db/seeds.rb +2 -0
- data/spec/dummy/lib/tasks/test.rake +10 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/mini_test/mini_test_integration_test.rb +46 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +26 -0
- data/spec/dummy/spec/decorators/rspec_integration_spec.rb +19 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
- data/spec/dummy/spec/models/post_spec.rb +14 -0
- data/spec/dummy/spec/spec_helper.rb +8 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +47 -7
- data/spec/integration/integration_spec.rb +33 -0
- data/{performance → spec/performance}/decorators.rb +2 -4
- data/spec/spec_helper.rb +20 -45
- data/spec/support/action_controller.rb +12 -0
- data/spec/support/active_model.rb +7 -0
- data/spec/support/active_record.rb +9 -0
- data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
- data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
- data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
- data/spec/support/decorators/product_decorator.rb +23 -0
- data/spec/support/{samples → decorators}/products_decorator.rb +1 -1
- data/spec/support/decorators/some_thing_decorator.rb +2 -0
- data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
- data/spec/support/dummy_app.rb +85 -0
- data/spec/support/matchers/have_text.rb +50 -0
- data/spec/support/{samples → models}/namespaced_product.rb +1 -3
- data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
- data/spec/support/{samples → models}/product.rb +17 -2
- data/spec/support/models/some_thing.rb +5 -0
- data/spec/support/models/uninferrable_decorator_model.rb +3 -0
- metadata +241 -87
- data/CHANGELOG.markdown +0 -57
- data/Readme.markdown +0 -387
- data/lib/draper/active_model_support.rb +0 -27
- data/lib/draper/base.rb +0 -312
- data/lib/draper/decorated_enumerable_proxy.rb +0 -90
- data/lib/draper/model_support.rb +0 -25
- data/lib/draper/rspec_integration.rb +0 -2
- data/lib/draper/system.rb +0 -18
- data/spec/draper/base_spec.rb +0 -873
- data/spec/draper/decorated_enumerable_proxy_spec.rb +0 -45
- data/spec/draper/model_support_spec.rb +0 -48
- data/spec/minitest-rails/spec_type_spec.rb +0 -63
- data/spec/support/samples/active_record.rb +0 -17
- data/spec/support/samples/decorator.rb +0 -5
- data/spec/support/samples/decorator_with_allows.rb +0 -3
- data/spec/support/samples/decorator_with_denies.rb +0 -3
- data/spec/support/samples/decorator_with_denies_all.rb +0 -3
- data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
- data/spec/support/samples/decorator_with_special_methods.rb +0 -13
- data/spec/support/samples/enumerable_proxy.rb +0 -3
- data/spec/support/samples/namespaced_product_decorator.rb +0 -7
- data/spec/support/samples/product_decorator.rb +0 -7
- data/spec/support/samples/sequel_product.rb +0 -13
- data/spec/support/samples/some_thing.rb +0 -2
- data/spec/support/samples/some_thing_decorator.rb +0 -3
- /data/{performance → spec/performance}/active_record.rb +0 -0
- /data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
- /data/{performance → spec/performance}/models.rb +0 -0
- /data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
- /data/spec/support/{samples → models}/widget.rb +0 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Draper::Decorator do
|
|
4
|
+
before { ApplicationController.new.view_context }
|
|
5
|
+
subject { decorator_class.new(source) }
|
|
6
|
+
let(:decorator_class) { Draper::Decorator }
|
|
7
|
+
let(:source) { Product.new }
|
|
8
|
+
|
|
9
|
+
describe "#initialize" do
|
|
10
|
+
describe "options validation" do
|
|
11
|
+
let(:valid_options) { {context: {}} }
|
|
12
|
+
|
|
13
|
+
it "does not raise error on valid options" do
|
|
14
|
+
expect { decorator_class.new(source, valid_options) }.to_not raise_error
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "raises error on invalid options" do
|
|
18
|
+
expect { decorator_class.new(source, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError, /Unknown key/)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "sets the source" do
|
|
23
|
+
subject.source.should be source
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "stores context" do
|
|
27
|
+
decorator = decorator_class.new(source, context: {some: 'context'})
|
|
28
|
+
decorator.context.should == {some: 'context'}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context "when decorating an instance of itself" do
|
|
32
|
+
it "does not redecorate" do
|
|
33
|
+
decorator = ProductDecorator.new(source)
|
|
34
|
+
ProductDecorator.new(decorator).source.should be source
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context "when options are supplied" do
|
|
38
|
+
it "overwrites existing context" do
|
|
39
|
+
decorator = ProductDecorator.new(source, context: {role: :admin})
|
|
40
|
+
ProductDecorator.new(decorator, context: {role: :user}).context.should == {role: :user}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context "when no options are supplied" do
|
|
45
|
+
it "preserves existing context" do
|
|
46
|
+
decorator = ProductDecorator.new(source, context: {role: :admin})
|
|
47
|
+
ProductDecorator.new(decorator).context.should == {role: :admin}
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context "when decorating other decorators" do
|
|
53
|
+
it "redecorates" do
|
|
54
|
+
decorator = ProductDecorator.new(source)
|
|
55
|
+
SpecificProductDecorator.new(decorator).source.should be decorator
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context "when the same decorator has been applied earlier in the chain" do
|
|
59
|
+
let(:decorator) { SpecificProductDecorator.new(ProductDecorator.new(Product.new)) }
|
|
60
|
+
|
|
61
|
+
it "warns" do
|
|
62
|
+
warning_message = nil
|
|
63
|
+
Object.any_instance.stub(:warn) {|message| warning_message = message }
|
|
64
|
+
|
|
65
|
+
expect{ProductDecorator.new(decorator)}.to change{warning_message}
|
|
66
|
+
warning_message.should =~ /ProductDecorator/
|
|
67
|
+
warning_message.should include caller(1).first
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "redecorates" do
|
|
71
|
+
Object.any_instance.stub(:warn)
|
|
72
|
+
ProductDecorator.new(decorator).source.should be decorator
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe "#context=" do
|
|
79
|
+
it "modifies the context" do
|
|
80
|
+
decorator = decorator_class.new(source, context: {some: 'context'})
|
|
81
|
+
decorator.context = {some: 'other_context'}
|
|
82
|
+
decorator.context.should == {some: 'other_context'}
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe ".decorate_collection" do
|
|
87
|
+
let(:source) { [Product.new, Widget.new] }
|
|
88
|
+
|
|
89
|
+
describe "options validation" do
|
|
90
|
+
let(:valid_options) { {with: :infer, context: {}} }
|
|
91
|
+
before(:each) { Draper::CollectionDecorator.stub(:new) }
|
|
92
|
+
|
|
93
|
+
it "does not raise error on valid options" do
|
|
94
|
+
expect { ProductDecorator.decorate_collection(source, valid_options) }.to_not raise_error
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "raises error on invalid options" do
|
|
98
|
+
expect { ProductDecorator.decorate_collection(source, valid_options.merge(foo: 'bar')) }.to raise_error(ArgumentError, /Unknown key/)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
context "when a custom collection decorator does not exist" do
|
|
103
|
+
subject { WidgetDecorator.decorate_collection(source) }
|
|
104
|
+
|
|
105
|
+
it "returns a regular collection decorator" do
|
|
106
|
+
subject.should be_a Draper::CollectionDecorator
|
|
107
|
+
subject.should == source
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "uses itself as the item decorator by default" do
|
|
111
|
+
subject.each {|item| item.should be_a WidgetDecorator}
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
context "when a custom collection decorator exists" do
|
|
116
|
+
subject { ProductDecorator.decorate_collection(source) }
|
|
117
|
+
|
|
118
|
+
it "returns the custom collection decorator" do
|
|
119
|
+
subject.should be_a ProductsDecorator
|
|
120
|
+
subject.should == source
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "uses itself as the item decorator by default" do
|
|
124
|
+
subject.each {|item| item.should be_a ProductDecorator}
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context "with context" do
|
|
129
|
+
subject { ProductDecorator.decorate_collection(source, with: :infer, context: {some: 'context'}) }
|
|
130
|
+
|
|
131
|
+
it "passes the context to the collection decorator" do
|
|
132
|
+
subject.context.should == {some: 'context'}
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe "#helpers" do
|
|
138
|
+
it "returns a HelperProxy" do
|
|
139
|
+
subject.helpers.should be_a Draper::HelperProxy
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "is aliased to #h" do
|
|
143
|
+
subject.h.should be subject.helpers
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "initializes the wrapper only once" do
|
|
147
|
+
helper_proxy = subject.helpers
|
|
148
|
+
helper_proxy.stub(:test_method) { "test_method" }
|
|
149
|
+
subject.helpers.test_method.should == "test_method"
|
|
150
|
+
subject.helpers.test_method.should == "test_method"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe "#localize" do
|
|
155
|
+
before { subject.helpers.should_receive(:localize).with(:an_object, {some: 'parameter'}) }
|
|
156
|
+
|
|
157
|
+
it "delegates to #helpers" do
|
|
158
|
+
subject.localize(:an_object, some: 'parameter')
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "is aliased to #l" do
|
|
162
|
+
subject.l(:an_object, some: 'parameter')
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
describe ".helpers" do
|
|
167
|
+
it "returns a HelperProxy" do
|
|
168
|
+
subject.class.helpers.should be_a Draper::HelperProxy
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "is aliased to .h" do
|
|
172
|
+
subject.class.h.should be subject.class.helpers
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
describe ".decorates" do
|
|
177
|
+
subject { Class.new(Draper::Decorator) }
|
|
178
|
+
|
|
179
|
+
context "with a symbol" do
|
|
180
|
+
it "sets .source_class" do
|
|
181
|
+
subject.decorates :product
|
|
182
|
+
subject.source_class.should be Product
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
context "with a string" do
|
|
187
|
+
it "sets .source_class" do
|
|
188
|
+
subject.decorates "product"
|
|
189
|
+
subject.source_class.should be Product
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
context "with a class" do
|
|
194
|
+
it "sets .source_class" do
|
|
195
|
+
subject.decorates Product
|
|
196
|
+
subject.source_class.should be Product
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
describe ".source_class" do
|
|
202
|
+
context "when not set by .decorates" do
|
|
203
|
+
context "for an anonymous decorator" do
|
|
204
|
+
subject { Class.new(Draper::Decorator) }
|
|
205
|
+
|
|
206
|
+
it "raises an UninferrableSourceError" do
|
|
207
|
+
expect{subject.source_class}.to raise_error Draper::UninferrableSourceError
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
context "for a decorator without a corresponding source" do
|
|
212
|
+
subject { SpecificProductDecorator }
|
|
213
|
+
|
|
214
|
+
it "raises an UninferrableSourceError" do
|
|
215
|
+
expect{subject.source_class}.to raise_error Draper::UninferrableSourceError
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
context "for a decorator called Decorator" do
|
|
220
|
+
subject { Draper::Decorator }
|
|
221
|
+
|
|
222
|
+
it "raises an UninferrableSourceError" do
|
|
223
|
+
expect{subject.source_class}.to raise_error Draper::UninferrableSourceError
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
context "for a decorator with a name not ending in Decorator" do
|
|
228
|
+
subject { DecoratorWithApplicationHelper }
|
|
229
|
+
|
|
230
|
+
it "raises an UninferrableSourceError" do
|
|
231
|
+
expect{subject.source_class}.to raise_error Draper::UninferrableSourceError
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
context "for an inferrable source" do
|
|
236
|
+
subject { ProductDecorator }
|
|
237
|
+
|
|
238
|
+
it "infers the source" do
|
|
239
|
+
subject.source_class.should be Product
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
context "for a namespaced inferrable source" do
|
|
244
|
+
subject { Namespace::ProductDecorator }
|
|
245
|
+
|
|
246
|
+
it "infers the namespaced source" do
|
|
247
|
+
subject.source_class.should be Namespace::Product
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
describe ".source_class?" do
|
|
254
|
+
subject { Class.new(Draper::Decorator) }
|
|
255
|
+
|
|
256
|
+
it "returns truthy when .source_class is set" do
|
|
257
|
+
subject.stub(:source_class).and_return(Product)
|
|
258
|
+
subject.source_class?.should be_true
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "returns false when .source_class is not inferrable" do
|
|
262
|
+
subject.stub(:source_class).and_raise(Draper::UninferrableSourceError.new(subject))
|
|
263
|
+
subject.source_class?.should be_false
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
describe ".decorates_association" do
|
|
268
|
+
let(:decorator_class) { Class.new(ProductDecorator) }
|
|
269
|
+
before { decorator_class.decorates_association :similar_products, with: ProductDecorator }
|
|
270
|
+
|
|
271
|
+
describe "overridden association method" do
|
|
272
|
+
let(:decorated_association) { ->{} }
|
|
273
|
+
|
|
274
|
+
describe "options validation" do
|
|
275
|
+
let(:valid_options) { {with: ProductDecorator, scope: :foo, context: {}} }
|
|
276
|
+
before(:each) { Draper::DecoratedAssociation.stub(:new).and_return(decorated_association) }
|
|
277
|
+
|
|
278
|
+
it "does not raise error on valid options" do
|
|
279
|
+
expect { decorator_class.decorates_association :similar_products, valid_options }.to_not raise_error
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it "raises error on invalid options" do
|
|
283
|
+
expect { decorator_class.decorates_association :similar_products, valid_options.merge(foo: 'bar') }.to raise_error(ArgumentError, /Unknown key/)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it "creates a DecoratedAssociation" do
|
|
288
|
+
Draper::DecoratedAssociation.should_receive(:new).with(subject, :similar_products, {with: ProductDecorator}).and_return(decorated_association)
|
|
289
|
+
subject.similar_products
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
it "receives the Decorator" do
|
|
293
|
+
Draper::DecoratedAssociation.should_receive(:new).with(kind_of(decorator_class), :similar_products, {with: ProductDecorator}).and_return(decorated_association)
|
|
294
|
+
subject.similar_products
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "memoizes the DecoratedAssociation" do
|
|
298
|
+
Draper::DecoratedAssociation.should_receive(:new).once.and_return(decorated_association)
|
|
299
|
+
subject.similar_products
|
|
300
|
+
subject.similar_products
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it "calls the DecoratedAssociation" do
|
|
304
|
+
Draper::DecoratedAssociation.stub(:new).and_return(decorated_association)
|
|
305
|
+
decorated_association.should_receive(:call).and_return(:decorated)
|
|
306
|
+
subject.similar_products.should be :decorated
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
describe ".decorates_associations" do
|
|
312
|
+
subject { decorator_class }
|
|
313
|
+
|
|
314
|
+
it "decorates each of the associations" do
|
|
315
|
+
subject.should_receive(:decorates_association).with(:similar_products, {})
|
|
316
|
+
subject.should_receive(:decorates_association).with(:previous_version, {})
|
|
317
|
+
|
|
318
|
+
subject.decorates_associations :similar_products, :previous_version
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it "dispatches options" do
|
|
322
|
+
subject.should_receive(:decorates_association).with(:similar_products, {with: ProductDecorator})
|
|
323
|
+
subject.should_receive(:decorates_association).with(:previous_version, {with: ProductDecorator})
|
|
324
|
+
|
|
325
|
+
subject.decorates_associations :similar_products, :previous_version, with: ProductDecorator
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
describe "#applied_decorators" do
|
|
330
|
+
it "returns a list of decorators applied to a model" do
|
|
331
|
+
decorator = ProductDecorator.new(SpecificProductDecorator.new(Product.new))
|
|
332
|
+
decorator.applied_decorators.should == [SpecificProductDecorator, ProductDecorator]
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
describe "#decorated_with?" do
|
|
337
|
+
it "checks if a decorator has been applied to a model" do
|
|
338
|
+
decorator = ProductDecorator.new(SpecificProductDecorator.new(Product.new))
|
|
339
|
+
decorator.should be_decorated_with ProductDecorator
|
|
340
|
+
decorator.should be_decorated_with SpecificProductDecorator
|
|
341
|
+
decorator.should_not be_decorated_with WidgetDecorator
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
describe "#decorated?" do
|
|
346
|
+
it "returns true" do
|
|
347
|
+
subject.should be_decorated
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
describe "#source" do
|
|
352
|
+
it "returns the wrapped object" do
|
|
353
|
+
subject.source.should be source
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
it "is aliased to #to_source" do
|
|
357
|
+
subject.to_source.should be source
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
it "is aliased to #model" do
|
|
361
|
+
subject.model.should be source
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
describe "#to_model" do
|
|
366
|
+
it "returns the decorator" do
|
|
367
|
+
subject.to_model.should be subject
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
describe "#to_param" do
|
|
372
|
+
it "proxies to the source" do
|
|
373
|
+
source.stub(:to_param).and_return(42)
|
|
374
|
+
subject.to_param.should == 42
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
describe "#==" do
|
|
379
|
+
context "with itself" do
|
|
380
|
+
it "returns true" do
|
|
381
|
+
(subject == subject).should be_true
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
context "with another decorator having the same source" do
|
|
386
|
+
it "returns true" do
|
|
387
|
+
(subject == ProductDecorator.new(source)).should be_true
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
context "with another decorator having a different source" do
|
|
392
|
+
it "returns false" do
|
|
393
|
+
(subject == ProductDecorator.new(Object.new)).should be_false
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
context "with the source object" do
|
|
398
|
+
it "returns true" do
|
|
399
|
+
(subject == source).should be_true
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
context "with another object" do
|
|
404
|
+
it "returns false" do
|
|
405
|
+
(subject == Object.new).should be_false
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
describe "#===" do
|
|
411
|
+
context "with itself" do
|
|
412
|
+
it "returns true" do
|
|
413
|
+
(subject === subject).should be_true
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
context "with another decorator having the same source" do
|
|
418
|
+
it "returns true" do
|
|
419
|
+
(subject === ProductDecorator.new(source)).should be_true
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
context "with another decorator having a different source" do
|
|
424
|
+
it "returns false" do
|
|
425
|
+
(subject === ProductDecorator.new(Object.new)).should be_false
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
context "with the source object" do
|
|
430
|
+
it "returns true" do
|
|
431
|
+
(subject === source).should be_true
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
context "with another object" do
|
|
436
|
+
it "returns false" do
|
|
437
|
+
(subject === Object.new).should be_false
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
describe ".delegate" do
|
|
443
|
+
subject { Class.new(Draper::Decorator) }
|
|
444
|
+
|
|
445
|
+
it "defaults the :to option to :source" do
|
|
446
|
+
Draper::Decorator.superclass.should_receive(:delegate).with(:foo, :bar, to: :source)
|
|
447
|
+
subject.delegate :foo, :bar
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
it "does not overwrite the :to option if supplied" do
|
|
451
|
+
Draper::Decorator.superclass.should_receive(:delegate).with(:foo, :bar, to: :baz)
|
|
452
|
+
subject.delegate :foo, :bar, to: :baz
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
describe ".delegate_all" do
|
|
457
|
+
let(:decorator_class) { Class.new(ProductDecorator) }
|
|
458
|
+
before { decorator_class.delegate_all }
|
|
459
|
+
|
|
460
|
+
describe "#method_missing" do
|
|
461
|
+
it "does not delegate methods that are defined on the decorator" do
|
|
462
|
+
subject.overridable.should be :overridden
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
it "does not delegate methods inherited from Object" do
|
|
466
|
+
subject.inspect.should_not be source.inspect
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
it "delegates missing methods that exist on the source" do
|
|
470
|
+
source.stub(:hello_world).and_return(:delegated)
|
|
471
|
+
subject.hello_world.should be :delegated
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
it "adds delegated methods to the decorator when they are used" do
|
|
475
|
+
subject.methods.should_not include :hello_world
|
|
476
|
+
subject.hello_world
|
|
477
|
+
subject.methods.should include :hello_world
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
it "passes blocks to delegated methods" do
|
|
481
|
+
subject.block{"marker"}.should == "marker"
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
it "does not confuse Kernel#Array" do
|
|
485
|
+
Array(subject).should be_a Array
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
it "delegates already-delegated methods" do
|
|
489
|
+
subject.delegated_method.should == "Yay, delegation"
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
it "does not delegate private methods" do
|
|
493
|
+
expect{subject.private_title}.to raise_error NoMethodError
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
context ".method_missing" do
|
|
498
|
+
subject { decorator_class }
|
|
499
|
+
|
|
500
|
+
context "without a source class" do
|
|
501
|
+
it "raises a NoMethodError on missing methods" do
|
|
502
|
+
expect{subject.hello_world}.to raise_error NoMethodError
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
context "with a source class" do
|
|
507
|
+
let(:source_class) { Product }
|
|
508
|
+
before { subject.decorates source_class }
|
|
509
|
+
|
|
510
|
+
it "does not delegate methods that are defined on the decorator" do
|
|
511
|
+
subject.overridable.should be :overridden
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
it "delegates missing methods that exist on the source" do
|
|
515
|
+
source_class.stub(:hello_world).and_return(:delegated)
|
|
516
|
+
subject.hello_world.should be :delegated
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
describe "#respond_to?" do
|
|
522
|
+
it "returns true for its own methods" do
|
|
523
|
+
subject.should respond_to :awesome_title
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
it "returns true for the source's methods" do
|
|
527
|
+
subject.should respond_to :title
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
context "with include_private" do
|
|
531
|
+
it "returns true for its own private methods" do
|
|
532
|
+
subject.respond_to?(:awesome_private_title, true).should be_true
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
it "returns false for the source's private methods" do
|
|
536
|
+
subject.respond_to?(:private_title, true).should be_false
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
describe ".respond_to?" do
|
|
542
|
+
subject { decorator_class }
|
|
543
|
+
|
|
544
|
+
context "without a source class" do
|
|
545
|
+
it "returns true for its own class methods" do
|
|
546
|
+
subject.should respond_to :my_class_method
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
it "returns false for other class methods" do
|
|
550
|
+
subject.should_not respond_to :sample_class_method
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
context "with a source_class" do
|
|
555
|
+
before { subject.decorates :product }
|
|
556
|
+
|
|
557
|
+
it "returns true for its own class methods" do
|
|
558
|
+
subject.should respond_to :my_class_method
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
it "returns true for the source's class methods" do
|
|
562
|
+
subject.should respond_to :sample_class_method
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
context "in a Rails application" do
|
|
569
|
+
let(:decorator_class) { DecoratorWithApplicationHelper }
|
|
570
|
+
|
|
571
|
+
it "has access to ApplicationHelper helpers" do
|
|
572
|
+
subject.uses_hello_world.should == "Hello, World!"
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
it "is able to use the content_tag helper" do
|
|
576
|
+
subject.sample_content.to_s.should == "<span>Hello, World!</span>"
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
it "is able to use the link_to helper" do
|
|
580
|
+
subject.sample_link.should == %{<a href="/World">Hello</a>}
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
it "is able to use the truncate helper" do
|
|
584
|
+
subject.sample_truncate.should == "Once..."
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
it "is able to access html_escape, a private method" do
|
|
588
|
+
subject.sample_html_escaped_text.should == '<script>danger</script>'
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
it "pretends to be the source class" do
|
|
593
|
+
subject.kind_of?(source.class).should be_true
|
|
594
|
+
subject.is_a?(source.class).should be_true
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
it "is still its own class" do
|
|
598
|
+
subject.kind_of?(subject.class).should be_true
|
|
599
|
+
subject.is_a?(subject.class).should be_true
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
it "pretends to be an instance of the source class" do
|
|
603
|
+
subject.instance_of?(source.class).should be_true
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
it "is still an instance of its own class" do
|
|
607
|
+
subject.instance_of?(subject.class).should be_true
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
describe ".decorates_finders" do
|
|
611
|
+
it "extends the Finders module" do
|
|
612
|
+
ProductDecorator.should be_a_kind_of Draper::Finders
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
describe "#serializable_hash" do
|
|
617
|
+
let(:decorator_class) { ProductDecorator }
|
|
618
|
+
|
|
619
|
+
it "serializes overridden attributes" do
|
|
620
|
+
subject.serializable_hash[:overridable].should be :overridden
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
end
|