draper 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|