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.
Files changed (140) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +11 -0
  3. data/.yardopts +1 -1
  4. data/CHANGELOG.md +161 -0
  5. data/CONTRIBUTING.md +11 -0
  6. data/Gemfile +20 -0
  7. data/LICENSE +7 -0
  8. data/README.md +324 -0
  9. data/Rakefile +52 -31
  10. data/draper.gemspec +11 -9
  11. data/lib/draper/automatic_delegation.rb +50 -0
  12. data/lib/draper/collection_decorator.rb +107 -0
  13. data/lib/draper/decoratable.rb +83 -0
  14. data/lib/draper/decorated_association.rb +74 -0
  15. data/lib/draper/decorator.rb +238 -0
  16. data/lib/draper/delegation.rb +13 -0
  17. data/lib/draper/finders.rb +37 -0
  18. data/lib/draper/helper_proxy.rb +17 -0
  19. data/lib/draper/lazy_helpers.rb +10 -6
  20. data/lib/draper/railtie.rb +27 -21
  21. data/lib/draper/tasks/test.rake +22 -0
  22. data/lib/draper/test/devise_helper.rb +34 -0
  23. data/lib/draper/test/minitest_integration.rb +2 -3
  24. data/lib/draper/test/rspec_integration.rb +3 -13
  25. data/lib/draper/test_case.rb +33 -0
  26. data/lib/draper/version.rb +1 -1
  27. data/lib/draper/view_context.rb +12 -17
  28. data/lib/draper/view_helpers.rb +37 -0
  29. data/lib/draper.rb +47 -8
  30. data/lib/generators/decorator/decorator_generator.rb +10 -2
  31. data/lib/generators/decorator/templates/decorator.rb +12 -27
  32. data/lib/generators/mini_test/decorator_generator.rb +20 -0
  33. data/lib/generators/mini_test/templates/decorator_spec.rb +4 -0
  34. data/lib/generators/mini_test/templates/decorator_test.rb +4 -0
  35. data/lib/generators/test_unit/templates/decorator_test.rb +1 -1
  36. data/spec/draper/collection_decorator_spec.rb +286 -0
  37. data/spec/draper/decoratable_spec.rb +192 -0
  38. data/spec/draper/decorated_association_spec.rb +142 -0
  39. data/spec/draper/decorator_spec.rb +624 -0
  40. data/spec/draper/finders_spec.rb +132 -0
  41. data/spec/draper/helper_proxy_spec.rb +12 -0
  42. data/spec/draper/view_helpers_spec.rb +41 -0
  43. data/spec/dummy/.rspec +2 -0
  44. data/spec/dummy/README.rdoc +261 -0
  45. data/spec/dummy/Rakefile +7 -0
  46. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  47. data/spec/dummy/app/controllers/localized_urls.rb +5 -0
  48. data/spec/dummy/app/controllers/posts_controller.rb +11 -0
  49. data/spec/dummy/app/decorators/post_decorator.rb +32 -0
  50. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  51. data/spec/dummy/app/mailers/application_mailer.rb +3 -0
  52. data/spec/dummy/app/mailers/post_mailer.rb +9 -0
  53. data/spec/dummy/app/models/post.rb +3 -0
  54. data/spec/dummy/app/views/layouts/application.html.erb +11 -0
  55. data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
  56. data/spec/dummy/app/views/posts/_post.html.erb +19 -0
  57. data/spec/dummy/app/views/posts/show.html.erb +1 -0
  58. data/spec/dummy/config/application.rb +64 -0
  59. data/spec/dummy/config/boot.rb +10 -0
  60. data/spec/dummy/config/database.yml +25 -0
  61. data/spec/dummy/config/environment.rb +5 -0
  62. data/spec/dummy/config/environments/development.rb +34 -0
  63. data/spec/dummy/config/environments/production.rb +55 -0
  64. data/spec/dummy/config/environments/test.rb +32 -0
  65. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  66. data/spec/dummy/config/initializers/inflections.rb +15 -0
  67. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  68. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  69. data/spec/dummy/config/initializers/session_store.rb +8 -0
  70. data/spec/dummy/config/locales/en.yml +5 -0
  71. data/spec/dummy/config/routes.rb +7 -0
  72. data/spec/dummy/config.ru +4 -0
  73. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
  74. data/spec/dummy/db/schema.rb +21 -0
  75. data/spec/dummy/db/seeds.rb +2 -0
  76. data/spec/dummy/lib/tasks/test.rake +10 -0
  77. data/spec/dummy/log/.gitkeep +0 -0
  78. data/spec/dummy/mini_test/mini_test_integration_test.rb +46 -0
  79. data/spec/dummy/public/404.html +26 -0
  80. data/spec/dummy/public/422.html +26 -0
  81. data/spec/dummy/public/500.html +25 -0
  82. data/spec/dummy/public/favicon.ico +0 -0
  83. data/spec/dummy/script/rails +6 -0
  84. data/spec/dummy/spec/decorators/post_decorator_spec.rb +26 -0
  85. data/spec/dummy/spec/decorators/rspec_integration_spec.rb +19 -0
  86. data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
  87. data/spec/dummy/spec/models/post_spec.rb +14 -0
  88. data/spec/dummy/spec/spec_helper.rb +8 -0
  89. data/spec/generators/decorator/decorator_generator_spec.rb +47 -7
  90. data/spec/integration/integration_spec.rb +33 -0
  91. data/{performance → spec/performance}/decorators.rb +2 -4
  92. data/spec/spec_helper.rb +20 -45
  93. data/spec/support/action_controller.rb +12 -0
  94. data/spec/support/active_model.rb +7 -0
  95. data/spec/support/active_record.rb +9 -0
  96. data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
  97. data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
  98. data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
  99. data/spec/support/decorators/product_decorator.rb +23 -0
  100. data/spec/support/{samples → decorators}/products_decorator.rb +1 -1
  101. data/spec/support/decorators/some_thing_decorator.rb +2 -0
  102. data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
  103. data/spec/support/dummy_app.rb +85 -0
  104. data/spec/support/matchers/have_text.rb +50 -0
  105. data/spec/support/{samples → models}/namespaced_product.rb +1 -3
  106. data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
  107. data/spec/support/{samples → models}/product.rb +17 -2
  108. data/spec/support/models/some_thing.rb +5 -0
  109. data/spec/support/models/uninferrable_decorator_model.rb +3 -0
  110. metadata +241 -87
  111. data/CHANGELOG.markdown +0 -57
  112. data/Readme.markdown +0 -387
  113. data/lib/draper/active_model_support.rb +0 -27
  114. data/lib/draper/base.rb +0 -312
  115. data/lib/draper/decorated_enumerable_proxy.rb +0 -90
  116. data/lib/draper/model_support.rb +0 -25
  117. data/lib/draper/rspec_integration.rb +0 -2
  118. data/lib/draper/system.rb +0 -18
  119. data/spec/draper/base_spec.rb +0 -873
  120. data/spec/draper/decorated_enumerable_proxy_spec.rb +0 -45
  121. data/spec/draper/model_support_spec.rb +0 -48
  122. data/spec/minitest-rails/spec_type_spec.rb +0 -63
  123. data/spec/support/samples/active_record.rb +0 -17
  124. data/spec/support/samples/decorator.rb +0 -5
  125. data/spec/support/samples/decorator_with_allows.rb +0 -3
  126. data/spec/support/samples/decorator_with_denies.rb +0 -3
  127. data/spec/support/samples/decorator_with_denies_all.rb +0 -3
  128. data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
  129. data/spec/support/samples/decorator_with_special_methods.rb +0 -13
  130. data/spec/support/samples/enumerable_proxy.rb +0 -3
  131. data/spec/support/samples/namespaced_product_decorator.rb +0 -7
  132. data/spec/support/samples/product_decorator.rb +0 -7
  133. data/spec/support/samples/sequel_product.rb +0 -13
  134. data/spec/support/samples/some_thing.rb +0 -2
  135. data/spec/support/samples/some_thing_decorator.rb +0 -3
  136. /data/{performance → spec/performance}/active_record.rb +0 -0
  137. /data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
  138. /data/{performance → spec/performance}/models.rb +0 -0
  139. /data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
  140. /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 == '&lt;script&gt;danger&lt;/script&gt;'
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