draper 4.0.4 → 4.0.5

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +3 -6
  4. data/lib/draper/collection_decorator.rb +0 -6
  5. data/lib/draper/compatibility/broadcastable.rb +24 -0
  6. data/lib/draper/compatibility/global_id.rb +9 -5
  7. data/lib/draper/decoratable.rb +5 -0
  8. data/lib/draper/decorator.rb +2 -18
  9. data/lib/draper/railtie.rb +6 -1
  10. data/lib/draper/version.rb +1 -1
  11. metadata +4 -232
  12. data/.github/PULL_REQUEST_TEMPLATE.md +0 -24
  13. data/.github/workflows/ci.yml +0 -71
  14. data/.gitignore +0 -18
  15. data/Gemfile +0 -46
  16. data/bin/bundle +0 -114
  17. data/bin/rake +0 -29
  18. data/draper.gemspec +0 -34
  19. data/spec/draper/collection_decorator_spec.rb +0 -291
  20. data/spec/draper/configuration_spec.rb +0 -49
  21. data/spec/draper/decoratable/equality_spec.rb +0 -10
  22. data/spec/draper/decoratable_spec.rb +0 -235
  23. data/spec/draper/decorated_association_spec.rb +0 -82
  24. data/spec/draper/decorates_assigned_spec.rb +0 -71
  25. data/spec/draper/decorator_spec.rb +0 -872
  26. data/spec/draper/draper_spec.rb +0 -25
  27. data/spec/draper/factory_spec.rb +0 -247
  28. data/spec/draper/finders_spec.rb +0 -166
  29. data/spec/draper/helper_proxy_spec.rb +0 -61
  30. data/spec/draper/lazy_helpers_spec.rb +0 -21
  31. data/spec/draper/query_methods/load_strategy_spec.rb +0 -26
  32. data/spec/draper/query_methods_spec.rb +0 -80
  33. data/spec/draper/undecorate_chain_spec.rb +0 -20
  34. data/spec/draper/undecorate_spec.rb +0 -19
  35. data/spec/draper/view_context/build_strategy_spec.rb +0 -115
  36. data/spec/draper/view_context_spec.rb +0 -182
  37. data/spec/draper/view_helpers_spec.rb +0 -8
  38. data/spec/dummy/.rspec +0 -1
  39. data/spec/dummy/Rakefile +0 -7
  40. data/spec/dummy/app/assets/config/manifest.js +0 -3
  41. data/spec/dummy/app/controllers/application_controller.rb +0 -2
  42. data/spec/dummy/app/controllers/base_controller.rb +0 -4
  43. data/spec/dummy/app/controllers/localized_urls.rb +0 -5
  44. data/spec/dummy/app/controllers/posts_controller.rb +0 -20
  45. data/spec/dummy/app/decorators/comment_decorator.rb +0 -13
  46. data/spec/dummy/app/decorators/mongoid_post_decorator.rb +0 -2
  47. data/spec/dummy/app/decorators/post_decorator.rb +0 -60
  48. data/spec/dummy/app/helpers/application_helper.rb +0 -5
  49. data/spec/dummy/app/jobs/publish_post_job.rb +0 -7
  50. data/spec/dummy/app/mailers/application_mailer.rb +0 -3
  51. data/spec/dummy/app/mailers/post_mailer.rb +0 -19
  52. data/spec/dummy/app/models/admin.rb +0 -3
  53. data/spec/dummy/app/models/application_record.rb +0 -3
  54. data/spec/dummy/app/models/comment.rb +0 -3
  55. data/spec/dummy/app/models/mongoid_post.rb +0 -3
  56. data/spec/dummy/app/models/post.rb +0 -7
  57. data/spec/dummy/app/models/user.rb +0 -3
  58. data/spec/dummy/app/views/layouts/application.html.erb +0 -11
  59. data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +0 -1
  60. data/spec/dummy/app/views/posts/_post.html.erb +0 -42
  61. data/spec/dummy/app/views/posts/show.html.erb +0 -1
  62. data/spec/dummy/bin/rails +0 -4
  63. data/spec/dummy/config/application.rb +0 -39
  64. data/spec/dummy/config/boot.rb +0 -5
  65. data/spec/dummy/config/database.yml +0 -25
  66. data/spec/dummy/config/environment.rb +0 -5
  67. data/spec/dummy/config/environments/development.rb +0 -78
  68. data/spec/dummy/config/environments/production.rb +0 -102
  69. data/spec/dummy/config/environments/test.rb +0 -69
  70. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  71. data/spec/dummy/config/initializers/draper.rb +0 -5
  72. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  73. data/spec/dummy/config/initializers/inflections.rb +0 -15
  74. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  75. data/spec/dummy/config/initializers/secret_token.rb +0 -8
  76. data/spec/dummy/config/initializers/session_store.rb +0 -8
  77. data/spec/dummy/config/locales/en.yml +0 -5
  78. data/spec/dummy/config/mongoid.yml +0 -142
  79. data/spec/dummy/config/routes.rb +0 -9
  80. data/spec/dummy/config/storage.yml +0 -7
  81. data/spec/dummy/config.ru +0 -4
  82. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +0 -8
  83. data/spec/dummy/db/migrate/20240907041839_create_comments.rb +0 -9
  84. data/spec/dummy/db/schema.rb +0 -28
  85. data/spec/dummy/db/seeds.rb +0 -2
  86. data/spec/dummy/fast_spec/post_decorator_spec.rb +0 -37
  87. data/spec/dummy/lib/tasks/test.rake +0 -16
  88. data/spec/dummy/log/.gitkeep +0 -0
  89. data/spec/dummy/public/404.html +0 -26
  90. data/spec/dummy/public/422.html +0 -26
  91. data/spec/dummy/public/500.html +0 -25
  92. data/spec/dummy/public/favicon.ico +0 -0
  93. data/spec/dummy/script/rails +0 -6
  94. data/spec/dummy/spec/decorators/active_model_serializers_spec.rb +0 -12
  95. data/spec/dummy/spec/decorators/devise_spec.rb +0 -55
  96. data/spec/dummy/spec/decorators/helpers_spec.rb +0 -21
  97. data/spec/dummy/spec/decorators/post_decorator_spec.rb +0 -64
  98. data/spec/dummy/spec/decorators/spec_type_spec.rb +0 -7
  99. data/spec/dummy/spec/decorators/view_context_spec.rb +0 -22
  100. data/spec/dummy/spec/jobs/publish_post_job_spec.rb +0 -11
  101. data/spec/dummy/spec/mailers/post_mailer_spec.rb +0 -25
  102. data/spec/dummy/spec/models/application_spec.rb +0 -7
  103. data/spec/dummy/spec/models/mongoid_post_spec.rb +0 -8
  104. data/spec/dummy/spec/models/post_spec.rb +0 -51
  105. data/spec/dummy/spec/rails_helper.rb +0 -69
  106. data/spec/dummy/spec/shared_examples/decoratable.rb +0 -22
  107. data/spec/dummy/spec/spec_helper.rb +0 -93
  108. data/spec/dummy/test/decorators/minitest/devise_test.rb +0 -55
  109. data/spec/dummy/test/decorators/minitest/helpers_test.rb +0 -21
  110. data/spec/dummy/test/decorators/minitest/spec_type_test.rb +0 -52
  111. data/spec/dummy/test/decorators/minitest/view_context_test.rb +0 -24
  112. data/spec/dummy/test/decorators/test_unit/devise_test.rb +0 -55
  113. data/spec/dummy/test/decorators/test_unit/helpers_test.rb +0 -21
  114. data/spec/dummy/test/decorators/test_unit/view_context_test.rb +0 -24
  115. data/spec/dummy/test/minitest_helper.rb +0 -2
  116. data/spec/dummy/test/test_helper.rb +0 -3
  117. data/spec/generators/controller/controller_generator_spec.rb +0 -23
  118. data/spec/generators/decorator/decorator_generator_spec.rb +0 -93
  119. data/spec/generators/install/install_generator_spec.rb +0 -19
  120. data/spec/integration/integration_spec.rb +0 -70
  121. data/spec/performance/active_record.rb +0 -4
  122. data/spec/performance/benchmark.rb +0 -55
  123. data/spec/performance/decorators.rb +0 -45
  124. data/spec/performance/models.rb +0 -20
  125. data/spec/spec_helper.rb +0 -50
  126. data/spec/support/dummy_app.rb +0 -85
  127. data/spec/support/matchers/have_text.rb +0 -50
  128. data/spec/support/shared_examples/decoratable_equality.rb +0 -40
  129. data/spec/support/shared_examples/view_helpers.rb +0 -39
