draper 0.18.0 → 1.0.0.beta1

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 (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