draper 0.17.0 → 1.0.0.beta2
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.
- data/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.markdown +49 -0
- data/Gemfile +4 -0
- data/Rakefile +50 -31
- data/Readme.markdown +63 -92
- data/draper.gemspec +6 -4
- data/lib/draper/collection_decorator.rb +88 -0
- data/lib/draper/decoratable.rb +44 -0
- data/lib/draper/decorated_association.rb +55 -0
- data/lib/draper/decorator.rb +260 -0
- data/lib/draper/finders.rb +34 -0
- data/lib/draper/helper_proxy.rb +16 -0
- data/lib/draper/railtie.rb +17 -21
- data/lib/draper/security.rb +48 -0
- data/lib/draper/tasks/tu.rake +5 -0
- data/lib/draper/test/minitest_integration.rb +5 -24
- data/lib/draper/test/rspec_integration.rb +0 -6
- data/lib/draper/test/test_unit_integration.rb +9 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +24 -6
- data/lib/draper/view_helpers.rb +36 -0
- data/lib/draper.rb +48 -7
- data/lib/generators/decorator/decorator_generator.rb +1 -1
- data/lib/generators/decorator/templates/decorator.rb +0 -1
- data/lib/generators/rspec/decorator_generator.rb +1 -1
- data/lib/generators/test_unit/decorator_generator.rb +1 -1
- data/lib/generators/test_unit/templates/decorator_test.rb +0 -3
- data/spec/draper/collection_decorator_spec.rb +240 -0
- data/spec/draper/decoratable_spec.rb +164 -0
- data/spec/draper/decorated_association_spec.rb +130 -0
- data/spec/draper/decorator_spec.rb +622 -0
- data/spec/draper/finders_spec.rb +145 -0
- data/spec/draper/helper_proxy_spec.rb +12 -0
- data/spec/draper/security_spec.rb +158 -0
- data/spec/draper/view_helpers_spec.rb +41 -0
- data/spec/dummy/.rspec +2 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/localized_urls.rb +5 -0
- data/spec/dummy/app/controllers/posts_controller.rb +17 -0
- data/spec/dummy/app/decorators/post_decorator.rb +29 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +3 -0
- data/spec/dummy/app/mailers/post_mailer.rb +9 -0
- data/spec/dummy/app/models/post.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +11 -0
- data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
- data/spec/dummy/app/views/posts/_post.html.erb +19 -0
- data/spec/dummy/app/views/posts/show.html.erb +1 -0
- data/spec/dummy/config/application.rb +64 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +34 -0
- data/spec/dummy/config/environments/production.rb +55 -0
- data/spec/dummy/config/environments/test.rb +32 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +7 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
- data/spec/dummy/db/schema.rb +21 -0
- data/spec/dummy/db/seeds.rb +2 -0
- data/spec/dummy/lib/tasks/spec.rake +5 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +26 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +29 -0
- data/spec/dummy/spec/spec_helper.rb +9 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +25 -6
- data/spec/integration/integration_spec.rb +33 -0
- data/spec/minitest-rails/spec_type_spec.rb +63 -0
- data/{performance → spec/performance}/decorators.rb +2 -4
- data/spec/spec_helper.rb +23 -26
- data/spec/support/action_controller.rb +12 -0
- data/spec/support/active_model.rb +7 -0
- data/spec/support/{samples/active_record.rb → active_record.rb} +5 -0
- data/spec/support/{samples → decorators}/decorator_with_application_helper.rb +1 -1
- data/spec/support/decorators/namespaced_product_decorator.rb +5 -0
- data/spec/support/decorators/non_active_model_product_decorator.rb +2 -0
- data/spec/support/decorators/product_decorator.rb +23 -0
- data/spec/support/decorators/products_decorator.rb +10 -0
- data/spec/support/decorators/some_thing_decorator.rb +2 -0
- data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
- data/spec/support/dummy_app.rb +84 -0
- data/spec/support/matchers/have_text.rb +50 -0
- data/spec/support/{samples → models}/namespaced_product.rb +1 -3
- data/spec/support/{samples → models}/non_active_model_product.rb +1 -0
- data/spec/support/{samples → models}/product.rb +17 -2
- data/spec/support/models/some_thing.rb +5 -0
- data/spec/support/models/uninferrable_decorator_model.rb +3 -0
- metadata +211 -102
- data/doc/ApplicationDecorator.html +0 -147
- data/doc/Draper/AllHelpers.html +0 -256
- data/doc/Draper/Base.html +0 -1222
- data/doc/Draper/DecoratorGenerator.html +0 -216
- data/doc/Draper/LazyHelpers.html +0 -174
- data/doc/Draper/ModelSupport.html +0 -164
- data/doc/Draper/System.html +0 -179
- data/doc/Draper.html +0 -123
- data/doc/_index.html +0 -172
- data/doc/class_list.html +0 -47
- data/doc/css/common.css +0 -1
- data/doc/css/full_list.css +0 -53
- data/doc/css/style.css +0 -320
- data/doc/file_list.html +0 -46
- data/doc/frames.html +0 -13
- data/doc/index.html +0 -172
- data/doc/js/app.js +0 -205
- data/doc/js/full_list.js +0 -150
- data/doc/js/jquery.js +0 -16
- data/doc/method_list.html +0 -174
- data/doc/top-level-namespace.html +0 -103
- data/lib/draper/active_model_support.rb +0 -27
- data/lib/draper/base.rb +0 -312
- data/lib/draper/decorated_enumerable_proxy.rb +0 -57
- data/lib/draper/model_support.rb +0 -17
- data/lib/draper/rspec_integration.rb +0 -2
- data/lib/draper/system.rb +0 -10
- data/lib/draper/test/view_context.rb +0 -12
- data/spec/draper/base_spec.rb +0 -863
- data/spec/draper/helper_support_spec.rb +0 -18
- data/spec/draper/model_support_spec.rb +0 -48
- data/spec/draper/view_context_spec.rb +0 -30
- data/spec/support/samples/active_model.rb +0 -9
- data/spec/support/samples/application_controller.rb +0 -42
- data/spec/support/samples/application_helper.rb +0 -8
- data/spec/support/samples/decorator.rb +0 -5
- data/spec/support/samples/decorator_with_allows.rb +0 -3
- data/spec/support/samples/decorator_with_denies.rb +0 -3
- data/spec/support/samples/decorator_with_denies_all.rb +0 -3
- data/spec/support/samples/decorator_with_multiple_allows.rb +0 -4
- data/spec/support/samples/decorator_with_special_methods.rb +0 -13
- data/spec/support/samples/namespaced_product_decorator.rb +0 -7
- data/spec/support/samples/product_decorator.rb +0 -7
- data/spec/support/samples/sequel_product.rb +0 -13
- data/spec/support/samples/some_thing.rb +0 -2
- data/spec/support/samples/some_thing_decorator.rb +0 -3
- /data/{performance → spec/performance}/active_record.rb +0 -0
- /data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
- /data/{performance → spec/performance}/models.rb +0 -0
- /data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
- /data/spec/support/{samples → models}/widget.rb +0 -0
|
@@ -0,0 +1,622 @@
|
|
|
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" do
|
|
129
|
+
subject { Class.new(Draper::Decorator) }
|
|
130
|
+
|
|
131
|
+
context "with a symbol" do
|
|
132
|
+
it "sets .source_class" do
|
|
133
|
+
subject.decorates :product
|
|
134
|
+
subject.source_class.should be Product
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context "with a string" do
|
|
139
|
+
it "sets .source_class" do
|
|
140
|
+
subject.decorates "product"
|
|
141
|
+
subject.source_class.should be Product
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
context "with a class" do
|
|
146
|
+
it "sets .source_class" do
|
|
147
|
+
subject.decorates Product
|
|
148
|
+
subject.source_class.should be Product
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe ".source_class" do
|
|
154
|
+
context "when not set by .decorates" do
|
|
155
|
+
context "for an anonymous decorator" do
|
|
156
|
+
subject { Class.new(Draper::Decorator) }
|
|
157
|
+
|
|
158
|
+
it "raises an UninferrableSourceError" do
|
|
159
|
+
expect{subject.source_class}.to raise_error Draper::UninferrableSourceError
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
context "for a decorator without a corresponding source" do
|
|
164
|
+
subject { SpecificProductDecorator }
|
|
165
|
+
|
|
166
|
+
it "raises an UninferrableSourceError" do
|
|
167
|
+
expect{subject.source_class}.to raise_error Draper::UninferrableSourceError
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
context "for a decorator called Decorator" do
|
|
172
|
+
subject { Draper::Decorator }
|
|
173
|
+
|
|
174
|
+
it "raises an UninferrableSourceError" do
|
|
175
|
+
expect{subject.source_class}.to raise_error Draper::UninferrableSourceError
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
context "for a decorator with a name not ending in Decorator" do
|
|
180
|
+
subject { DecoratorWithApplicationHelper }
|
|
181
|
+
|
|
182
|
+
it "raises an UninferrableSourceError" do
|
|
183
|
+
expect{subject.source_class}.to raise_error Draper::UninferrableSourceError
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
context "for an inferrable source" do
|
|
188
|
+
subject { ProductDecorator }
|
|
189
|
+
|
|
190
|
+
it "infers the source" do
|
|
191
|
+
subject.source_class.should be Product
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
context "for a namespaced inferrable source" do
|
|
196
|
+
subject { Namespace::ProductDecorator }
|
|
197
|
+
|
|
198
|
+
it "infers the namespaced source" do
|
|
199
|
+
subject.source_class.should be Namespace::Product
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
describe ".source_class?" do
|
|
206
|
+
subject { Class.new(Draper::Decorator) }
|
|
207
|
+
|
|
208
|
+
it "returns truthy when .source_class is set" do
|
|
209
|
+
subject.stub(:source_class).and_return(Product)
|
|
210
|
+
subject.source_class?.should be_true
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "returns false when .source_class is not inferrable" do
|
|
214
|
+
subject.stub(:source_class).and_raise(Draper::UninferrableSourceError.new(subject))
|
|
215
|
+
subject.source_class?.should be_false
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
describe ".decorates_association" do
|
|
220
|
+
let(:decorator_class) { Class.new(ProductDecorator) }
|
|
221
|
+
before { decorator_class.decorates_association :similar_products, with: ProductDecorator }
|
|
222
|
+
|
|
223
|
+
describe "overridden association method" do
|
|
224
|
+
let(:decorated_association) { ->{} }
|
|
225
|
+
|
|
226
|
+
it "creates a DecoratedAssociation" do
|
|
227
|
+
Draper::DecoratedAssociation.should_receive(:new).with(source, :similar_products, {with: ProductDecorator}).and_return(decorated_association)
|
|
228
|
+
subject.similar_products
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
it "memoizes the DecoratedAssociation" do
|
|
232
|
+
Draper::DecoratedAssociation.should_receive(:new).once.and_return(decorated_association)
|
|
233
|
+
subject.similar_products
|
|
234
|
+
subject.similar_products
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it "calls the DecoratedAssociation" do
|
|
238
|
+
Draper::DecoratedAssociation.stub(:new).and_return(decorated_association)
|
|
239
|
+
decorated_association.should_receive(:call).and_return(:decorated)
|
|
240
|
+
subject.similar_products.should be :decorated
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
describe ".decorates_associations" do
|
|
246
|
+
subject { decorator_class }
|
|
247
|
+
|
|
248
|
+
it "decorates each of the associations" do
|
|
249
|
+
subject.should_receive(:decorates_association).with(:similar_products, {})
|
|
250
|
+
subject.should_receive(:decorates_association).with(:previous_version, {})
|
|
251
|
+
|
|
252
|
+
subject.decorates_associations :similar_products, :previous_version
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it "dispatches options" do
|
|
256
|
+
subject.should_receive(:decorates_association).with(:similar_products, {with: ProductDecorator})
|
|
257
|
+
subject.should_receive(:decorates_association).with(:previous_version, {with: ProductDecorator})
|
|
258
|
+
|
|
259
|
+
subject.decorates_associations :similar_products, :previous_version, with: ProductDecorator
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
describe "#applied_decorators" do
|
|
264
|
+
it "returns a list of decorators applied to a model" do
|
|
265
|
+
decorator = ProductDecorator.new(SpecificProductDecorator.new(Product.new))
|
|
266
|
+
decorator.applied_decorators.should == [SpecificProductDecorator, ProductDecorator]
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
describe "#decorated_with?" do
|
|
271
|
+
it "checks if a decorator has been applied to a model" do
|
|
272
|
+
decorator = ProductDecorator.new(SpecificProductDecorator.new(Product.new))
|
|
273
|
+
decorator.should be_decorated_with ProductDecorator
|
|
274
|
+
decorator.should be_decorated_with SpecificProductDecorator
|
|
275
|
+
decorator.should_not be_decorated_with WidgetDecorator
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
describe "#decorated?" do
|
|
280
|
+
it "returns true" do
|
|
281
|
+
subject.should be_decorated
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
describe "#source" do
|
|
286
|
+
it "returns the wrapped object" do
|
|
287
|
+
subject.source.should be source
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
it "is aliased to #to_source" do
|
|
291
|
+
subject.to_source.should be source
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it "is aliased to #model" do
|
|
295
|
+
subject.model.should be source
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
describe "#to_model" do
|
|
300
|
+
it "returns the decorator" do
|
|
301
|
+
subject.to_model.should be subject
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
describe "#to_param" do
|
|
306
|
+
it "proxies to the source" do
|
|
307
|
+
source.stub(:to_param).and_return(42)
|
|
308
|
+
subject.to_param.should == 42
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
describe "#==" do
|
|
313
|
+
context "with itself" do
|
|
314
|
+
it "returns true" do
|
|
315
|
+
(subject == subject).should be_true
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
context "with another decorator having the same source" do
|
|
320
|
+
it "returns true" do
|
|
321
|
+
(subject == ProductDecorator.new(source)).should be_true
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
context "with another decorator having a different source" do
|
|
326
|
+
it "returns false" do
|
|
327
|
+
(subject == ProductDecorator.new(Object.new)).should be_false
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
context "with the source object" do
|
|
332
|
+
it "returns true" do
|
|
333
|
+
(subject == source).should be_true
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
context "with another object" do
|
|
338
|
+
it "returns false" do
|
|
339
|
+
(subject == Object.new).should be_false
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
describe "#===" do
|
|
345
|
+
context "with itself" do
|
|
346
|
+
it "returns true" do
|
|
347
|
+
(subject === subject).should be_true
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
context "with another decorator having the same source" do
|
|
352
|
+
it "returns true" do
|
|
353
|
+
(subject === ProductDecorator.new(source)).should be_true
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
context "with another decorator having a different source" do
|
|
358
|
+
it "returns false" do
|
|
359
|
+
(subject === ProductDecorator.new(Object.new)).should be_false
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
context "with the source object" do
|
|
364
|
+
it "returns true" do
|
|
365
|
+
(subject === source).should be_true
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
context "with another object" do
|
|
370
|
+
it "returns false" do
|
|
371
|
+
(subject === Object.new).should be_false
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
describe "#respond_to?" do
|
|
377
|
+
let(:decorator_class) { Class.new(ProductDecorator) }
|
|
378
|
+
|
|
379
|
+
it "returns true for its own methods" do
|
|
380
|
+
subject.should respond_to :awesome_title
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
it "returns true for the source's methods" do
|
|
384
|
+
subject.should respond_to :title
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
context "with include_private" do
|
|
388
|
+
it "returns true for its own private methods" do
|
|
389
|
+
subject.respond_to?(:awesome_private_title, true).should be_true
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
it "returns false for the source's private methods" do
|
|
393
|
+
subject.respond_to?(:private_title, true).should be_false
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
context "with method security" do
|
|
398
|
+
it "respects allows" do
|
|
399
|
+
subject.class.allows :hello_world
|
|
400
|
+
|
|
401
|
+
subject.should respond_to :hello_world
|
|
402
|
+
subject.should_not respond_to :goodnight_moon
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
it "respects denies" do
|
|
406
|
+
subject.class.denies :goodnight_moon
|
|
407
|
+
|
|
408
|
+
subject.should respond_to :hello_world
|
|
409
|
+
subject.should_not respond_to :goodnight_moon
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
it "respects denies_all" do
|
|
413
|
+
subject.class.denies_all
|
|
414
|
+
|
|
415
|
+
subject.should_not respond_to :hello_world
|
|
416
|
+
subject.should_not respond_to :goodnight_moon
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
describe ".respond_to?" do
|
|
422
|
+
subject { Class.new(ProductDecorator) }
|
|
423
|
+
|
|
424
|
+
context "without a source class" do
|
|
425
|
+
it "returns true for its own class methods" do
|
|
426
|
+
subject.should respond_to :my_class_method
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
it "returns false for other class methods" do
|
|
430
|
+
subject.should_not respond_to :sample_class_method
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
context "with a source_class" do
|
|
435
|
+
before { subject.decorates :product }
|
|
436
|
+
|
|
437
|
+
it "returns true for its own class methods" do
|
|
438
|
+
subject.should respond_to :my_class_method
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
it "returns true for the source's class methods" do
|
|
442
|
+
subject.should respond_to :sample_class_method
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
describe "proxying" do
|
|
448
|
+
context "instance methods" do
|
|
449
|
+
let(:decorator_class) { Class.new(ProductDecorator) }
|
|
450
|
+
|
|
451
|
+
it "does not proxy methods that are defined on the decorator" do
|
|
452
|
+
subject.overridable.should be :overridden
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
it "does not proxy methods inherited from Object" do
|
|
456
|
+
subject.inspect.should_not be source.inspect
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
it "proxies missing methods that exist on the source" do
|
|
460
|
+
source.stub(:hello_world).and_return(:proxied)
|
|
461
|
+
subject.hello_world.should be :proxied
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
it "adds proxied methods to the decorator when they are used" do
|
|
465
|
+
subject.methods.should_not include :hello_world
|
|
466
|
+
subject.hello_world
|
|
467
|
+
subject.methods.should include :hello_world
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
it "passes blocks to proxied methods" do
|
|
471
|
+
subject.block{"marker"}.should == "marker"
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
it "does not confuse Kernel#Array" do
|
|
475
|
+
Array(subject).should be_a Array
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
it "proxies delegated methods" do
|
|
479
|
+
subject.delegated_method.should == "Yay, delegation"
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
it "does not proxy private methods" do
|
|
483
|
+
expect{subject.private_title}.to raise_error NoMethodError
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
context "with method security" do
|
|
487
|
+
it "respects allows" do
|
|
488
|
+
source.stub(:hello_world, :goodnight_moon).and_return(:proxied)
|
|
489
|
+
subject.class.allows :hello_world
|
|
490
|
+
|
|
491
|
+
subject.hello_world.should be :proxied
|
|
492
|
+
expect{subject.goodnight_moon}.to raise_error NameError
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
it "respects denies" do
|
|
496
|
+
source.stub(:hello_world, :goodnight_moon).and_return(:proxied)
|
|
497
|
+
subject.class.denies :goodnight_moon
|
|
498
|
+
|
|
499
|
+
subject.hello_world.should be :proxied
|
|
500
|
+
expect{subject.goodnight_moon}.to raise_error NameError
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
it "respects denies_all" do
|
|
504
|
+
source.stub(:hello_world, :goodnight_moon).and_return(:proxied)
|
|
505
|
+
subject.class.denies_all
|
|
506
|
+
|
|
507
|
+
expect{subject.hello_world}.to raise_error NameError
|
|
508
|
+
expect{subject.goodnight_moon}.to raise_error NameError
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
context "class methods" do
|
|
514
|
+
subject { Class.new(ProductDecorator) }
|
|
515
|
+
let(:source_class) { Product }
|
|
516
|
+
before { subject.decorates source_class }
|
|
517
|
+
|
|
518
|
+
it "does not proxy methods that are defined on the decorator" do
|
|
519
|
+
subject.overridable.should be :overridden
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
it "proxies missing methods that exist on the source" do
|
|
523
|
+
source_class.stub(:hello_world).and_return(:proxied)
|
|
524
|
+
subject.hello_world.should be :proxied
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
describe "method security" do
|
|
530
|
+
subject(:decorator_class) { Draper::Decorator }
|
|
531
|
+
let(:security) { stub }
|
|
532
|
+
before { decorator_class.stub(:security).and_return(security) }
|
|
533
|
+
|
|
534
|
+
it "delegates .denies to Draper::Security" do
|
|
535
|
+
security.should_receive(:denies).with(:foo, :bar)
|
|
536
|
+
decorator_class.denies :foo, :bar
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
it "delegates .denies_all to Draper::Security" do
|
|
540
|
+
security.should_receive(:denies_all)
|
|
541
|
+
decorator_class.denies_all
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
it "delegates .allows to Draper::Security" do
|
|
545
|
+
security.should_receive(:allows).with(:foo, :bar)
|
|
546
|
+
decorator_class.allows :foo, :bar
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
context "in a Rails application" do
|
|
551
|
+
let(:decorator_class) { DecoratorWithApplicationHelper }
|
|
552
|
+
|
|
553
|
+
it "has access to ApplicationHelper helpers" do
|
|
554
|
+
subject.uses_hello_world.should == "Hello, World!"
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
it "is able to use the content_tag helper" do
|
|
558
|
+
subject.sample_content.to_s.should == "<span>Hello, World!</span>"
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
it "is able to use the link_to helper" do
|
|
562
|
+
subject.sample_link.should == %{<a href="/World">Hello</a>}
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
it "is able to use the truncate helper" do
|
|
566
|
+
subject.sample_truncate.should == "Once..."
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
it "is able to access html_escape, a private method" do
|
|
570
|
+
subject.sample_html_escaped_text.should == '<script>danger</script>'
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
it "pretends to be the source class" do
|
|
575
|
+
subject.kind_of?(source.class).should be_true
|
|
576
|
+
subject.is_a?(source.class).should be_true
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
it "is still its own class" do
|
|
580
|
+
subject.kind_of?(subject.class).should be_true
|
|
581
|
+
subject.is_a?(subject.class).should be_true
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
describe ".decorates_finders" do
|
|
585
|
+
it "extends the Finders module" do
|
|
586
|
+
ProductDecorator.should be_a_kind_of Draper::Finders
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
describe "#serializable_hash" do
|
|
591
|
+
let(:decorator_class) { ProductDecorator }
|
|
592
|
+
|
|
593
|
+
it "serializes overridden attributes" do
|
|
594
|
+
subject.serializable_hash[:overridable].should be :overridden
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
describe ".method_missing" do
|
|
599
|
+
context "when called on an anonymous decorator" do
|
|
600
|
+
subject { ->{ Class.new(Draper::Decorator).fizzbuzz } }
|
|
601
|
+
it { should raise_error NoMethodError }
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
context "when called on an uninferrable decorator" do
|
|
605
|
+
subject { ->{ SpecificProductDecorator.fizzbuzz } }
|
|
606
|
+
it { should raise_error NoMethodError }
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
context "when called on an inferrable decorator" do
|
|
610
|
+
context "for a method known to the inferred class" do
|
|
611
|
+
subject { ->{ ProductDecorator.model_name } }
|
|
612
|
+
it { should_not raise_error }
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
context "for a method unknown to the inferred class" do
|
|
616
|
+
subject { ->{ ProductDecorator.fizzbuzz } }
|
|
617
|
+
it { should raise_error NoMethodError }
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
end
|