draper 1.1.0 → 1.2.0
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.
- 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
|