draper 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +140 -120
- data/Gemfile +5 -3
- data/Guardfile +22 -1
- data/README.md +4 -3
- data/draper.gemspec +3 -1
- data/lib/draper.rb +6 -0
- data/lib/draper/automatic_delegation.rb +8 -2
- data/lib/draper/collection_decorator.rb +17 -28
- data/lib/draper/decoratable.rb +6 -3
- data/lib/draper/decoratable/equality.rb +15 -3
- data/lib/draper/decorated_association.rb +11 -50
- data/lib/draper/decorates_assigned.rb +44 -0
- data/lib/draper/decorator.rb +19 -6
- data/lib/draper/factory.rb +87 -0
- data/lib/draper/helper_proxy.rb +2 -0
- data/lib/draper/railtie.rb +8 -2
- data/lib/draper/version.rb +1 -1
- data/lib/generators/decorator/decorator_generator.rb +9 -9
- data/spec/draper/collection_decorator_spec.rb +48 -28
- data/spec/draper/decoratable_spec.rb +13 -4
- data/spec/draper/decorated_association_spec.rb +53 -114
- data/spec/draper/decorates_assigned_spec.rb +71 -0
- data/spec/draper/decorator_spec.rb +58 -8
- data/spec/draper/factory_spec.rb +238 -0
- data/spec/draper/helper_proxy_spec.rb +11 -0
- data/spec/draper/lazy_helpers_spec.rb +21 -0
- data/spec/dummy/app/controllers/posts_controller.rb +3 -1
- data/spec/dummy/app/decorators/mongoid_post_decorator.rb +2 -0
- data/spec/dummy/app/views/posts/show.html.erb +1 -1
- data/spec/dummy/config/application.rb +1 -0
- data/spec/dummy/spec/decorators/active_model_serializers_spec.rb +11 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +12 -0
- data/spec/dummy/spec/models/mongoid_post_spec.rb +2 -4
- data/spec/dummy/spec/models/post_spec.rb +2 -10
- data/spec/dummy/spec/shared_examples/decoratable.rb +24 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +83 -91
- data/spec/spec_helper.rb +1 -0
- metadata +50 -43
data/lib/draper/version.rb
CHANGED
@@ -2,9 +2,9 @@ module Rails
|
|
2
2
|
module Generators
|
3
3
|
class DecoratorGenerator < NamedBase
|
4
4
|
source_root File.expand_path("../templates", __FILE__)
|
5
|
-
check_class_collision :
|
5
|
+
check_class_collision suffix: "Decorator"
|
6
6
|
|
7
|
-
class_option :parent, :
|
7
|
+
class_option :parent, type: :string, desc: "The parent class for the generated decorator"
|
8
8
|
|
9
9
|
def create_decorator_file
|
10
10
|
template 'decorator.rb', File.join('app/decorators', class_path, "#{file_name}_decorator.rb")
|
@@ -15,16 +15,16 @@ module Rails
|
|
15
15
|
private
|
16
16
|
|
17
17
|
def parent_class_name
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
options.fetch("parent") do
|
19
|
+
begin
|
20
|
+
require 'application_decorator'
|
21
|
+
ApplicationDecorator
|
22
|
+
rescue LoadError
|
23
|
+
"Draper::Decorator"
|
24
|
+
end
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
28
|
# Rails 3.0.X compatibility, stolen from https://github.com/jnunemaker/mongomapper/pull/385/files#L1R32
|
29
29
|
unless methods.include?(:module_namespacing)
|
30
30
|
def module_namespacing(&block)
|
@@ -89,39 +89,19 @@ module Draper
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
context "when the
|
93
|
-
|
94
|
-
|
95
|
-
decorator = ProductsDecorator.new([Product.new], with: OtherDecorator)
|
92
|
+
context "when the :with option was given" do
|
93
|
+
it "uses the :with option" do
|
94
|
+
decorator = CollectionDecorator.new([Product.new], with: OtherDecorator).first
|
96
95
|
|
97
|
-
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
context "when the :with option was not given" do
|
102
|
-
it "infers the item decorator from the collection decorator" do
|
103
|
-
decorator = ProductsDecorator.new([Product.new])
|
104
|
-
|
105
|
-
expect(*decorator).to be_decorated_with ProductDecorator
|
106
|
-
end
|
96
|
+
expect(decorator).to be_decorated_with OtherDecorator
|
107
97
|
end
|
108
98
|
end
|
109
99
|
|
110
|
-
context "when the
|
111
|
-
|
112
|
-
|
113
|
-
decorator = CollectionDecorator.new([Product.new], with: OtherDecorator)
|
114
|
-
|
115
|
-
expect(*decorator).to be_decorated_with OtherDecorator
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context "when the :with option was not given" do
|
120
|
-
it "infers the item decorator from each item" do
|
121
|
-
decorator = CollectionDecorator.new([double(decorate: :inferred_decorator)])
|
100
|
+
context "when the :with option was not given" do
|
101
|
+
it "infers the item decorator from each item" do
|
102
|
+
decorator = CollectionDecorator.new([double(decorate: :inferred_decorator)]).first
|
122
103
|
|
123
|
-
|
124
|
-
end
|
104
|
+
expect(decorator).to be :inferred_decorator
|
125
105
|
end
|
126
106
|
end
|
127
107
|
end
|
@@ -255,5 +235,45 @@ module Draper
|
|
255
235
|
end
|
256
236
|
end
|
257
237
|
|
238
|
+
describe '#decorated?' do
|
239
|
+
it 'returns true' do
|
240
|
+
decorator = ProductsDecorator.new([Product.new])
|
241
|
+
|
242
|
+
expect(decorator).to be_decorated
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe '#decorated_with?' do
|
247
|
+
it "checks if a decorator has been applied to a collection" do
|
248
|
+
decorator = ProductsDecorator.new([Product.new])
|
249
|
+
|
250
|
+
expect(decorator).to be_decorated_with ProductsDecorator
|
251
|
+
expect(decorator).not_to be_decorated_with OtherDecorator
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe '#kind_of?' do
|
256
|
+
it 'asks the kind of its decorated collection' do
|
257
|
+
decorator = ProductsDecorator.new([])
|
258
|
+
decorator.decorated_collection.should_receive(:kind_of?).with(Array).and_return("true")
|
259
|
+
expect(decorator.kind_of?(Array)).to eq "true"
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'when asking the underlying collection returns false' do
|
263
|
+
it 'asks the CollectionDecorator instance itself' do
|
264
|
+
decorator = ProductsDecorator.new([])
|
265
|
+
decorator.decorated_collection.stub(:kind_of?).with(::Draper::CollectionDecorator).and_return(false)
|
266
|
+
expect(decorator.kind_of?(::Draper::CollectionDecorator)).to be true
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe '#is_a?' do
|
272
|
+
it 'aliases to #kind_of?' do
|
273
|
+
decorator = ProductsDecorator.new([])
|
274
|
+
expect(decorator.method(:kind_of?)).to eq decorator.method(:is_a?)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
258
278
|
end
|
259
279
|
end
|
@@ -108,17 +108,19 @@ module Draper
|
|
108
108
|
end
|
109
109
|
|
110
110
|
describe ".decorate" do
|
111
|
+
let(:scoping_method) { Rails::VERSION::MAJOR >= 4 ? :all : :scoped }
|
112
|
+
|
111
113
|
it "calls #decorate_collection on .decorator_class" do
|
112
114
|
scoped = [Product.new]
|
113
|
-
Product.stub
|
115
|
+
Product.stub scoping_method => scoped
|
114
116
|
|
115
|
-
Product.decorator_class.should_receive(:decorate_collection).with(scoped,
|
117
|
+
Product.decorator_class.should_receive(:decorate_collection).with(scoped, with: nil).and_return(:decorated_collection)
|
116
118
|
expect(Product.decorate).to be :decorated_collection
|
117
119
|
end
|
118
120
|
|
119
121
|
it "accepts options" do
|
120
|
-
options = {context: {some: "context"}}
|
121
|
-
Product.stub
|
122
|
+
options = {with: ProductDecorator, context: {some: "context"}}
|
123
|
+
Product.stub scoping_method => []
|
122
124
|
|
123
125
|
Product.decorator_class.should_receive(:decorate_collection).with([], options)
|
124
126
|
Product.decorate(options)
|
@@ -161,6 +163,13 @@ module Draper
|
|
161
163
|
expect{Model.decorator_class}.to raise_error UninferrableDecoratorError
|
162
164
|
end
|
163
165
|
end
|
166
|
+
|
167
|
+
context "when an unrelated NameError is thrown" do
|
168
|
+
it "re-raises that error" do
|
169
|
+
String.any_instance.stub(:constantize).and_return{Draper::Base}
|
170
|
+
expect{Product.decorator_class}.to raise_error NameError, /Draper::Base/
|
171
|
+
end
|
172
|
+
end
|
164
173
|
end
|
165
174
|
|
166
175
|
end
|
@@ -4,139 +4,78 @@ module Draper
|
|
4
4
|
describe DecoratedAssociation do
|
5
5
|
|
6
6
|
describe "#initialize" do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
expect{DecoratedAssociation.new(Decorator.new(Model.new), :association, valid_options)}.not_to raise_error
|
11
|
-
end
|
12
|
-
|
13
|
-
it "raises error on invalid options" do
|
14
|
-
expect{DecoratedAssociation.new(Decorator.new(Model.new), :association, foo: "bar")}.to raise_error ArgumentError, /Unknown key/
|
15
|
-
end
|
7
|
+
it "accepts valid options" do
|
8
|
+
valid_options = {with: Decorator, scope: :foo, context: {}}
|
9
|
+
expect{DecoratedAssociation.new(Decorator.new(Model.new), :association, valid_options)}.not_to raise_error
|
16
10
|
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe "#call" do
|
20
|
-
let(:context) { {some: "context"} }
|
21
|
-
let(:options) { {} }
|
22
11
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
DecoratedAssociation.new(owner, :association, options).tap do |decorated_association|
|
27
|
-
decorated_association.stub context: context
|
28
|
-
end
|
12
|
+
it "rejects invalid options" do
|
13
|
+
expect{DecoratedAssociation.new(Decorator.new(Model.new), :association, foo: "bar")}.to raise_error ArgumentError, /Unknown key/
|
29
14
|
end
|
30
15
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
context "when :with option was given" do
|
35
|
-
let(:options) { {with: Decorator} }
|
16
|
+
it "creates a factory" do
|
17
|
+
options = {with: Decorator, context: {foo: "bar"}}
|
36
18
|
|
37
|
-
|
38
|
-
|
39
|
-
expect(decorated_association.call).to be :decorated
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context "when :with option was not given" do
|
44
|
-
it "infers the decorator" do
|
45
|
-
associated.stub decorator_class: OtherDecorator
|
46
|
-
|
47
|
-
OtherDecorator.should_receive(:decorate).with(associated, context: context).and_return(:decorated)
|
48
|
-
expect(decorated_association.call).to be :decorated
|
49
|
-
end
|
50
|
-
end
|
19
|
+
Factory.should_receive(:new).with(options)
|
20
|
+
DecoratedAssociation.new(double, :association, options)
|
51
21
|
end
|
52
22
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
let(:options) { {with: ProductsDecorator} }
|
58
|
-
|
59
|
-
it "uses the specified decorator" do
|
60
|
-
ProductsDecorator.should_receive(:decorate).with(associated, context: context).and_return(:decorated_collection)
|
61
|
-
expect(decorated_association.call).to be :decorated_collection
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context "when :with option is a singular decorator" do
|
66
|
-
let(:options) { {with: ProductDecorator} }
|
67
|
-
|
68
|
-
it "uses a CollectionDecorator of the specified decorator" do
|
69
|
-
ProductDecorator.should_receive(:decorate_collection).with(associated, context: context).and_return(:decorated_collection)
|
70
|
-
expect(decorated_association.call).to be :decorated_collection
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
context "when :with option was not given" do
|
75
|
-
context "when the collection itself is decoratable" do
|
76
|
-
before { associated.stub decorator_class: ProductsDecorator }
|
77
|
-
|
78
|
-
it "infers the decorator" do
|
79
|
-
ProductsDecorator.should_receive(:decorate).with(associated, context: context).and_return(:decorated_collection)
|
80
|
-
expect(decorated_association.call).to be :decorated_collection
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
context "when the collection is not decoratable" do
|
85
|
-
it "uses a CollectionDecorator of inferred decorators" do
|
86
|
-
CollectionDecorator.should_receive(:decorate).with(associated, context: context).and_return(:decorated_collection)
|
87
|
-
expect(decorated_association.call).to be :decorated_collection
|
88
|
-
end
|
89
|
-
end
|
23
|
+
describe ":with option" do
|
24
|
+
it "defaults to nil" do
|
25
|
+
Factory.should_receive(:new).with(with: nil, context: anything())
|
26
|
+
DecoratedAssociation.new(double, :association, {})
|
90
27
|
end
|
91
28
|
end
|
92
29
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
expect(decorated_association.call.source).to be scoped
|
30
|
+
describe ":context option" do
|
31
|
+
it "defaults to the identity function" do
|
32
|
+
Factory.should_receive(:new).with do |options|
|
33
|
+
options[:context].call(:anything) == :anything
|
34
|
+
end
|
35
|
+
DecoratedAssociation.new(double, :association, {})
|
100
36
|
end
|
101
37
|
end
|
102
38
|
end
|
103
39
|
|
104
|
-
describe "#
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
40
|
+
describe "#call" do
|
41
|
+
it "calls the factory" do
|
42
|
+
factory = double
|
43
|
+
Factory.stub new: factory
|
44
|
+
associated = double
|
45
|
+
owner_context = {foo: "bar"}
|
46
|
+
source = double(association: associated)
|
47
|
+
owner = double(source: source, context: owner_context)
|
48
|
+
decorated_association = DecoratedAssociation.new(owner, :association, {})
|
49
|
+
decorated = double
|
50
|
+
|
51
|
+
factory.should_receive(:decorate).with(associated, context_args: owner_context).and_return(decorated)
|
52
|
+
expect(decorated_association.call).to be decorated
|
110
53
|
end
|
111
54
|
|
112
|
-
|
113
|
-
|
55
|
+
it "memoizes" do
|
56
|
+
factory = double
|
57
|
+
Factory.stub new: factory
|
58
|
+
owner = double(source: double(association: double), context: {})
|
59
|
+
decorated_association = DecoratedAssociation.new(owner, :association, {})
|
60
|
+
decorated = double
|
114
61
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
it "calls it with the owner's context" do
|
119
|
-
context.should_receive(:call).with(owner_context)
|
120
|
-
decorated_association.context
|
121
|
-
end
|
122
|
-
|
123
|
-
it "returns the lambda's return value" do
|
124
|
-
expect(decorated_association.context).to be :dynamic_context
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context "and is not callable" do
|
129
|
-
let(:context) { {other: "context"} }
|
130
|
-
|
131
|
-
it "returns the specified value" do
|
132
|
-
expect(decorated_association.context).to be context
|
133
|
-
end
|
134
|
-
end
|
62
|
+
factory.should_receive(:decorate).once.and_return(decorated)
|
63
|
+
expect(decorated_association.call).to be decorated
|
64
|
+
expect(decorated_association.call).to be decorated
|
135
65
|
end
|
136
66
|
|
137
|
-
context "when :
|
138
|
-
it "
|
139
|
-
|
67
|
+
context "when the :scope option was given" do
|
68
|
+
it "applies the scope before decoration" do
|
69
|
+
factory = double
|
70
|
+
Factory.stub new: factory
|
71
|
+
scoped = double
|
72
|
+
source = double(association: double(applied_scope: scoped))
|
73
|
+
owner = double(source: source, context: {})
|
74
|
+
decorated_association = DecoratedAssociation.new(owner, :association, scope: :applied_scope)
|
75
|
+
decorated = double
|
76
|
+
|
77
|
+
factory.should_receive(:decorate).with(scoped, anything()).and_return(decorated)
|
78
|
+
expect(decorated_association.call).to be decorated
|
140
79
|
end
|
141
80
|
end
|
142
81
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Draper
|
4
|
+
describe DecoratesAssigned do
|
5
|
+
let(:controller_class) do
|
6
|
+
Class.new do
|
7
|
+
extend DecoratesAssigned
|
8
|
+
|
9
|
+
def self.helper_method(method)
|
10
|
+
helper_methods << method
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.helper_methods
|
14
|
+
@helper_methods ||= []
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".decorates_assigned" do
|
20
|
+
it "adds helper methods" do
|
21
|
+
controller_class.decorates_assigned :article, :author
|
22
|
+
|
23
|
+
expect(controller_class.instance_methods).to include :article
|
24
|
+
expect(controller_class.instance_methods).to include :author
|
25
|
+
|
26
|
+
expect(controller_class.helper_methods).to include :article
|
27
|
+
expect(controller_class.helper_methods).to include :author
|
28
|
+
end
|
29
|
+
|
30
|
+
it "creates a factory" do
|
31
|
+
Factory.should_receive(:new).once
|
32
|
+
controller_class.decorates_assigned :article, :author
|
33
|
+
end
|
34
|
+
|
35
|
+
it "passes options to the factory" do
|
36
|
+
options = {foo: "bar"}
|
37
|
+
|
38
|
+
Factory.should_receive(:new).with(options)
|
39
|
+
controller_class.decorates_assigned :article, :author, options
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "the generated method" do
|
43
|
+
it "decorates the instance variable" do
|
44
|
+
source = double
|
45
|
+
factory = double
|
46
|
+
Factory.stub new: factory
|
47
|
+
|
48
|
+
controller_class.decorates_assigned :article
|
49
|
+
controller = controller_class.new
|
50
|
+
controller.instance_variable_set "@article", source
|
51
|
+
|
52
|
+
factory.should_receive(:decorate).with(source, context_args: controller).and_return(:decorated)
|
53
|
+
expect(controller.article).to be :decorated
|
54
|
+
end
|
55
|
+
|
56
|
+
it "memoizes" do
|
57
|
+
factory = double
|
58
|
+
Factory.stub new: factory
|
59
|
+
|
60
|
+
controller_class.decorates_assigned :article
|
61
|
+
controller = controller_class.new
|
62
|
+
|
63
|
+
factory.should_receive(:decorate).once
|
64
|
+
controller.article
|
65
|
+
controller.article
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -145,6 +145,13 @@ module Draper
|
|
145
145
|
ProductDecorator.decorate_collection([], options)
|
146
146
|
end
|
147
147
|
end
|
148
|
+
|
149
|
+
context "when a NameError is thrown" do
|
150
|
+
it "re-raises that error" do
|
151
|
+
String.any_instance.stub(:constantize).and_return{Draper::DecoratedEnumerableProxy}
|
152
|
+
expect{ProductDecorator.decorate_collection([])}.to raise_error NameError, /Draper::DecoratedEnumerableProxy/
|
153
|
+
end
|
154
|
+
end
|
148
155
|
end
|
149
156
|
|
150
157
|
describe ".decorates" do
|
@@ -170,20 +177,23 @@ module Draper
|
|
170
177
|
end
|
171
178
|
|
172
179
|
describe ".source_class" do
|
180
|
+
protect_class ProductDecorator
|
181
|
+
protect_class Namespaced::ProductDecorator
|
182
|
+
|
173
183
|
context "when not set by .decorates" do
|
174
|
-
it "raises an
|
184
|
+
it "raises an UninferrableSourceError for a so-named 'Decorator'" do
|
175
185
|
expect{Decorator.source_class}.to raise_error UninferrableSourceError
|
176
186
|
end
|
177
187
|
|
178
|
-
it "raises an
|
188
|
+
it "raises an UninferrableSourceError for anonymous decorators" do
|
179
189
|
expect{Class.new(Decorator).source_class}.to raise_error UninferrableSourceError
|
180
190
|
end
|
181
191
|
|
182
|
-
it "raises an
|
192
|
+
it "raises an UninferrableSourceError for a decorator without a model" do
|
183
193
|
expect{OtherDecorator.source_class}.to raise_error UninferrableSourceError
|
184
194
|
end
|
185
195
|
|
186
|
-
it "raises an
|
196
|
+
it "raises an UninferrableSourceError for other naming conventions" do
|
187
197
|
expect{ProductPresenter.source_class}.to raise_error UninferrableSourceError
|
188
198
|
end
|
189
199
|
|
@@ -194,6 +204,13 @@ module Draper
|
|
194
204
|
it "infers namespaced sources" do
|
195
205
|
expect(Namespaced::ProductDecorator.source_class).to be Namespaced::Product
|
196
206
|
end
|
207
|
+
|
208
|
+
context "when an unrelated NameError is thrown" do
|
209
|
+
it "re-raises that error" do
|
210
|
+
String.any_instance.stub(:constantize).and_return{SomethingThatDoesntExist}
|
211
|
+
expect{ProductDecorator.source_class}.to raise_error NameError, /SomethingThatDoesntExist/
|
212
|
+
end
|
213
|
+
end
|
197
214
|
end
|
198
215
|
end
|
199
216
|
|
@@ -345,6 +362,15 @@ module Draper
|
|
345
362
|
end
|
346
363
|
end
|
347
364
|
|
365
|
+
describe "#attributes" do
|
366
|
+
it "returns only the source's attributes that are implemented by the decorator" do
|
367
|
+
decorator = Decorator.new(double(attributes: {foo: "bar", baz: "qux"}))
|
368
|
+
decorator.stub(:foo)
|
369
|
+
|
370
|
+
expect(decorator.attributes).to eq({foo: "bar"})
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
348
374
|
describe ".model_name" do
|
349
375
|
it "delegates to the source class" do
|
350
376
|
Decorator.stub source_class: double(model_name: :delegated)
|
@@ -354,13 +380,18 @@ module Draper
|
|
354
380
|
end
|
355
381
|
|
356
382
|
describe "#==" do
|
357
|
-
it "
|
383
|
+
it "works for a source that does not include Decoratable" do
|
358
384
|
source = Object.new
|
359
385
|
decorator = Decorator.new(source)
|
360
386
|
|
361
|
-
expect(
|
362
|
-
|
363
|
-
|
387
|
+
expect(decorator).to eq Decorator.new(source)
|
388
|
+
end
|
389
|
+
|
390
|
+
it "works for a multiply-decorated source that does not include Decoratable" do
|
391
|
+
source = Object.new
|
392
|
+
decorator = Decorator.new(source)
|
393
|
+
|
394
|
+
expect(decorator).to eq ProductDecorator.new(Decorator.new(source))
|
364
395
|
end
|
365
396
|
|
366
397
|
it "is true when source #== is true" do
|
@@ -554,6 +585,25 @@ module Draper
|
|
554
585
|
end
|
555
586
|
end
|
556
587
|
end
|
588
|
+
|
589
|
+
describe "#respond_to_missing?" do
|
590
|
+
it "allows #method to be called on delegated methods" do
|
591
|
+
source = Class.new{def hello_world; end}.new
|
592
|
+
decorator = Decorator.new(source)
|
593
|
+
|
594
|
+
expect { decorator.method(:hello_world) }.not_to raise_error NameError
|
595
|
+
expect(decorator.method(:hello_world)).not_to be_nil
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
describe ".respond_to_missing?" do
|
600
|
+
it "allows .method to be called on delegated class methods" do
|
601
|
+
Decorator.stub source_class: double(hello_world: :delegated)
|
602
|
+
|
603
|
+
expect { Decorator.method(:hello_world) }.not_to raise_error NameError
|
604
|
+
expect(Decorator.method(:hello_world)).not_to be_nil
|
605
|
+
end
|
606
|
+
end
|
557
607
|
end
|
558
608
|
|
559
609
|
describe "class spoofing" do
|