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