draper 0.18.0 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG.markdown +8 -0
  4. data/Gemfile +4 -0
  5. data/Rakefile +50 -31
  6. data/Readme.markdown +42 -48
  7. data/draper.gemspec +2 -1
  8. data/lib/draper.rb +42 -7
  9. data/lib/draper/collection_decorator.rb +88 -0
  10. data/lib/draper/decoratable.rb +44 -0
  11. data/lib/draper/decorated_association.rb +55 -0
  12. data/lib/draper/decorator.rb +208 -0
  13. data/lib/draper/finders.rb +44 -0
  14. data/lib/draper/helper_proxy.rb +16 -0
  15. data/lib/draper/railtie.rb +17 -21
  16. data/lib/draper/security.rb +48 -0
  17. data/lib/draper/tasks/tu.rake +5 -0
  18. data/lib/draper/test/minitest_integration.rb +1 -1
  19. data/lib/draper/test/test_unit_integration.rb +9 -0
  20. data/lib/draper/version.rb +1 -1
  21. data/lib/draper/view_context.rb +6 -13
  22. data/lib/draper/view_helpers.rb +36 -0
  23. data/lib/generators/decorator/decorator_generator.rb +1 -1
  24. data/lib/generators/decorator/templates/decorator.rb +0 -1
  25. data/spec/draper/collection_decorator_spec.rb +240 -0
  26. data/spec/draper/decoratable_spec.rb +164 -0
  27. data/spec/draper/decorated_association_spec.rb +130 -0
  28. data/spec/draper/decorator_spec.rb +497 -0
  29. data/spec/draper/finders_spec.rb +156 -0
  30. data/spec/draper/helper_proxy_spec.rb +12 -0
  31. data/spec/draper/security_spec.rb +158 -0
  32. data/spec/draper/view_helpers_spec.rb +41 -0
  33. data/spec/dummy/.rspec +2 -0
  34. data/spec/dummy/README.rdoc +261 -0
  35. data/spec/dummy/Rakefile +7 -0
  36. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  37. data/spec/dummy/app/controllers/localized_urls.rb +5 -0
  38. data/spec/dummy/app/controllers/posts_controller.rb +17 -0
  39. data/spec/dummy/app/decorators/post_decorator.rb +25 -0
  40. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  41. data/spec/dummy/app/mailers/application_mailer.rb +3 -0
  42. data/spec/dummy/app/mailers/post_mailer.rb +9 -0
  43. data/spec/dummy/app/models/post.rb +3 -0
  44. data/spec/dummy/app/views/layouts/application.html.erb +11 -0
  45. data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
  46. data/spec/dummy/app/views/posts/_post.html.erb +19 -0
  47. data/spec/dummy/app/views/posts/show.html.erb +1 -0
  48. data/spec/dummy/config.ru +4 -0
  49. data/spec/dummy/config/application.rb +64 -0
  50. data/spec/dummy/config/boot.rb +10 -0
  51. data/spec/dummy/config/database.yml +25 -0
  52. data/spec/dummy/config/environment.rb +5 -0
  53. data/spec/dummy/config/environments/development.rb +34 -0
  54. data/spec/dummy/config/environments/production.rb +55 -0
  55. data/spec/dummy/config/environments/test.rb +32 -0
  56. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  57. data/spec/dummy/config/initializers/inflections.rb +15 -0
  58. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  59. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  60. data/spec/dummy/config/initializers/session_store.rb +8 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/locales/en.yml +5 -0
  63. data/spec/dummy/config/routes.rb +7 -0
  64. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
  65. data/spec/dummy/db/schema.rb +21 -0
  66. data/spec/dummy/db/seeds.rb +2 -0
  67. data/spec/dummy/lib/tasks/spec.rake +5 -0
  68. data/spec/dummy/public/404.html +26 -0
  69. data/spec/dummy/public/422.html +26 -0
  70. data/spec/dummy/public/500.html +25 -0
  71. data/spec/dummy/public/favicon.ico +0 -0
  72. data/spec/dummy/script/rails +6 -0
  73. data/spec/dummy/spec/decorators/post_decorator_spec.rb +23 -0
  74. data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
  75. data/spec/dummy/spec/spec_helper.rb +9 -0
  76. data/spec/generators/decorator/decorator_generator_spec.rb +3 -4
  77. data/spec/integration/integration_spec.rb +33 -0
  78. data/{performance → spec/performance}/active_record.rb +0 -0
  79. data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
  80. data/{performance → spec/performance}/decorators.rb +2 -4
  81. data/{performance → spec/performance}/models.rb +0 -0
  82. data/spec/spec_helper.rb +20 -41
  83. data/spec/support/action_controller.rb +12 -0
  84. data/spec/support/active_model.rb +7 -0
  85. data/spec/support/{samples/active_record.rb → active_record.rb} +5 -0
  86. data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
  87. data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
  88. data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
  89. data/spec/support/decorators/product_decorator.rb +19 -0
  90. data/spec/support/{samples → decorators}/products_decorator.rb +1 -1
  91. data/spec/support/decorators/some_thing_decorator.rb +2 -0
  92. data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
  93. data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
  94. data/spec/support/dummy_app.rb +84 -0
  95. data/spec/support/matchers/have_text.rb +50 -0
  96. data/spec/support/{samples → models}/namespaced_product.rb +1 -3
  97. data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
  98. data/spec/support/{samples → models}/product.rb +13 -2
  99. data/spec/support/models/some_thing.rb +5 -0
  100. data/spec/support/models/uninferrable_decorator_model.rb +3 -0
  101. data/spec/support/{samples → models}/widget.rb +0 -0
  102. metadata +185 -68
  103. data/lib/draper/active_model_support.rb +0 -27
  104. data/lib/draper/base.rb +0 -312
  105. data/lib/draper/decorated_enumerable_proxy.rb +0 -90
  106. data/lib/draper/model_support.rb +0 -25
  107. data/lib/draper/rspec_integration.rb +0 -2
  108. data/lib/draper/system.rb +0 -18
  109. data/spec/draper/base_spec.rb +0 -873
  110. data/spec/draper/decorated_enumerable_proxy_spec.rb +0 -45
  111. data/spec/draper/model_support_spec.rb +0 -48
  112. data/spec/support/samples/decorator.rb +0 -5
  113. data/spec/support/samples/decorator_with_allows.rb +0 -3
  114. data/spec/support/samples/decorator_with_denies.rb +0 -3
  115. data/spec/support/samples/decorator_with_denies_all.rb +0 -3
  116. data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
  117. data/spec/support/samples/decorator_with_special_methods.rb +0 -13
  118. data/spec/support/samples/enumerable_proxy.rb +0 -3
  119. data/spec/support/samples/namespaced_product_decorator.rb +0 -7
  120. data/spec/support/samples/product_decorator.rb +0 -7
  121. data/spec/support/samples/sequel_product.rb +0 -13
  122. data/spec/support/samples/some_thing.rb +0 -2
  123. data/spec/support/samples/some_thing_decorator.rb +0 -3
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe Draper::DecoratedAssociation do
4
+ let(:decorated_association) { Draper::DecoratedAssociation.new(source, association, options) }
5
+ let(:source) { Product.new }
6
+ let(:options) { {} }
7
+
8
+ describe "#call" do
9
+ subject { decorated_association.call }
10
+
11
+ context "for an ActiveModel collection association" do
12
+ let(:association) { :similar_products }
13
+
14
+ context "when the association is not empty" do
15
+ it "decorates the collection" do
16
+ subject.should be_a Draper::CollectionDecorator
17
+ end
18
+
19
+ it "infers the decorator" do
20
+ subject.decorator_class.should be :infer
21
+ end
22
+ end
23
+
24
+ context "when the association is empty" do
25
+ it "doesn't decorate the collection" do
26
+ source.stub(:similar_products).and_return([])
27
+ subject.should_not be_a Draper::CollectionDecorator
28
+ subject.should be_empty
29
+ end
30
+ end
31
+ end
32
+
33
+ context "for non-ActiveModel collection associations" do
34
+ let(:association) { :poro_similar_products }
35
+
36
+ context "when the association is not empty" do
37
+ it "decorates the collection" do
38
+ subject.should be_a Draper::CollectionDecorator
39
+ end
40
+
41
+ it "infers the decorator" do
42
+ subject.decorator_class.should be :infer
43
+ end
44
+ end
45
+
46
+ context "when the association is empty" do
47
+ it "doesn't decorate the collection" do
48
+ source.stub(:poro_similar_products).and_return([])
49
+ subject.should_not be_a Draper::CollectionDecorator
50
+ subject.should be_empty
51
+ end
52
+ end
53
+ end
54
+
55
+ context "for an ActiveModel singular association" do
56
+ let(:association) { :previous_version }
57
+
58
+ context "when the association is present" do
59
+ it "decorates the association" do
60
+ subject.should be_decorated_with ProductDecorator
61
+ end
62
+ end
63
+
64
+ context "when the association is absent" do
65
+ it "doesn't decorate the association" do
66
+ source.stub(:previous_version).and_return(nil)
67
+ subject.should be_nil
68
+ end
69
+ end
70
+ end
71
+
72
+ context "for a non-ActiveModel singular association" do
73
+ let(:association) { :poro_previous_version }
74
+
75
+ context "when the association is present" do
76
+ it "decorates the association" do
77
+ subject.should be_decorated_with ProductDecorator
78
+ end
79
+ end
80
+
81
+ context "when the association is absent" do
82
+ it "doesn't decorate the association" do
83
+ source.stub(:poro_previous_version).and_return(nil)
84
+ subject.should be_nil
85
+ end
86
+ end
87
+ end
88
+
89
+ context "when a decorator is specified" do
90
+ let(:options) { {with: SpecificProductDecorator} }
91
+
92
+ context "for a singular association" do
93
+ let(:association) { :previous_version }
94
+
95
+ it "decorates with the specified decorator" do
96
+ subject.should be_decorated_with SpecificProductDecorator
97
+ end
98
+ end
99
+
100
+ context "for a collection association" do
101
+ let(:association) { :similar_products}
102
+
103
+ it "decorates with a collection of the specifed decorators" do
104
+ subject.should be_a Draper::CollectionDecorator
105
+ subject.decorator_class.should be SpecificProductDecorator
106
+ end
107
+ end
108
+ end
109
+
110
+ context "when a collection decorator is specified" do
111
+ let(:association) { :similar_products }
112
+ let(:options) { {with: ProductsDecorator} }
113
+
114
+ it "decorates with the specified decorator" do
115
+ subject.should be_a ProductsDecorator
116
+ end
117
+ end
118
+
119
+ context "with a scope" do
120
+ let(:association) { :thing }
121
+ let(:options) { {scope: :foo} }
122
+
123
+ it "applies the scope before decoration" do
124
+ scoped = [SomeThing.new]
125
+ SomeThing.any_instance.should_receive(:foo).and_return(scoped)
126
+ subject.source.should be scoped
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,497 @@
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
+ it "sets the source" do
11
+ subject.source.should be source
12
+ end
13
+
14
+ it "stores options" do
15
+ decorator = decorator_class.new(source, some: "options")
16
+ decorator.options.should == {some: "options"}
17
+ end
18
+
19
+ context "when decorating an instance of itself" do
20
+ it "does not redecorate" do
21
+ decorator = ProductDecorator.new(source)
22
+ ProductDecorator.new(decorator).source.should be source
23
+ end
24
+
25
+ context "when options are supplied" do
26
+ it "overwrites existing options" do
27
+ decorator = ProductDecorator.new(source, role: :admin)
28
+ ProductDecorator.new(decorator, role: :user).options.should == {role: :user}
29
+ end
30
+ end
31
+
32
+ context "when no options are supplied" do
33
+ it "preserves existing options" do
34
+ decorator = ProductDecorator.new(source, role: :admin)
35
+ ProductDecorator.new(decorator).options.should == {role: :admin}
36
+ end
37
+ end
38
+ end
39
+
40
+ it "decorates other decorators" do
41
+ decorator = ProductDecorator.new(source)
42
+ SpecificProductDecorator.new(decorator).source.should be decorator
43
+ end
44
+
45
+ it "warns if target is already decorated with the same decorator class" do
46
+ warning_message = nil
47
+ Object.any_instance.stub(:warn) { |message| warning_message = message }
48
+
49
+ deep_decorator = SpecificProductDecorator.new(ProductDecorator.new(Product.new))
50
+ expect {
51
+ ProductDecorator.new(deep_decorator)
52
+ }.to change { warning_message }
53
+ warning_message.should =~ /ProductDecorator/
54
+ warning_message.should include caller(1).first
55
+ end
56
+ end
57
+
58
+ describe ".decorate_collection" do
59
+ subject { ProductDecorator.decorate_collection(source) }
60
+ let(:source) { [Product.new, Widget.new] }
61
+
62
+ it "returns a collection decorator" do
63
+ subject.should be_a Draper::CollectionDecorator
64
+ subject.source.should be source
65
+ end
66
+
67
+ it "uses itself as the item decorator by default" do
68
+ subject.each {|item| item.should be_a ProductDecorator}
69
+ end
70
+
71
+ context "when given :with => :infer" do
72
+ subject { ProductDecorator.decorate_collection(source, with: :infer) }
73
+
74
+ it "infers the item decorators" do
75
+ subject.first.should be_a ProductDecorator
76
+ subject.last.should be_a WidgetDecorator
77
+ end
78
+ end
79
+
80
+ context "with options" do
81
+ subject { ProductDecorator.decorate_collection(source, with: :infer, some: "options") }
82
+
83
+ it "passes the options to the collection decorator" do
84
+ subject.options.should == {some: "options"}
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "#helpers" do
90
+ it "returns a HelperProxy" do
91
+ subject.helpers.should be_a Draper::HelperProxy
92
+ end
93
+
94
+ it "is aliased to #h" do
95
+ subject.h.should be subject.helpers
96
+ end
97
+
98
+ it "initializes the wrapper only once" do
99
+ helper_proxy = subject.helpers
100
+ helper_proxy.stub(:test_method) { "test_method" }
101
+ subject.helpers.test_method.should == "test_method"
102
+ subject.helpers.test_method.should == "test_method"
103
+ end
104
+ end
105
+
106
+ describe "#localize" do
107
+ before { subject.helpers.should_receive(:localize).with(:an_object, {some: "options"}) }
108
+
109
+ it "delegates to #helpers" do
110
+ subject.localize(:an_object, some: "options")
111
+ end
112
+
113
+ it "is aliased to #l" do
114
+ subject.l(:an_object, some: "options")
115
+ end
116
+ end
117
+
118
+ describe ".helpers" do
119
+ it "returns a HelperProxy" do
120
+ subject.class.helpers.should be_a Draper::HelperProxy
121
+ end
122
+
123
+ it "is aliased to .h" do
124
+ subject.class.h.should be subject.class.helpers
125
+ end
126
+ end
127
+
128
+ describe ".decorates_association" do
129
+ let(:decorator_class) { Class.new(ProductDecorator) }
130
+ before { decorator_class.decorates_association :similar_products, with: ProductDecorator }
131
+
132
+ describe "overridden association method" do
133
+ let(:decorated_association) { ->{} }
134
+
135
+ it "creates a DecoratedAssociation" do
136
+ Draper::DecoratedAssociation.should_receive(:new).with(source, :similar_products, {with: ProductDecorator}).and_return(decorated_association)
137
+ subject.similar_products
138
+ end
139
+
140
+ it "memoizes the DecoratedAssociation" do
141
+ Draper::DecoratedAssociation.should_receive(:new).once.and_return(decorated_association)
142
+ subject.similar_products
143
+ subject.similar_products
144
+ end
145
+
146
+ it "calls the DecoratedAssociation" do
147
+ Draper::DecoratedAssociation.stub(:new).and_return(decorated_association)
148
+ decorated_association.should_receive(:call).and_return(:decorated)
149
+ subject.similar_products.should be :decorated
150
+ end
151
+ end
152
+ end
153
+
154
+ describe ".decorates_associations" do
155
+ subject { decorator_class }
156
+
157
+ it "decorates each of the associations" do
158
+ subject.should_receive(:decorates_association).with(:similar_products, {})
159
+ subject.should_receive(:decorates_association).with(:previous_version, {})
160
+
161
+ subject.decorates_associations :similar_products, :previous_version
162
+ end
163
+
164
+ it "dispatches options" do
165
+ subject.should_receive(:decorates_association).with(:similar_products, {with: ProductDecorator})
166
+ subject.should_receive(:decorates_association).with(:previous_version, {with: ProductDecorator})
167
+
168
+ subject.decorates_associations :similar_products, :previous_version, with: ProductDecorator
169
+ end
170
+ end
171
+
172
+ describe "#applied_decorators" do
173
+ it "returns a list of decorators applied to a model" do
174
+ decorator = ProductDecorator.new(SpecificProductDecorator.new(Product.new))
175
+ decorator.applied_decorators.should == [SpecificProductDecorator, ProductDecorator]
176
+ end
177
+ end
178
+
179
+ describe "#decorated_with?" do
180
+ it "checks if a decorator has been applied to a model" do
181
+ decorator = ProductDecorator.new(SpecificProductDecorator.new(Product.new))
182
+ decorator.should be_decorated_with ProductDecorator
183
+ decorator.should be_decorated_with SpecificProductDecorator
184
+ decorator.should_not be_decorated_with WidgetDecorator
185
+ end
186
+ end
187
+
188
+ describe "#decorated?" do
189
+ it "returns true" do
190
+ subject.should be_decorated
191
+ end
192
+ end
193
+
194
+ describe "#source" do
195
+ it "returns the wrapped object" do
196
+ subject.source.should be source
197
+ end
198
+
199
+ it "is aliased to #to_source" do
200
+ subject.to_source.should be source
201
+ end
202
+
203
+ it "is aliased to #model" do
204
+ subject.model.should be source
205
+ end
206
+ end
207
+
208
+ describe "#to_model" do
209
+ it "returns the decorator" do
210
+ subject.to_model.should be subject
211
+ end
212
+ end
213
+
214
+ describe "#to_param" do
215
+ it "proxies to the source" do
216
+ source.stub(:to_param).and_return(42)
217
+ subject.to_param.should == 42
218
+ end
219
+ end
220
+
221
+ describe "#==" do
222
+ context "with itself" do
223
+ it "returns true" do
224
+ (subject == subject).should be_true
225
+ end
226
+ end
227
+
228
+ context "with another decorator having the same source" do
229
+ it "returns true" do
230
+ (subject == ProductDecorator.new(source)).should be_true
231
+ end
232
+ end
233
+
234
+ context "with another decorator having a different source" do
235
+ it "returns false" do
236
+ (subject == ProductDecorator.new(Object.new)).should be_false
237
+ end
238
+ end
239
+
240
+ context "with the source object" do
241
+ it "returns true" do
242
+ (subject == source).should be_true
243
+ end
244
+ end
245
+
246
+ context "with another object" do
247
+ it "returns false" do
248
+ (subject == Object.new).should be_false
249
+ end
250
+ end
251
+ end
252
+
253
+ describe "#===" do
254
+ context "with itself" do
255
+ it "returns true" do
256
+ (subject === subject).should be_true
257
+ end
258
+ end
259
+
260
+ context "with another decorator having the same source" do
261
+ it "returns true" do
262
+ (subject === ProductDecorator.new(source)).should be_true
263
+ end
264
+ end
265
+
266
+ context "with another decorator having a different source" do
267
+ it "returns false" do
268
+ (subject === ProductDecorator.new(Object.new)).should be_false
269
+ end
270
+ end
271
+
272
+ context "with the source object" do
273
+ it "returns true" do
274
+ (subject === source).should be_true
275
+ end
276
+ end
277
+
278
+ context "with another object" do
279
+ it "returns false" do
280
+ (subject === Object.new).should be_false
281
+ end
282
+ end
283
+ end
284
+
285
+ describe "#respond_to?" do
286
+ let(:decorator_class) { Class.new(ProductDecorator) }
287
+
288
+ it "returns true for its own methods" do
289
+ subject.should respond_to :awesome_title
290
+ end
291
+
292
+ it "returns true for the source's methods" do
293
+ subject.should respond_to :title
294
+ end
295
+
296
+ context "with include_private" do
297
+ it "returns true for its own private methods" do
298
+ subject.respond_to?(:awesome_private_title, true).should be_true
299
+ end
300
+
301
+ it "returns true for the source's private methods" do
302
+ subject.respond_to?(:private_title, true).should be_true
303
+ end
304
+ end
305
+
306
+ context "with method security" do
307
+ it "respects allows" do
308
+ subject.class.allows :hello_world
309
+
310
+ subject.should respond_to :hello_world
311
+ subject.should_not respond_to :goodnight_moon
312
+ end
313
+
314
+ it "respects denies" do
315
+ subject.class.denies :goodnight_moon
316
+
317
+ subject.should respond_to :hello_world
318
+ subject.should_not respond_to :goodnight_moon
319
+ end
320
+
321
+ it "respects denies_all" do
322
+ subject.class.denies_all
323
+
324
+ subject.should_not respond_to :hello_world
325
+ subject.should_not respond_to :goodnight_moon
326
+ end
327
+ end
328
+ end
329
+
330
+ describe "method proxying" do
331
+ let(:decorator_class) { Class.new(ProductDecorator) }
332
+
333
+ it "does not proxy methods that are defined on the decorator" do
334
+ subject.overridable.should be :overridden
335
+ end
336
+
337
+ it "does not proxy methods inherited from Object" do
338
+ subject.inspect.should_not be source.inspect
339
+ end
340
+
341
+ it "proxies missing methods that exist on the source" do
342
+ source.stub(:hello_world).and_return(:proxied)
343
+ subject.hello_world.should be :proxied
344
+ end
345
+
346
+ it "adds proxied methods to the decorator when they are used" do
347
+ subject.methods.should_not include :hello_world
348
+ subject.hello_world
349
+ subject.methods.should include :hello_world
350
+ end
351
+
352
+ it "passes blocks to proxied methods" do
353
+ subject.block{"marker"}.should == "marker"
354
+ end
355
+
356
+ it "does not confuse Kernel#Array" do
357
+ Array(subject).should be_a Array
358
+ end
359
+
360
+ it "proxies delegated methods" do
361
+ subject.delegated_method.should == "Yay, delegation"
362
+ end
363
+
364
+ context "with method security" do
365
+ it "respects allows" do
366
+ source.stub(:hello_world, :goodnight_moon).and_return(:proxied)
367
+ subject.class.allows :hello_world
368
+
369
+ subject.hello_world.should be :proxied
370
+ expect{subject.goodnight_moon}.to raise_error NameError
371
+ end
372
+
373
+ it "respects denies" do
374
+ source.stub(:hello_world, :goodnight_moon).and_return(:proxied)
375
+ subject.class.denies :goodnight_moon
376
+
377
+ subject.hello_world.should be :proxied
378
+ expect{subject.goodnight_moon}.to raise_error NameError
379
+ end
380
+
381
+ it "respects denies_all" do
382
+ source.stub(:hello_world, :goodnight_moon).and_return(:proxied)
383
+ subject.class.denies_all
384
+
385
+ expect{subject.hello_world}.to raise_error NameError
386
+ expect{subject.goodnight_moon}.to raise_error NameError
387
+ end
388
+ end
389
+ end
390
+
391
+ describe "method security" do
392
+ subject(:decorator_class) { Draper::Decorator }
393
+ let(:security) { stub }
394
+ before { decorator_class.stub(:security).and_return(security) }
395
+
396
+ it "delegates .denies to Draper::Security" do
397
+ security.should_receive(:denies).with(:foo, :bar)
398
+ decorator_class.denies :foo, :bar
399
+ end
400
+
401
+ it "delegates .denies_all to Draper::Security" do
402
+ security.should_receive(:denies_all)
403
+ decorator_class.denies_all
404
+ end
405
+
406
+ it "delegates .allows to Draper::Security" do
407
+ security.should_receive(:allows).with(:foo, :bar)
408
+ decorator_class.allows :foo, :bar
409
+ end
410
+ end
411
+
412
+ context "in a Rails application" do
413
+ let(:decorator_class) { DecoratorWithApplicationHelper }
414
+
415
+ it "has access to ApplicationHelper helpers" do
416
+ subject.uses_hello_world.should == "Hello, World!"
417
+ end
418
+
419
+ it "is able to use the content_tag helper" do
420
+ subject.sample_content.to_s.should == "<span>Hello, World!</span>"
421
+ end
422
+
423
+ it "is able to use the link_to helper" do
424
+ subject.sample_link.should == "<a href=\"/World\">Hello</a>"
425
+ end
426
+
427
+ it "is able to use the truncate helper" do
428
+ subject.sample_truncate.should == "Once..."
429
+ end
430
+
431
+ it "is able to access html_escape, a private method" do
432
+ subject.sample_html_escaped_text.should == '&lt;script&gt;danger&lt;/script&gt;'
433
+ end
434
+ end
435
+
436
+ it "pretends to be the source class" do
437
+ subject.kind_of?(source.class).should be_true
438
+ subject.is_a?(source.class).should be_true
439
+ end
440
+
441
+ it "is still its own class" do
442
+ subject.kind_of?(subject.class).should be_true
443
+ subject.is_a?(subject.class).should be_true
444
+ end
445
+
446
+ describe ".has_finders" do
447
+ it "extends the Finders module" do
448
+ ProductDecorator.should be_a_kind_of Draper::Finders
449
+ end
450
+
451
+ context "with no options" do
452
+ it "infers the finder class" do
453
+ ProductDecorator.finder_class.should be Product
454
+ end
455
+
456
+ context "for a namespaced model" do
457
+ it "infers the finder class" do
458
+ Namespace::ProductDecorator.finder_class.should be Namespace::Product
459
+ end
460
+ end
461
+ end
462
+
463
+ context "with :for option" do
464
+ subject { Class.new(Draper::Decorator) }
465
+
466
+ context "with a symbol" do
467
+ it "sets the finder class" do
468
+ subject.has_finders for: :product
469
+ subject.finder_class.should be Product
470
+ end
471
+ end
472
+
473
+ context "with a string" do
474
+ it "sets the finder class" do
475
+ subject.has_finders for: "some_thing"
476
+ subject.finder_class.should be SomeThing
477
+ end
478
+ end
479
+
480
+ context "with a class" do
481
+ it "sets the finder_class" do
482
+ subject.has_finders for: Namespace::Product
483
+ subject.finder_class.should be Namespace::Product
484
+ end
485
+ end
486
+ end
487
+ end
488
+
489
+ describe "#serializable_hash" do
490
+ let(:decorator_class) { ProductDecorator }
491
+
492
+ it "serializes overridden attributes" do
493
+ subject.serializable_hash[:overridable].should be :overridden
494
+ end
495
+ end
496
+
497
+ end