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.
- data/.gitignore +3 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.markdown +8 -0
- data/Gemfile +4 -0
- data/Rakefile +50 -31
- data/Readme.markdown +42 -48
- data/draper.gemspec +2 -1
- data/lib/draper.rb +42 -7
- 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 +208 -0
- data/lib/draper/finders.rb +44 -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 +1 -1
- data/lib/draper/test/test_unit_integration.rb +9 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +6 -13
- data/lib/draper/view_helpers.rb +36 -0
- data/lib/generators/decorator/decorator_generator.rb +1 -1
- data/lib/generators/decorator/templates/decorator.rb +0 -1
- 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 +497 -0
- data/spec/draper/finders_spec.rb +156 -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 +25 -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.ru +4 -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/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/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 +23 -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 +3 -4
- data/spec/integration/integration_spec.rb +33 -0
- data/{performance → spec/performance}/active_record.rb +0 -0
- data/{performance/bechmark.rb → spec/performance/benchmark.rb} +0 -0
- data/{performance → spec/performance}/decorators.rb +2 -4
- data/{performance → spec/performance}/models.rb +0 -0
- data/spec/spec_helper.rb +20 -41
- 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 +19 -0
- data/spec/support/{samples → decorators}/products_decorator.rb +1 -1
- data/spec/support/decorators/some_thing_decorator.rb +2 -0
- data/spec/support/{samples → decorators}/specific_product_decorator.rb +0 -2
- data/spec/support/{samples → decorators}/widget_decorator.rb +0 -0
- 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 +13 -2
- data/spec/support/models/some_thing.rb +5 -0
- data/spec/support/models/uninferrable_decorator_model.rb +3 -0
- data/spec/support/{samples → models}/widget.rb +0 -0
- metadata +185 -68
- data/lib/draper/active_model_support.rb +0 -27
- data/lib/draper/base.rb +0 -312
- data/lib/draper/decorated_enumerable_proxy.rb +0 -90
- data/lib/draper/model_support.rb +0 -25
- data/lib/draper/rspec_integration.rb +0 -2
- data/lib/draper/system.rb +0 -18
- data/spec/draper/base_spec.rb +0 -873
- data/spec/draper/decorated_enumerable_proxy_spec.rb +0 -45
- data/spec/draper/model_support_spec.rb +0 -48
- 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/enumerable_proxy.rb +0 -3
- 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
@@ -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 == '<script>danger</script>'
|
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
|