draper 1.0.0 → 1.1.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.
- data/CHANGELOG.md +3 -0
- data/CONTRIBUTING.md +5 -1
- data/Gemfile +23 -9
- data/README.md +144 -52
- data/Rakefile +1 -0
- data/draper.gemspec +1 -1
- data/lib/draper.rb +9 -6
- data/lib/draper/decoratable.rb +3 -7
- data/lib/draper/decoratable/equality.rb +14 -0
- data/lib/draper/decorator.rb +4 -7
- data/lib/draper/helper_proxy.rb +22 -3
- data/lib/draper/test/devise_helper.rb +18 -22
- data/lib/draper/test/rspec_integration.rb +4 -0
- data/lib/draper/test_case.rb +20 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +75 -13
- data/lib/draper/view_context/build_strategy.rb +48 -0
- data/lib/draper/view_helpers.rb +2 -2
- data/spec/draper/collection_decorator_spec.rb +169 -196
- data/spec/draper/decoratable/equality_spec.rb +10 -0
- data/spec/draper/decoratable_spec.rb +107 -132
- data/spec/draper/decorated_association_spec.rb +99 -96
- data/spec/draper/decorator_spec.rb +408 -434
- data/spec/draper/finders_spec.rb +160 -126
- data/spec/draper/helper_proxy_spec.rb +38 -8
- data/spec/draper/view_context/build_strategy_spec.rb +116 -0
- data/spec/draper/view_context_spec.rb +154 -0
- data/spec/draper/view_helpers_spec.rb +4 -37
- data/spec/dummy/app/controllers/posts_controller.rb +7 -0
- data/spec/dummy/app/decorators/post_decorator.rb +26 -2
- data/spec/dummy/app/helpers/application_helper.rb +3 -0
- data/spec/dummy/app/mailers/post_mailer.rb +10 -0
- data/spec/dummy/app/models/admin.rb +5 -0
- data/spec/dummy/app/models/mongoid_post.rb +5 -0
- data/spec/dummy/app/models/user.rb +5 -0
- data/spec/dummy/app/views/posts/_post.html.erb +15 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/config/application.rb +9 -3
- data/spec/dummy/config/boot.rb +2 -7
- data/spec/dummy/config/environments/development.rb +2 -3
- data/spec/dummy/config/environments/production.rb +2 -0
- data/spec/dummy/config/environments/test.rb +3 -4
- data/spec/dummy/config/initializers/secret_token.rb +1 -0
- data/spec/dummy/config/mongoid.yml +80 -0
- data/spec/dummy/config/routes.rb +2 -0
- data/spec/dummy/fast_spec/post_decorator_spec.rb +38 -0
- data/spec/dummy/lib/tasks/test.rake +11 -5
- data/spec/dummy/spec/decorators/devise_spec.rb +64 -0
- data/spec/dummy/spec/decorators/helpers_spec.rb +21 -0
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +26 -6
- data/spec/dummy/spec/decorators/spec_type_spec.rb +7 -0
- data/spec/dummy/spec/decorators/view_context_spec.rb +22 -0
- data/spec/dummy/spec/mailers/post_mailer_spec.rb +10 -6
- data/spec/dummy/spec/models/mongoid_post_spec.rb +10 -0
- data/spec/dummy/spec/models/post_spec.rb +5 -5
- data/spec/dummy/spec/spec_helper.rb +1 -0
- data/spec/dummy/test/decorators/minitest/devise_test.rb +64 -0
- data/spec/dummy/test/decorators/minitest/helpers_test.rb +21 -0
- data/spec/dummy/{mini_test/mini_test_integration_test.rb → test/decorators/minitest/spec_type_test.rb} +9 -3
- data/spec/dummy/test/decorators/minitest/view_context_test.rb +24 -0
- data/spec/dummy/test/decorators/test_unit/devise_test.rb +64 -0
- data/spec/dummy/test/decorators/test_unit/helpers_test.rb +21 -0
- data/spec/dummy/test/decorators/test_unit/view_context_test.rb +24 -0
- data/spec/dummy/test/minitest_helper.rb +4 -0
- data/spec/dummy/test/test_helper.rb +3 -0
- data/spec/generators/decorator/decorator_generator_spec.rb +1 -0
- data/spec/integration/integration_spec.rb +31 -6
- data/spec/spec_helper.rb +32 -25
- data/spec/support/shared_examples/decoratable_equality.rb +40 -0
- data/spec/support/shared_examples/view_helpers.rb +39 -0
- metadata +56 -44
- data/spec/dummy/README.rdoc +0 -261
- data/spec/dummy/spec/decorators/rspec_integration_spec.rb +0 -19
- data/spec/support/action_controller.rb +0 -12
- data/spec/support/active_model.rb +0 -7
- data/spec/support/active_record.rb +0 -9
- data/spec/support/decorators/decorator_with_application_helper.rb +0 -25
- data/spec/support/decorators/namespaced_product_decorator.rb +0 -5
- data/spec/support/decorators/non_active_model_product_decorator.rb +0 -2
- data/spec/support/decorators/product_decorator.rb +0 -23
- data/spec/support/decorators/products_decorator.rb +0 -10
- data/spec/support/decorators/some_thing_decorator.rb +0 -2
- data/spec/support/decorators/specific_product_decorator.rb +0 -2
- data/spec/support/decorators/widget_decorator.rb +0 -2
- data/spec/support/models/namespaced_product.rb +0 -49
- data/spec/support/models/non_active_model_product.rb +0 -3
- data/spec/support/models/product.rb +0 -95
- data/spec/support/models/some_thing.rb +0 -5
- data/spec/support/models/uninferrable_decorator_model.rb +0 -3
- data/spec/support/models/widget.rb +0 -2
@@ -1,192 +1,167 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'support/shared_examples/decoratable_equality'
|
2
3
|
|
3
|
-
|
4
|
-
|
4
|
+
module Draper
|
5
|
+
describe Decoratable do
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
7
|
+
describe "#decorate" do
|
8
|
+
it "returns a decorator for self" do
|
9
|
+
product = Product.new
|
10
|
+
decorator = product.decorate
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
12
|
+
expect(decorator).to be_a ProductDecorator
|
13
|
+
expect(decorator.source).to be product
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
16
|
+
it "accepts context" do
|
17
|
+
context = {some: "context"}
|
18
|
+
decorator = Product.new.decorate(context: context)
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
subject.applied_decorators.should be_empty
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe "#decorated_with?" do
|
29
|
-
it "returns false" do
|
30
|
-
subject.should_not be_decorated_with ProductDecorator
|
31
|
-
end
|
32
|
-
end
|
20
|
+
expect(decorator.context).to be context
|
21
|
+
end
|
33
22
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
end
|
23
|
+
it "uses the #decorator_class" do
|
24
|
+
product = Product.new
|
25
|
+
product.stub decorator_class: OtherDecorator
|
39
26
|
|
40
|
-
|
41
|
-
it "delegates to .decorator_class" do
|
42
|
-
Product.stub(:decorator_class).and_return(WidgetDecorator)
|
43
|
-
product = Product.new
|
44
|
-
product.decorator_class.should be WidgetDecorator
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
describe "#==" do
|
49
|
-
context "with itself" do
|
50
|
-
it "returns true" do
|
51
|
-
(subject == subject).should be_true
|
27
|
+
expect(product.decorate).to be_an_instance_of OtherDecorator
|
52
28
|
end
|
53
29
|
end
|
54
30
|
|
55
|
-
|
56
|
-
it "returns
|
57
|
-
(
|
31
|
+
describe "#applied_decorators" do
|
32
|
+
it "returns an empty list" do
|
33
|
+
expect(Product.new.applied_decorators).to eq []
|
58
34
|
end
|
59
35
|
end
|
60
36
|
|
61
|
-
|
62
|
-
it "returns
|
63
|
-
|
64
|
-
(subject == decorator).should be_true
|
37
|
+
describe "#decorated_with?" do
|
38
|
+
it "returns false" do
|
39
|
+
expect(Product.new).not_to be_decorated_with Decorator
|
65
40
|
end
|
66
41
|
end
|
67
42
|
|
68
|
-
|
43
|
+
describe "#decorated?" do
|
69
44
|
it "returns false" do
|
70
|
-
|
71
|
-
(subject == decorator).should be_false
|
45
|
+
expect(Product.new).not_to be_decorated
|
72
46
|
end
|
73
47
|
end
|
74
|
-
end
|
75
48
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
49
|
+
describe "#decorator_class" do
|
50
|
+
it "delegates to .decorator_class" do
|
51
|
+
product = Product.new
|
52
|
+
|
53
|
+
Product.should_receive(:decorator_class).and_return(:some_decorator)
|
54
|
+
expect(product.decorator_class).to be :some_decorator
|
80
55
|
end
|
81
56
|
end
|
82
57
|
|
83
|
-
|
84
|
-
|
85
|
-
(subject === Product.new).should be_false
|
86
|
-
end
|
58
|
+
describe "#==" do
|
59
|
+
it_behaves_like "decoration-aware #==", Product.new
|
87
60
|
end
|
88
61
|
|
89
|
-
|
90
|
-
it "
|
91
|
-
|
92
|
-
|
62
|
+
describe "#===" do
|
63
|
+
it "is true when #== is true" do
|
64
|
+
product = Product.new
|
65
|
+
|
66
|
+
product.should_receive(:==).and_return(true)
|
67
|
+
expect(product === :anything).to be_true
|
93
68
|
end
|
94
|
-
end
|
95
69
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
(
|
70
|
+
it "is false when #== is false" do
|
71
|
+
product = Product.new
|
72
|
+
|
73
|
+
product.should_receive(:==).and_return(false)
|
74
|
+
expect(product === :anything).to be_false
|
100
75
|
end
|
101
76
|
end
|
102
|
-
end
|
103
77
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
(Product === Product.new).should be_true
|
78
|
+
describe ".====" do
|
79
|
+
it "is true for an instance" do
|
80
|
+
expect(Product === Product.new).to be_true
|
108
81
|
end
|
109
|
-
end
|
110
82
|
|
111
|
-
|
112
|
-
|
113
|
-
(Product === Widget.new).should be_true
|
83
|
+
it "is true for a derived instance" do
|
84
|
+
expect(Product === Class.new(Product).new).to be_true
|
114
85
|
end
|
115
|
-
end
|
116
86
|
|
117
|
-
|
118
|
-
|
119
|
-
(Product === Object.new).should be_false
|
87
|
+
it "is false for an unrelated instance" do
|
88
|
+
expect(Product === Model.new).to be_false
|
120
89
|
end
|
121
|
-
end
|
122
90
|
|
123
|
-
|
124
|
-
it "returns true" do
|
91
|
+
it "is true for a decorated instance" do
|
125
92
|
decorator = double(source: Product.new)
|
126
|
-
|
93
|
+
|
94
|
+
expect(Product === decorator).to be_true
|
127
95
|
end
|
128
|
-
end
|
129
96
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
(Product === decorator).
|
97
|
+
it "is true for a decorated derived instance" do
|
98
|
+
decorator = double(source: Class.new(Product).new)
|
99
|
+
|
100
|
+
expect(Product === decorator).to be_true
|
134
101
|
end
|
135
|
-
end
|
136
102
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
(Product === decorator).
|
103
|
+
it "is false for a decorated unrelated instance" do
|
104
|
+
decorator = double(source: Model.new)
|
105
|
+
|
106
|
+
expect(Product === decorator).to be_false
|
141
107
|
end
|
142
108
|
end
|
143
|
-
end
|
144
109
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
decorator = Product.decorate
|
110
|
+
describe ".decorate" do
|
111
|
+
it "calls #decorate_collection on .decorator_class" do
|
112
|
+
scoped = [Product.new]
|
113
|
+
Product.stub scoped: scoped
|
150
114
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
end
|
115
|
+
Product.decorator_class.should_receive(:decorate_collection).with(scoped, {}).and_return(:decorated_collection)
|
116
|
+
expect(Product.decorate).to be :decorated_collection
|
117
|
+
end
|
155
118
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
end
|
119
|
+
it "accepts options" do
|
120
|
+
options = {context: {some: "context"}}
|
121
|
+
Product.stub scoped: []
|
160
122
|
|
161
|
-
|
162
|
-
|
123
|
+
Product.decorator_class.should_receive(:decorate_collection).with([], options)
|
124
|
+
Product.decorate(options)
|
125
|
+
end
|
163
126
|
end
|
164
|
-
end
|
165
127
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
128
|
+
describe ".decorator_class" do
|
129
|
+
context "for classes" do
|
130
|
+
it "infers the decorator from the class" do
|
131
|
+
expect(Product.decorator_class).to be ProductDecorator
|
132
|
+
end
|
170
133
|
end
|
171
|
-
end
|
172
134
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
135
|
+
context "for ActiveModel classes" do
|
136
|
+
it "infers the decorator from the model name" do
|
137
|
+
Product.stub(:model_name).and_return("Other")
|
138
|
+
|
139
|
+
expect(Product.decorator_class).to be OtherDecorator
|
140
|
+
end
|
177
141
|
end
|
178
|
-
end
|
179
142
|
|
180
|
-
|
181
|
-
|
182
|
-
|
143
|
+
context "in a namespace" do
|
144
|
+
context "for classes" do
|
145
|
+
it "infers the decorator from the class" do
|
146
|
+
expect(Namespaced::Product.decorator_class).to be Namespaced::ProductDecorator
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "for ActiveModel classes" do
|
151
|
+
it "infers the decorator from the model name" do
|
152
|
+
Namespaced::Product.stub(:model_name).and_return("Namespaced::Other")
|
153
|
+
|
154
|
+
expect(Namespaced::Product.decorator_class).to be Namespaced::OtherDecorator
|
155
|
+
end
|
156
|
+
end
|
183
157
|
end
|
184
|
-
end
|
185
158
|
|
186
|
-
|
187
|
-
|
188
|
-
|
159
|
+
context "when the decorator can't be inferred" do
|
160
|
+
it "throws an UninferrableDecoratorError" do
|
161
|
+
expect{Model.decorator_class}.to raise_error UninferrableDecoratorError
|
162
|
+
end
|
189
163
|
end
|
190
164
|
end
|
165
|
+
|
191
166
|
end
|
192
167
|
end
|
@@ -1,142 +1,145 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
it "does not raise error on valid options" do
|
14
|
-
expect { Draper::DecoratedAssociation.new(owner, :association, valid_options) }.to_not raise_error
|
15
|
-
end
|
3
|
+
module Draper
|
4
|
+
describe DecoratedAssociation do
|
5
|
+
|
6
|
+
describe "#initialize" do
|
7
|
+
describe "options validation" do
|
8
|
+
it "does not raise error on valid options" do
|
9
|
+
valid_options = {with: Decorator, scope: :foo, context: {}}
|
10
|
+
expect{DecoratedAssociation.new(Decorator.new(Model.new), :association, valid_options)}.not_to raise_error
|
11
|
+
end
|
16
12
|
|
17
|
-
|
18
|
-
|
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
|
19
16
|
end
|
20
17
|
end
|
21
|
-
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
describe "#call" do
|
20
|
+
let(:context) { {some: "context"} }
|
21
|
+
let(:options) { {} }
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
decorated_association.stub context: context
|
30
|
-
end
|
23
|
+
let(:decorated_association) do
|
24
|
+
owner = double(context: nil, source: double(association: associated))
|
31
25
|
|
32
|
-
|
33
|
-
|
34
|
-
|
26
|
+
DecoratedAssociation.new(owner, :association, options).tap do |decorated_association|
|
27
|
+
decorated_association.stub context: context
|
28
|
+
end
|
29
|
+
end
|
35
30
|
|
36
|
-
context "
|
37
|
-
let(:
|
31
|
+
context "for a singular association" do
|
32
|
+
let(:associated) { Model.new }
|
38
33
|
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
context "when :with option was given" do
|
35
|
+
let(:options) { {with: Decorator} }
|
36
|
+
|
37
|
+
it "uses the specified decorator" do
|
38
|
+
Decorator.should_receive(:decorate).with(associated, context: context).and_return(:decorated)
|
39
|
+
expect(decorated_association.call).to be :decorated
|
40
|
+
end
|
42
41
|
end
|
43
|
-
end
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
50
|
end
|
51
51
|
end
|
52
|
-
end
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
let(:collection_decorator) { ProductsDecorator }
|
53
|
+
context "for a collection association" do
|
54
|
+
let(:associated) { [] }
|
57
55
|
|
58
|
-
|
59
|
-
|
56
|
+
context "when :with option is a collection decorator" do
|
57
|
+
let(:options) { {with: ProductsDecorator} }
|
60
58
|
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
64
63
|
end
|
65
|
-
end
|
66
64
|
|
67
|
-
|
68
|
-
|
69
|
-
let(:decorator) { SpecificProductDecorator }
|
65
|
+
context "when :with option is a singular decorator" do
|
66
|
+
let(:options) { {with: ProductDecorator} }
|
70
67
|
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
74
72
|
end
|
75
|
-
end
|
76
73
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
83
82
|
end
|
84
|
-
end
|
85
83
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
90
89
|
end
|
91
90
|
end
|
92
91
|
end
|
93
|
-
end
|
94
92
|
|
95
|
-
|
96
|
-
|
97
|
-
|
93
|
+
context "with a scope" do
|
94
|
+
let(:options) { {scope: :foo} }
|
95
|
+
let(:associated) { double(foo: scoped) }
|
96
|
+
let(:scoped) { Product.new }
|
98
97
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
decorated_association.call.should == scoped
|
98
|
+
it "applies the scope before decoration" do
|
99
|
+
expect(decorated_association.call.source).to be scoped
|
100
|
+
end
|
103
101
|
end
|
104
102
|
end
|
105
|
-
end
|
106
103
|
|
107
|
-
|
108
|
-
|
104
|
+
describe "#context" do
|
105
|
+
let(:owner_context) { {some: "context"} }
|
106
|
+
let(:options) { {} }
|
107
|
+
let(:decorated_association) do
|
108
|
+
owner = double(context: owner_context)
|
109
|
+
DecoratedAssociation.new(owner, :association, options)
|
110
|
+
end
|
109
111
|
|
110
|
-
|
111
|
-
|
112
|
+
context "when :context option was given" do
|
113
|
+
let(:options) { {context: context} }
|
112
114
|
|
113
|
-
|
114
|
-
|
115
|
+
context "and is callable" do
|
116
|
+
let(:context) { ->(*){ :dynamic_context } }
|
115
117
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
118
|
+
it "calls it with the owner's context" do
|
119
|
+
context.should_receive(:call).with(owner_context)
|
120
|
+
decorated_association.context
|
121
|
+
end
|
120
122
|
|
121
|
-
|
122
|
-
|
123
|
+
it "returns the lambda's return value" do
|
124
|
+
expect(decorated_association.context).to be :dynamic_context
|
125
|
+
end
|
123
126
|
end
|
124
|
-
end
|
125
127
|
|
126
|
-
|
127
|
-
|
128
|
+
context "and is not callable" do
|
129
|
+
let(:context) { {other: "context"} }
|
128
130
|
|
129
|
-
|
130
|
-
|
131
|
+
it "returns the specified value" do
|
132
|
+
expect(decorated_association.context).to be context
|
133
|
+
end
|
131
134
|
end
|
132
135
|
end
|
133
|
-
end
|
134
136
|
|
135
|
-
|
136
|
-
|
137
|
-
|
137
|
+
context "when :context option was not given" do
|
138
|
+
it "returns the owner's context" do
|
139
|
+
expect(decorated_association.context).to be owner_context
|
140
|
+
end
|
138
141
|
end
|
139
142
|
end
|
140
|
-
end
|
141
143
|
|
144
|
+
end
|
142
145
|
end
|