@@ -1,872 +0,0 @@
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 object" do
21
- object = Model.new
22
- decorator = Decorator.new(object)
23
-
24
- expect(decorator.object).to be object
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 object instead" do
36
- object = Model.new
37
- decorated = Decorator.new(object)
38
- redecorated = Decorator.new(decorated)
39
-
40
- expect(redecorated.object).to be object
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.object).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
- allow_any_instance_of(Object).to receive(:warn) { |instance, 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
- allow_any_instance_of(Object).to receive(:warn)
86
- redecorated = Decorator.decorate(decorated)
87
-
88
- expect(redecorated.object).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 { allow(CollectionDecorator).to receive(: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
- object = [Model.new]
120
-
121
- expect(CollectionDecorator).to receive(:new).with(object, {with: Decorator})
122
- Decorator.decorate_collection(object)
123
- end
124
-
125
- it "passes options to the collection decorator" do
126
- options = {with: OtherDecorator, context: {some: "context"}}
127
-
128
- expect(CollectionDecorator).to 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
- object = [Model.new]
136
-
137
- expect(ProductsDecorator).to receive(:new).with(object, {with: ProductDecorator})
138
- ProductDecorator.decorate_collection(object)
139
- end
140
-
141
- it "passes options to the collection decorator" do
142
- options = {with: OtherDecorator, context: {some: "context"}}
143
-
144
- expect(ProductsDecorator).to 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 .object_class with a symbol" do
154
- Decorator.decorates :product
155
-
156
- expect(Decorator.object_class).to be Product
157
- end
158
-
159
- it "sets .object_class with a string" do
160
- Decorator.decorates "product"
161
-
162
- expect(Decorator.object_class).to be Product
163
- end
164
-
165
- it "sets .object_class with a class" do
166
- Decorator.decorates Product
167
-
168
- expect(Decorator.object_class).to be Product
169
- end
170
- end
171
-
172
- describe ".object_class" do
173
- protect_class ProductDecorator
174
- protect_class Namespaced::ProductDecorator
175
-
176
- context "when not set by .decorates" do
177
- it "raises an UninferrableObjectError for a so-named 'Decorator'" do
178
- expect{Decorator.object_class}.to raise_error UninferrableObjectError
179
- end
180
-
181
- it "raises an UninferrableObjectError for anonymous decorators" do
182
- expect{Class.new(Decorator).object_class}.to raise_error UninferrableObjectError
183
- end
184
-
185
- it "raises an UninferrableObjectError for a decorator without a model" do
186
- SomeDecorator = Class.new(Draper::Decorator)
187
- expect{SomeDecorator.object_class}.to raise_error UninferrableObjectError
188
- end
189
-
190
- it "raises an UninferrableObjectError for other naming conventions" do
191
- ProductPresenter = Class.new(Draper::Decorator)
192
- expect{ProductPresenter.object_class}.to raise_error UninferrableObjectError
193
- end
194
-
195
- it "infers the object class for '<Model>Decorator'" do
196
- expect(ProductDecorator.object_class).to be Product
197
- end
198
-
199
- it "infers the object class for namespaced decorators" do
200
- expect(Namespaced::ProductDecorator.object_class).to be Namespaced::Product
201
- end
202
-
203
- context "when an unrelated NameError is thrown" do
204
- it "re-raises that error" do
205
- # Not related to safe_constantize behavior, we just want to raise a NameError inside the function
206
- allow_any_instance_of(String).to receive(:safe_constantize) { SomethingThatDoesntExist }
207
- expect{ProductDecorator.object_class}.to raise_error NameError, /SomethingThatDoesntExist/
208
- end
209
- end
210
- end
211
- end
212
-
213
- describe ".object_class?" do
214
- it "returns truthy when .object_class is set" do
215
- allow(Decorator).to receive(:object_class).and_return(Model)
216
-
217
- expect(Decorator.object_class?).to be_truthy
218
- end
219
-
220
- it "returns false when .object_class is not inferrable" do
221
- allow(Decorator).to receive(:object_class).and_raise(UninferrableObjectError.new(Decorator))
222
-
223
- expect(Decorator.object_class?).to be_falsey
224
- end
225
- end
226
-
227
- describe '.collection_decorator_class' do
228
- it 'defaults to CollectionDecorator' do
229
- allow_any_instance_of(String).to receive(:safe_constantize) { nil }
230
- expect(ProductDecorator.collection_decorator_class).to be Draper::CollectionDecorator
231
- end
232
-
233
- it 'infers collection decorator based on name' do
234
- expect(ProductDecorator.collection_decorator_class).to be ProductsDecorator
235
- end
236
-
237
- it 'infers collection decorator base on name for namespeced model' do
238
- expect(Namespaced::ProductDecorator.collection_decorator_class).to be Namespaced::ProductsDecorator
239
- end
240
- end
241
-
242
- describe ".decorates_association" do
243
- protect_class Decorator
244
-
245
- describe "options validation" do
246
- before { allow(DecoratedAssociation).to receive(:new).and_return(->{}) }
247
-
248
- it "does not raise error on valid options" do
249
- valid_options = {with: Class, scope: :sorted, context: {}}
250
- expect{Decorator.decorates_association(:children, valid_options)}.not_to raise_error
251
- end
252
-
253
- it "raises error on invalid options" do
254
- expect{Decorator.decorates_association(:children, foo: "bar")}.to raise_error ArgumentError, /Unknown key/
255
- end
256
- end
257
-
258
- describe "defines an association method" do
259
- it "creates a DecoratedAssociation" do
260
- options = {with: Class.new, scope: :foo, context: {}}
261
- Decorator.decorates_association :children, options
262
- decorator = Decorator.new(Model.new)
263
-
264
- expect(DecoratedAssociation).to receive(:new).with(decorator, :children, options).and_return(->{})
265
- decorator.children
266
- end
267
-
268
- it "memoizes the DecoratedAssociation" do
269
- Decorator.decorates_association :children
270
- decorator = Decorator.new(Model.new)
271
-
272
- expect(DecoratedAssociation).to receive(:new).once.and_return(->{})
273
- decorator.children
274
- decorator.children
275
- end
276
-
277
- it "calls the DecoratedAssociation" do
278
- Decorator.decorates_association :children
279
- decorator = Decorator.new(Model.new)
280
- decorated_association = ->{}
281
- allow(DecoratedAssociation).to receive(:new).and_return(decorated_association)
282
-
283
- expect(decorated_association).to receive(:call).and_return(:decorated)
284
- expect(decorator.children).to be :decorated
285
- end
286
- end
287
- end
288
-
289
- describe ".decorates_associations" do
290
- protect_class Decorator
291
-
292
- it "decorates each of the associations" do
293
- expect(Decorator).to receive(:decorates_association).with(:friends, {})
294
- expect(Decorator).to receive(:decorates_association).with(:enemies, {})
295
- Decorator.decorates_associations :friends, :enemies
296
- end
297
-
298
- it "dispatches options" do
299
- options = {with: Class.new, scope: :foo, context: {}}
300
-
301
- expect(Decorator).to receive(:decorates_association).with(:friends, options)
302
- expect(Decorator).to receive(:decorates_association).with(:enemies, options)
303
- Decorator.decorates_associations :friends, :enemies, options
304
- end
305
- end
306
-
307
- describe "#applied_decorators" do
308
- it "returns a list of decorators applied to a model" do
309
- decorator = ProductDecorator.new(OtherDecorator.new(Decorator.new(Model.new)))
310
-
311
- expect(decorator.applied_decorators).to eq [Decorator, OtherDecorator, ProductDecorator]
312
- end
313
- end
314
-
315
- describe "#decorated_with?" do
316
- it "checks if a decorator has been applied to a model" do
317
- decorator = ProductDecorator.new(Decorator.new(Model.new))
318
-
319
- expect(decorator).to be_decorated_with Decorator
320
- expect(decorator).to be_decorated_with ProductDecorator
321
- expect(decorator).not_to be_decorated_with OtherDecorator
322
- end
323
- end
324
-
325
- describe "#decorated?" do
326
- it "returns true" do
327
- decorator = Decorator.new(Model.new)
328
-
329
- expect(decorator).to be_decorated
330
- end
331
- end
332
-
333
- describe "#object" do
334
- it "returns the wrapped object" do
335
- object = Model.new
336
- decorator = Decorator.new(object)
337
-
338
- expect(decorator.object).to be object
339
- expect(decorator.model).to be object
340
- end
341
-
342
- it "is aliased to #model" do
343
- object = Model.new
344
- decorator = Decorator.new(object)
345
-
346
- expect(decorator.model).to be object
347
- end
348
- end
349
-
350
- describe "aliasing object to object class name" do
351
- context "when object_class is inferrable from the decorator name" do
352
- it "aliases object to the object class name" do
353
- object = double
354
- decorator = ProductDecorator.new(object)
355
-
356
- expect(decorator.product).to be object
357
- end
358
- end
359
-
360
- context "when object_class is set by decorates" do
361
- it "aliases object to the object class name" do
362
- decorator_class = Class.new(Decorator) { decorates Product }
363
- object = double
364
- decorator = decorator_class.new(object)
365
-
366
- expect(decorator.product).to be object
367
- end
368
- end
369
-
370
- context "when object_class's name is several words long" do
371
- it "underscores the method name" do
372
- stub_const "LongWindedModel", Class.new
373
- decorator_class = Class.new(Decorator) { decorates LongWindedModel }
374
- object = double
375
- decorator = decorator_class.new(object)
376
-
377
- expect(decorator.long_winded_model).to be object
378
- end
379
- end
380
-
381
- context "when object_class is not set" do
382
- it "does not alias object" do
383
- decorator_class = Class.new(Decorator)
384
-
385
- expect(decorator_class.instance_methods).to eq Decorator.instance_methods
386
- end
387
- end
388
- end
389
-
390
- describe "#to_model" do
391
- it "returns the decorator" do
392
- decorator = Decorator.new(Model.new)
393
-
394
- expect(decorator.to_model).to be decorator
395
- end
396
- end
397
-
398
- describe "#to_param" do
399
- it "delegates to the object" do
400
- decorator = Decorator.new(double(to_param: :delegated))
401
-
402
- expect(decorator.to_param).to be :delegated
403
- end
404
- end
405
-
406
- describe "#present?" do
407
- it "delegates to the object" do
408
- decorator = Decorator.new(double(present?: :delegated))
409
-
410
- expect(decorator.present?).to be :delegated
411
- end
412
- end
413
-
414
- describe "#blank?" do
415
- it "delegates to the object" do
416
- decorator = Decorator.new(double(blank?: :delegated))
417
-
418
- expect(decorator.blank?).to be :delegated
419
- end
420
- end
421
-
422
- describe "#to_partial_path" do
423
- it "delegates to the object" do
424
- decorator = Decorator.new(double(to_partial_path: :delegated))
425
-
426
- expect(decorator.to_partial_path).to be :delegated
427
- end
428
- end
429
-
430
- describe "#to_s" do
431
- it "delegates to the object" do
432
- decorator = Decorator.new(double(to_s: :delegated))
433
-
434
- expect(decorator.to_s).to be :delegated
435
- end
436
- end
437
-
438
- describe "#inspect" do
439
- it "returns a detailed description of the decorator" do
440
- decorator = ProductDecorator.new(double)
441
-
442
- expect(decorator.inspect).to match(/#<ProductDecorator:0x\h+ .+>/)
443
- end
444
-
445
- it "includes the object" do
446
- decorator = Decorator.new(double(inspect: "#<the object>"))
447
-
448
- expect(decorator.inspect).to include "@object=#<the object>"
449
- end
450
-
451
- it "includes the context" do
452
- decorator = Decorator.new(double, context: {foo: "bar"})
453
-
454
- expect(decorator.inspect).to include '@context={:foo=>"bar"}'
455
- end
456
-
457
- it "includes other instance variables" do
458
- decorator = Decorator.new(double)
459
- decorator.instance_variable_set :@foo, "bar"
460
-
461
- expect(decorator.inspect).to include '@foo="bar"'
462
- end
463
- end
464
-
465
- describe "#attributes" do
466
- it "returns only the object's attributes that are implemented by the decorator" do
467
- decorator = Decorator.new(double(attributes: {foo: "bar", baz: "qux"}))
468
- allow(decorator).to receive(:foo)
469
-
470
- expect(decorator.attributes).to eq({foo: "bar"})
471
- end
472
- end
473
-
474
- describe ".model_name" do
475
- it "delegates to the object class" do
476
- allow(Decorator).to receive(:object_class).and_return(double(model_name: :delegated))
477
-
478
- expect(Decorator.model_name).to be :delegated
479
- end
480
- end
481
-
482
- describe "#==" do
483
- it "works for a object that does not include Decoratable" do
484
- object = Object.new
485
- decorator = Decorator.new(object)
486
-
487
- expect(decorator).to eq Decorator.new(object)
488
- end
489
-
490
- it "works for a multiply-decorated object that does not include Decoratable" do
491
- object = Object.new
492
- decorator = Decorator.new(object)
493
-
494
- expect(decorator).to eq ProductDecorator.new(Decorator.new(object))
495
- end
496
-
497
- it "is true when object #== is true" do
498
- object = Model.new
499
- decorator = Decorator.new(object)
500
- other = double(object: Model.new)
501
-
502
- expect(object).to receive(:==).with(other).and_return(true)
503
- expect(decorator == other).to be_truthy
504
- end
505
-
506
- it "is false when object #== is false" do
507
- object = Model.new
508
- decorator = Decorator.new(object)
509
- other = double(object: Model.new)
510
-
511
- expect(object).to receive(:==).with(other).and_return(false)
512
- expect(decorator == other).to be_falsey
513
- end
514
- end
515
-
516
- describe "#===" do
517
- it "is true when #== is true" do
518
- decorator = Decorator.new(Model.new)
519
- allow(decorator).to receive(:==) { true }
520
-
521
- expect(decorator === :anything).to be_truthy
522
- end
523
-
524
- it "is false when #== is false" do
525
- decorator = Decorator.new(Model.new)
526
- allow(decorator).to receive(:==).with(:anything).and_return(false)
527
-
528
- expect(decorator === :anything).to be_falsey
529
- end
530
- end
531
-
532
- describe "#eql?" do
533
- it "is true when #eql? is true" do
534
- first = Decorator.new('foo')
535
- second = Decorator.new('foo')
536
-
537
- expect(first.eql? second).to be
538
- end
539
-
540
- it "is false when #eql? is false" do
541
- first = Decorator.new('foo')
542
- second = Decorator.new('bar')
543
-
544
- expect(first.eql? second).to_not be
545
- end
546
- end
547
-
548
- describe "#hash" do
549
- it "is consistent for equal objects" do
550
- object = Model.new
551
- first = Decorator.new(object)
552
- second = Decorator.new(object)
553
-
554
- expect(first.hash == second.hash).to be
555
- end
556
- end
557
-
558
- describe ".delegate" do
559
- protect_class Decorator
560
-
561
- it "defaults the :to option to :object" do
562
- expect(Object).to receive(:delegate).with(:foo, :bar, to: :object)
563
- Decorator.delegate :foo, :bar
564
- end
565
-
566
- it "does not overwrite the :to option if supplied" do
567
- expect(Object).to receive(:delegate).with(:foo, :bar, to: :baz)
568
- Decorator.delegate :foo, :bar, to: :baz
569
- end
570
- end
571
-
572
- context "with .delegate_all" do
573
- protect_class Decorator
574
-
575
- before { Decorator.delegate_all }
576
-
577
- describe "#method_missing" do
578
- it "delegates missing methods that exist on the object" do
579
- decorator = Decorator.new(double(hello_world: :delegated))
580
-
581
- expect(decorator.hello_world).to be :delegated
582
- end
583
-
584
- it 'delegates `super` to parent class first' do
585
- parent_decorator_class = Class.new(Decorator) do
586
- def hello_world
587
- "parent#hello_world"
588
- end
589
- end
590
-
591
- child_decorator_class = Class.new(parent_decorator_class) do
592
- def hello_world
593
- super
594
- end
595
- end
596
-
597
- decorator = child_decorator_class.new(double(hello_world: 'object#hello_world'))
598
- expect(decorator.hello_world).to eq 'parent#hello_world'
599
- end
600
-
601
- it 'delegates `super` to object if method does not exist on parent class' do
602
- decorator_class = Class.new(Decorator) do
603
- def hello_world
604
- super
605
- end
606
- end
607
-
608
- decorator = decorator_class.new(double(hello_world: 'object#hello_world'))
609
- expect(decorator.hello_world).to eq 'object#hello_world'
610
- end
611
-
612
- it 'raises `NoMethodError` when `super` is called on for method that does not exist' do
613
- decorator_class = Class.new(Decorator) do
614
- def hello_world
615
- super
616
- end
617
- end
618
-
619
- decorator = decorator_class.new(double)
620
- expect{decorator.hello_world}.to raise_error NoMethodError
621
- end
622
-
623
- it "allows decorator to decorate different classes of objects" do
624
- decorator_1 = Decorator.new(double)
625
- decorator_2 = Decorator.new(double(hello_world: :delegated))
626
-
627
- decorator_2.hello_world
628
- expect(decorator_1.methods).not_to include :hello_world
629
- end
630
-
631
- it "passes blocks to delegated methods" do
632
- object = Model.new
633
- allow(object).to receive(:hello_world) { |*args, &block| block.call }
634
- decorator = Decorator.new(object)
635
-
636
- expect(decorator.hello_world{:yielded}).to be :yielded
637
- end
638
-
639
- it "does not confuse Kernel#Array" do
640
- decorator = Decorator.new(Model.new)
641
-
642
- expect(Array(decorator)).to be_an Array
643
- end
644
-
645
- it "delegates already-delegated methods" do
646
- object = Class.new{ delegate :bar, to: :foo }.new
647
- allow(object).to receive_messages foo: double(bar: :delegated)
648
- decorator = Decorator.new(object)
649
-
650
- expect(decorator.bar).to be :delegated
651
- end
652
-
653
- it "does not delegate private methods" do
654
- object = Class.new{ private; def hello_world; end }.new
655
- decorator = Decorator.new(object)
656
-
657
- expect{decorator.hello_world}.to raise_error NoMethodError
658
- end
659
-
660
- it "does not delegate methods that do not exist on the object" do
661
- decorator = Decorator.new(Model.new)
662
-
663
- expect(decorator.methods).not_to include :hello_world
664
- expect{decorator.hello_world}.to raise_error NoMethodError
665
- expect(decorator.methods).not_to include :hello_world
666
- end
667
-
668
- context 'when decorator overrides a public method defined on the object with a private' do
669
- let(:decorator_class) do
670
- Class.new(Decorator) do
671
- private
672
-
673
- def hello_world
674
- 'hello world'
675
- end
676
- end
677
- end
678
-
679
- let(:object) { Class.new { def hello_world; end }.new }
680
-
681
- it 'does not delegate the public method defined on the object' do
682
- decorator = decorator_class.new(object)
683
-
684
- expect{ decorator.hello_world }.to raise_error NoMethodError
685
- end
686
- end
687
-
688
- context 'when delegated method has the same name as private method defined on another object' do
689
- let(:decorator_class) { Class.new(Decorator) }
690
- let(:object) { Class.new { def print; end }.new }
691
-
692
- it 'delegates the public method defined on the object' do
693
- decorator = decorator_class.new(object)
694
-
695
- # `print` private method is defined on `Object`
696
- expect{ decorator.print }.not_to raise_error
697
- end
698
- end
699
- end
700
-
701
- context ".method_missing" do
702
- context "without an object class" do
703
- it "raises a NoMethodError on missing methods" do
704
- expect{Decorator.hello_world}.to raise_error NoMethodError
705
- end
706
- end
707
-
708
- context "with an object class" do
709
- it "delegates methods that exist on the object class" do
710
- object_class = Class.new
711
- allow(object_class).to receive_messages hello_world: :delegated
712
- allow(Decorator).to receive_messages object_class: object_class
713
-
714
- expect(Decorator.hello_world).to be :delegated
715
- end
716
-
717
- it "does not delegate methods that do not exist on the object class" do
718
- allow(Decorator).to receive_messages object_class: Class.new
719
-
720
- expect{Decorator.hello_world}.to raise_error NoMethodError
721
- end
722
- end
723
- end
724
-
725
- describe "#respond_to?" do
726
- it "returns true for its own methods" do
727
- Decorator.class_eval{def hello_world; end}
728
- decorator = Decorator.new(Model.new)
729
-
730
- expect(decorator).to respond_to :hello_world
731
- end
732
-
733
- it "returns true for the object's methods" do
734
- decorator = Decorator.new(double(hello_world: :delegated))
735
-
736
- expect(decorator).to respond_to :hello_world
737
- end
738
-
739
- context "with include_private" do
740
- it "returns true for its own private methods" do
741
- Decorator.class_eval{private; def hello_world; end}
742
- decorator = Decorator.new(Model.new)
743
-
744
- expect(decorator.respond_to?(:hello_world, true)).to be_truthy
745
- end
746
-
747
- it "returns false for the object's private methods" do
748
- object = Class.new{private; def hello_world; end}.new
749
- decorator = Decorator.new(object)
750
-
751
- expect(decorator.respond_to?(:hello_world, true)).to be_falsey
752
- end
753
- end
754
- end
755
-
756
- describe ".respond_to?" do
757
- context "without a object class" do
758
- it "returns true for its own class methods" do
759
- Decorator.class_eval{def self.hello_world; end}
760
-
761
- expect(Decorator).to respond_to :hello_world
762
- end
763
-
764
- it "returns false for other class methods" do
765
- expect(Decorator).not_to respond_to :goodnight_moon
766
- end
767
- end
768
-
769
- context "with a object class" do
770
- it "returns true for its own class methods" do
771
- Decorator.class_eval{def self.hello_world; end}
772
- allow(Decorator).to receive_messages object_class: Class.new
773
-
774
- expect(Decorator).to respond_to :hello_world
775
- end
776
-
777
- it "returns true for the object's class methods" do
778
- allow(Decorator).to receive_messages object_class: double(hello_world: :delegated)
779
-
780
- expect(Decorator).to respond_to :hello_world
781
- end
782
- end
783
- end
784
-
785
- describe "#respond_to_missing?" do
786
- it "allows #method to be called on delegated methods" do
787
- object = Class.new{def hello_world; end}.new
788
- decorator = Decorator.new(object)
789
-
790
- expect(decorator.method(:hello_world)).not_to be_nil
791
- end
792
- end
793
-
794
- describe ".respond_to_missing?" do
795
- it "allows .method to be called on delegated class methods" do
796
- allow(Decorator).to receive_messages object_class: double(hello_world: :delegated)
797
-
798
- expect(Decorator.method(:hello_world)).not_to be_nil
799
- end
800
- end
801
- end
802
-
803
- describe "class spoofing" do
804
- it "pretends to be a kind of the object class" do
805
- decorator = Decorator.new(Model.new)
806
-
807
- expect(decorator.kind_of?(Model)).to be_truthy
808
- expect(decorator.is_a?(Model)).to be_truthy
809
- end
810
-
811
- it "is still a kind of its own class" do
812
- decorator = Decorator.new(Model.new)
813
-
814
- expect(decorator.kind_of?(Decorator)).to be_truthy
815
- expect(decorator.is_a?(Decorator)).to be_truthy
816
- end
817
-
818
- it "pretends to be an instance of the object class" do
819
- decorator = Decorator.new(Model.new)
820
-
821
- expect(decorator.instance_of?(Model)).to be_truthy
822
- end
823
-
824
- it "is still an instance of its own class" do
825
- decorator = Decorator.new(Model.new)
826
-
827
- expect(decorator.instance_of?(Decorator)).to be_truthy
828
- end
829
- end
830
-
831
- describe ".decorates_finders" do
832
- protect_class Decorator
833
-
834
- it "extends the Finders module" do
835
- expect(Decorator).not_to be_a_kind_of Finders
836
- Decorator.decorates_finders
837
- expect(Decorator).to be_a_kind_of Finders
838
- end
839
- end
840
-
841
- describe "Enumerable hash and equality functionality" do
842
- describe "#uniq" do
843
- it "removes duplicate objects with same decorator" do
844
- object = Model.new
845
- array = [Decorator.new(object), Decorator.new(object)]
846
-
847
- expect(array.uniq.count).to eq(1)
848
- end
849
-
850
- it "separates different objects with identical decorators" do
851
- array = [Decorator.new('foo'), Decorator.new('bar')]
852
-
853
- expect(array.uniq.count).to eq(2)
854
- end
855
-
856
- it "separates identical objects with different decorators" do
857
- object = Model.new
858
- array = [Decorator.new(object), OtherDecorator.new(object)]
859
-
860
- expect(array.uniq.count).to eq(2)
861
- end
862
-
863
- it "distinguishes between an objects and its decorated version" do
864
- object = Model.new
865
- array = [Decorator.new(object), object]
866
-
867
- expect(array.uniq.count).to eq(2)
868
- end
869
- end
870
- end
871
- end
872
- end