draper 3.0.0.pre1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +16 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +24 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +3 -2
- data/CHANGELOG.md +20 -0
- data/Guardfile +5 -5
- data/README.md +27 -5
- data/Rakefile +1 -2
- data/draper.gemspec +1 -0
- data/lib/draper.rb +7 -2
- data/lib/draper/automatic_delegation.rb +5 -3
- data/lib/draper/collection_decorator.rb +1 -11
- data/lib/draper/compatibility/api_only.rb +23 -0
- data/lib/draper/configuration.rb +15 -0
- data/lib/draper/decoratable.rb +2 -2
- data/lib/draper/decorator.rb +4 -12
- data/lib/draper/finders.rb +0 -0
- data/lib/draper/helper_proxy.rb +1 -8
- data/lib/draper/railtie.rb +12 -13
- data/lib/draper/tasks/test.rake +1 -1
- data/lib/draper/test/devise_helper.rb +1 -8
- data/lib/draper/test/minitest_integration.rb +0 -0
- data/lib/draper/test/rspec_integration.rb +0 -0
- data/lib/draper/undecorate.rb +8 -0
- data/lib/draper/version.rb +1 -1
- data/lib/draper/view_context.rb +3 -19
- data/lib/draper/view_context/build_strategy.rb +11 -2
- data/lib/generators/controller_override.rb +2 -2
- data/lib/generators/draper/install_generator.rb +14 -0
- data/lib/generators/draper/templates/application_decorator.rb +8 -0
- data/lib/generators/mini_test/decorator_generator.rb +1 -1
- data/lib/generators/rails/decorator_generator.rb +1 -8
- data/lib/generators/rspec/templates/decorator_spec.rb +1 -1
- data/spec/draper/collection_decorator_spec.rb +11 -26
- data/spec/draper/configuration_spec.rb +25 -0
- data/spec/draper/decoratable_spec.rb +28 -13
- data/spec/draper/decorated_association_spec.rb +9 -9
- data/spec/draper/decorates_assigned_spec.rb +6 -6
- data/spec/draper/decorator_spec.rb +104 -89
- data/spec/draper/draper_spec.rb +24 -0
- data/spec/draper/factory_spec.rb +24 -24
- data/spec/draper/finders_spec.rb +21 -21
- data/spec/draper/helper_proxy_spec.rb +2 -2
- data/spec/draper/lazy_helpers_spec.rb +2 -2
- data/spec/draper/undecorate_chain_spec.rb +20 -0
- data/spec/draper/view_context/build_strategy_spec.rb +26 -10
- data/spec/draper/view_context_spec.rb +49 -21
- data/spec/dummy/app/controllers/base_controller.rb +4 -0
- data/spec/dummy/app/controllers/posts_controller.rb +2 -2
- data/spec/dummy/app/decorators/post_decorator.rb +0 -0
- data/spec/dummy/config/boot.rb +1 -1
- data/spec/dummy/config/initializers/draper.rb +3 -0
- data/spec/dummy/db/schema.rb +4 -4
- data/spec/dummy/fast_spec/post_decorator_spec.rb +1 -1
- data/spec/dummy/lib/tasks/test.rake +1 -1
- data/spec/dummy/spec/decorators/devise_spec.rb +0 -9
- data/spec/dummy/spec/decorators/post_decorator_spec.rb +2 -2
- data/spec/dummy/test/decorators/minitest/devise_test.rb +0 -9
- data/spec/dummy/test/decorators/minitest/view_context_test.rb +3 -3
- data/spec/dummy/test/decorators/test_unit/devise_test.rb +0 -9
- data/spec/generators/controller/controller_generator_spec.rb +3 -3
- data/spec/generators/decorator/decorator_generator_spec.rb +11 -10
- data/spec/generators/install/install_generator_spec.rb +19 -0
- data/spec/spec_helper.rb +4 -3
- data/spec/support/shared_examples/view_helpers.rb +8 -8
- metadata +38 -7
- data/spec/dummy/app/controllers/application_controller.rb +0 -4
data/lib/draper/tasks/test.rake
CHANGED
@@ -1,14 +1,7 @@
|
|
1
1
|
module Draper
|
2
2
|
module DeviseHelper
|
3
3
|
def sign_in(resource_or_scope, resource = nil)
|
4
|
-
scope =
|
5
|
-
Devise::Mapping.find_scope!(resource_or_scope)
|
6
|
-
rescue RuntimeError => e
|
7
|
-
# Draper 1.0 didn't require the mapping to exist
|
8
|
-
ActiveSupport::Deprecation.warn("#{e.message}.\nUse `sign_in :user, mock_user` instead.", caller)
|
9
|
-
:user
|
10
|
-
end
|
11
|
-
|
4
|
+
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
12
5
|
_stub_current_scope scope, resource || resource_or_scope
|
13
6
|
end
|
14
7
|
|
File without changes
|
File without changes
|
data/lib/draper/undecorate.rb
CHANGED
data/lib/draper/version.rb
CHANGED
data/lib/draper/view_context.rb
CHANGED
@@ -20,8 +20,10 @@ module Draper
|
|
20
20
|
RequestStore.store[:current_controller]
|
21
21
|
end
|
22
22
|
|
23
|
-
# Sets the current controller.
|
23
|
+
# Sets the current controller. Clears view context when we are setting
|
24
|
+
# different controller.
|
24
25
|
def self.controller=(controller)
|
26
|
+
clear! if RequestStore.store[:current_controller] != controller
|
25
27
|
RequestStore.store[:current_controller] = controller
|
26
28
|
end
|
27
29
|
|
@@ -82,23 +84,5 @@ module Draper
|
|
82
84
|
def self.build_strategy
|
83
85
|
@build_strategy ||= Draper::ViewContext::BuildStrategy.new(:full)
|
84
86
|
end
|
85
|
-
|
86
|
-
# @deprecated Use {controller} instead.
|
87
|
-
def self.current_controller
|
88
|
-
ActiveSupport::Deprecation.warn("Draper::ViewContext.current_controller is deprecated (use controller instead)", caller)
|
89
|
-
self.controller || ApplicationController.new
|
90
|
-
end
|
91
|
-
|
92
|
-
# @deprecated Use {controller=} instead.
|
93
|
-
def self.current_controller=(controller)
|
94
|
-
ActiveSupport::Deprecation.warn("Draper::ViewContext.current_controller= is deprecated (use controller instead)", caller)
|
95
|
-
self.controller = controller
|
96
|
-
end
|
97
|
-
|
98
|
-
# @deprecated Use {build} instead.
|
99
|
-
def self.build_view_context
|
100
|
-
ActiveSupport::Deprecation.warn("Draper::ViewContext.build_view_context is deprecated (use build instead)", caller)
|
101
|
-
build
|
102
|
-
end
|
103
87
|
end
|
104
88
|
end
|
@@ -37,10 +37,19 @@ module Draper
|
|
37
37
|
attr_reader :block
|
38
38
|
|
39
39
|
def controller
|
40
|
-
|
41
|
-
|
40
|
+
Draper::ViewContext.controller ||= Draper.default_controller.new
|
41
|
+
Draper::ViewContext.controller.tap do |controller|
|
42
|
+
controller.request ||= new_test_request controller if defined?(ActionController::TestRequest)
|
42
43
|
end
|
43
44
|
end
|
45
|
+
|
46
|
+
def new_test_request(controller)
|
47
|
+
is_above_rails_5_1 ? ActionController::TestRequest.create(controller) : ActionController::TestRequest.create
|
48
|
+
end
|
49
|
+
|
50
|
+
def is_above_rails_5_1
|
51
|
+
ActionController::TestRequest.method(:create).parameters.first == [:req, :controller_class]
|
52
|
+
end
|
44
53
|
end
|
45
54
|
|
46
55
|
end
|
@@ -5,13 +5,13 @@ require "rails/generators/rails/scaffold_controller/scaffold_controller_generato
|
|
5
5
|
module Rails
|
6
6
|
module Generators
|
7
7
|
class ControllerGenerator
|
8
|
-
hook_for :decorator, default: true do |generator|
|
8
|
+
hook_for :decorator, type: :boolean, default: true do |generator|
|
9
9
|
invoke generator, [name.singularize]
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
13
|
class ScaffoldControllerGenerator
|
14
|
-
hook_for :decorator, default: true
|
14
|
+
hook_for :decorator, type: :boolean, default: true
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Draper
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
|
6
|
+
desc 'Creates an ApplicationDecorator, if none exists.'
|
7
|
+
|
8
|
+
def create_application_decorator
|
9
|
+
file = 'application_decorator.rb'
|
10
|
+
copy_file file, "app/decorators/#{file}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -7,7 +7,7 @@ module MiniTest
|
|
7
7
|
File.expand_path('../templates', __FILE__)
|
8
8
|
end
|
9
9
|
|
10
|
-
class_option :spec, :
|
10
|
+
class_option :spec, type: :boolean, default: false, desc: "Use MiniTest::Spec DSL"
|
11
11
|
|
12
12
|
check_class_collision suffix: "DecoratorTest"
|
13
13
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Rails
|
2
2
|
module Generators
|
3
|
-
|
3
|
+
class DecoratorGenerator < NamedBase
|
4
4
|
source_root File.expand_path("../templates", __FILE__)
|
5
5
|
check_class_collision suffix: "Decorator"
|
6
6
|
|
@@ -24,13 +24,6 @@ module Rails
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
-
|
28
|
-
# Rails 3.0.X compatibility, stolen from https://github.com/jnunemaker/mongomapper/pull/385/files#L1R32
|
29
|
-
unless methods.include?(:module_namespacing)
|
30
|
-
def module_namespacing
|
31
|
-
yield if block_given?
|
32
|
-
end
|
33
|
-
end
|
34
27
|
end
|
35
28
|
end
|
36
29
|
end
|
@@ -63,7 +63,7 @@ module Draper
|
|
63
63
|
it "does not trigger decoration" do
|
64
64
|
decorator = CollectionDecorator.new([])
|
65
65
|
|
66
|
-
decorator.
|
66
|
+
expect(decorator).not_to receive(:decorated_collection)
|
67
67
|
decorator.context = {other: "context"}
|
68
68
|
end
|
69
69
|
|
@@ -110,37 +110,22 @@ module Draper
|
|
110
110
|
protect_class ProductsDecorator
|
111
111
|
|
112
112
|
it "defaults the :to option to :object" do
|
113
|
-
Object.
|
113
|
+
expect(Object).to receive(:delegate).with(:foo, :bar, to: :object)
|
114
114
|
ProductsDecorator.delegate :foo, :bar
|
115
115
|
end
|
116
116
|
|
117
117
|
it "does not overwrite the :to option if supplied" do
|
118
|
-
Object.
|
118
|
+
expect(Object).to receive(:delegate).with(:foo, :bar, to: :baz)
|
119
119
|
ProductsDecorator.delegate :foo, :bar, to: :baz
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
123
|
describe "#find" do
|
124
|
-
|
125
|
-
|
126
|
-
decorator = CollectionDecorator.new([])
|
127
|
-
|
128
|
-
decorator.decorated_collection.should_receive(:find).and_return(:delegated)
|
129
|
-
expect(decorator.find{|p| p.title == "title"}).to be :delegated
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
context "without a block" do
|
134
|
-
it "decorates object.find" do
|
135
|
-
object = []
|
136
|
-
found = double(decorate: :decorated)
|
137
|
-
decorator = CollectionDecorator.new(object)
|
124
|
+
it "decorates Enumerable#find" do
|
125
|
+
decorator = CollectionDecorator.new([])
|
138
126
|
|
139
|
-
|
140
|
-
|
141
|
-
expect(decorator.find(1)).to be :decorated
|
142
|
-
end
|
143
|
-
end
|
127
|
+
expect(decorator.decorated_collection).to receive(:find).and_return(:delegated)
|
128
|
+
expect(decorator.find{|p| p.title == "title"}).to be :delegated
|
144
129
|
end
|
145
130
|
end
|
146
131
|
|
@@ -149,7 +134,7 @@ module Draper
|
|
149
134
|
it "delegates to the decorated collection" do
|
150
135
|
decorator = CollectionDecorator.new([])
|
151
136
|
|
152
|
-
decorator.decorated_collection.
|
137
|
+
expect(decorator.decorated_collection).to receive(:to_ary).and_return(:delegated)
|
153
138
|
expect(decorator.to_ary).to be :delegated
|
154
139
|
end
|
155
140
|
end
|
@@ -157,7 +142,7 @@ module Draper
|
|
157
142
|
it "delegates array methods to the decorated collection" do
|
158
143
|
decorator = CollectionDecorator.new([])
|
159
144
|
|
160
|
-
decorator.
|
145
|
+
allow(decorator).to receive_message_chain(:decorated_collection, :[]).with(42).and_return(:delegated)
|
161
146
|
expect(decorator[42]).to be :delegated
|
162
147
|
end
|
163
148
|
|
@@ -267,14 +252,14 @@ module Draper
|
|
267
252
|
describe '#kind_of?' do
|
268
253
|
it 'asks the kind of its decorated collection' do
|
269
254
|
decorator = ProductsDecorator.new([])
|
270
|
-
decorator.decorated_collection.
|
255
|
+
expect(decorator.decorated_collection).to receive(:kind_of?).with(Array).and_return("true")
|
271
256
|
expect(decorator.kind_of?(Array)).to eq "true"
|
272
257
|
end
|
273
258
|
|
274
259
|
context 'when asking the underlying collection returns false' do
|
275
260
|
it 'asks the CollectionDecorator instance itself' do
|
276
261
|
decorator = ProductsDecorator.new([])
|
277
|
-
decorator.decorated_collection.
|
262
|
+
allow(decorator.decorated_collection).to receive(:kind_of?).with(::Draper::CollectionDecorator).and_return(false)
|
278
263
|
expect(decorator.kind_of?(::Draper::CollectionDecorator)).to be true
|
279
264
|
end
|
280
265
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Draper
|
4
|
+
RSpec.describe Configuration do
|
5
|
+
it 'yields Draper on configure' do
|
6
|
+
Draper.configure { |config| expect(config).to be Draper }
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'defaults default_controller to ApplicationController' do
|
10
|
+
expect(Draper.default_controller).to be ApplicationController
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'allows customizing default_controller through configure' do
|
14
|
+
default = Draper.default_controller
|
15
|
+
|
16
|
+
Draper.configure do |config|
|
17
|
+
config.default_controller = CustomController
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(Draper.default_controller).to be CustomController
|
21
|
+
|
22
|
+
Draper.default_controller = default
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -22,7 +22,7 @@ module Draper
|
|
22
22
|
|
23
23
|
it "uses the #decorator_class" do
|
24
24
|
product = Product.new
|
25
|
-
product.
|
25
|
+
allow(product).to receive_messages decorator_class: OtherDecorator
|
26
26
|
|
27
27
|
expect(product.decorate).to be_an_instance_of OtherDecorator
|
28
28
|
end
|
@@ -70,7 +70,7 @@ module Draper
|
|
70
70
|
it "delegates to .decorator_class" do
|
71
71
|
product = Product.new
|
72
72
|
|
73
|
-
Product.
|
73
|
+
expect(Product).to receive(:decorator_class).and_return(:some_decorator)
|
74
74
|
expect(product.decorator_class).to be :some_decorator
|
75
75
|
end
|
76
76
|
end
|
@@ -83,14 +83,14 @@ module Draper
|
|
83
83
|
it "is true when #== is true" do
|
84
84
|
product = Product.new
|
85
85
|
|
86
|
-
product.
|
86
|
+
expect(product).to receive(:==).and_return(true)
|
87
87
|
expect(product === :anything).to be_truthy
|
88
88
|
end
|
89
89
|
|
90
90
|
it "is false when #== is false" do
|
91
91
|
product = Product.new
|
92
92
|
|
93
|
-
product.
|
93
|
+
expect(product).to receive(:==).and_return(false)
|
94
94
|
expect(product === :anything).to be_falsey
|
95
95
|
end
|
96
96
|
end
|
@@ -109,19 +109,25 @@ module Draper
|
|
109
109
|
end
|
110
110
|
|
111
111
|
it "is true for a decorated instance" do
|
112
|
-
decorator =
|
112
|
+
decorator = Product.new.decorate
|
113
113
|
|
114
114
|
expect(Product === decorator).to be_truthy
|
115
115
|
end
|
116
116
|
|
117
117
|
it "is true for a decorated derived instance" do
|
118
|
-
decorator =
|
118
|
+
decorator = Class.new(Product).new.decorate
|
119
119
|
|
120
120
|
expect(Product === decorator).to be_truthy
|
121
121
|
end
|
122
122
|
|
123
123
|
it "is false for a decorated unrelated instance" do
|
124
|
-
decorator =
|
124
|
+
decorator = Other.new.decorate
|
125
|
+
|
126
|
+
expect(Product === decorator).to be_falsey
|
127
|
+
end
|
128
|
+
|
129
|
+
it "is false for a non-decorator which happens to respond to object" do
|
130
|
+
decorator = double(object: Product.new)
|
125
131
|
|
126
132
|
expect(Product === decorator).to be_falsey
|
127
133
|
end
|
@@ -130,17 +136,17 @@ module Draper
|
|
130
136
|
describe ".decorate" do
|
131
137
|
it "calls #decorate_collection on .decorator_class" do
|
132
138
|
scoped = [Product.new]
|
133
|
-
Product.
|
139
|
+
allow(Product).to receive(:all).and_return(scoped)
|
134
140
|
|
135
|
-
Product.decorator_class.
|
141
|
+
expect(Product.decorator_class).to receive(:decorate_collection).with(scoped, with: nil).and_return(:decorated_collection)
|
136
142
|
expect(Product.decorate).to be :decorated_collection
|
137
143
|
end
|
138
144
|
|
139
145
|
it "accepts options" do
|
140
146
|
options = {with: ProductDecorator, context: {some: "context"}}
|
141
|
-
Product.
|
147
|
+
allow(Product).to receive(:all).and_return([])
|
142
148
|
|
143
|
-
Product.decorator_class.
|
149
|
+
expect(Product.decorator_class).to receive(:decorate_collection).with([], options)
|
144
150
|
Product.decorate(options)
|
145
151
|
end
|
146
152
|
end
|
@@ -175,13 +181,22 @@ module Draper
|
|
175
181
|
|
176
182
|
context "for ActiveModel classes" do
|
177
183
|
it "infers the decorator from the model name" do
|
178
|
-
Namespaced::Product.
|
184
|
+
allow(Namespaced::Product).to receive(:model_name).and_return("Namespaced::Other")
|
179
185
|
|
180
186
|
expect(Namespaced::Product.decorator_class).to be Namespaced::OtherDecorator
|
181
187
|
end
|
182
188
|
end
|
183
189
|
end
|
184
190
|
|
191
|
+
context "when the decorator contains name error" do
|
192
|
+
it "throws an NameError" do
|
193
|
+
# We imitate ActiveSupport::Autoload behavior here in order to cause lazy NameError exception raising
|
194
|
+
allow_any_instance_of(Module).to receive(:const_missing) { Class.new { any_nonexisting_method_name } }
|
195
|
+
|
196
|
+
expect{Model.decorator_class}.to raise_error { |error| expect(error).to be_an_instance_of(NameError) }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
185
200
|
context "when the decorator can't be inferred" do
|
186
201
|
it "throws an UninferrableDecoratorError" do
|
187
202
|
expect{Model.decorator_class}.to raise_error UninferrableDecoratorError
|
@@ -190,7 +205,7 @@ module Draper
|
|
190
205
|
|
191
206
|
context "when an unrelated NameError is thrown" do
|
192
207
|
it "re-raises that error" do
|
193
|
-
String.
|
208
|
+
allow_any_instance_of(String).to receive(:constantize) { Draper::Base }
|
194
209
|
expect{Product.decorator_class}.to raise_error NameError, /Draper::Base/
|
195
210
|
end
|
196
211
|
end
|
@@ -16,20 +16,20 @@ module Draper
|
|
16
16
|
it "creates a factory" do
|
17
17
|
options = {with: Decorator, context: {foo: "bar"}}
|
18
18
|
|
19
|
-
Factory.
|
19
|
+
expect(Factory).to receive(:new).with(options)
|
20
20
|
DecoratedAssociation.new(double, :association, options)
|
21
21
|
end
|
22
22
|
|
23
23
|
describe ":with option" do
|
24
24
|
it "defaults to nil" do
|
25
|
-
Factory.
|
25
|
+
expect(Factory).to receive(:new).with(with: nil, context: anything())
|
26
26
|
DecoratedAssociation.new(double, :association, {})
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
describe ":context option" do
|
31
31
|
it "defaults to the identity function" do
|
32
|
-
Factory.
|
32
|
+
expect(Factory).to receive(:new) do |options|
|
33
33
|
options[:context].call(:anything) == :anything
|
34
34
|
end
|
35
35
|
DecoratedAssociation.new(double, :association, {})
|
@@ -40,7 +40,7 @@ module Draper
|
|
40
40
|
describe "#call" do
|
41
41
|
it "calls the factory" do
|
42
42
|
factory = double
|
43
|
-
Factory.
|
43
|
+
allow(Factory).to receive_messages(new: factory)
|
44
44
|
associated = double
|
45
45
|
owner_context = {foo: "bar"}
|
46
46
|
object = double(association: associated)
|
@@ -48,18 +48,18 @@ module Draper
|
|
48
48
|
decorated_association = DecoratedAssociation.new(owner, :association, {})
|
49
49
|
decorated = double
|
50
50
|
|
51
|
-
factory.
|
51
|
+
expect(factory).to receive(:decorate).with(associated, context_args: owner_context).and_return(decorated)
|
52
52
|
expect(decorated_association.call).to be decorated
|
53
53
|
end
|
54
54
|
|
55
55
|
it "memoizes" do
|
56
56
|
factory = double
|
57
|
-
Factory.
|
57
|
+
allow(Factory).to receive_messages(new: factory)
|
58
58
|
owner = double(object: double(association: double), context: {})
|
59
59
|
decorated_association = DecoratedAssociation.new(owner, :association, {})
|
60
60
|
decorated = double
|
61
61
|
|
62
|
-
factory.
|
62
|
+
expect(factory).to receive(:decorate).once.and_return(decorated)
|
63
63
|
expect(decorated_association.call).to be decorated
|
64
64
|
expect(decorated_association.call).to be decorated
|
65
65
|
end
|
@@ -67,14 +67,14 @@ module Draper
|
|
67
67
|
context "when the :scope option was given" do
|
68
68
|
it "applies the scope before decoration" do
|
69
69
|
factory = double
|
70
|
-
Factory.
|
70
|
+
allow(Factory).to receive_messages(new: factory)
|
71
71
|
scoped = double
|
72
72
|
object = double(association: double(applied_scope: scoped))
|
73
73
|
owner = double(object: object, context: {})
|
74
74
|
decorated_association = DecoratedAssociation.new(owner, :association, scope: :applied_scope)
|
75
75
|
decorated = double
|
76
76
|
|
77
|
-
factory.
|
77
|
+
expect(factory).to receive(:decorate).with(scoped, anything()).and_return(decorated)
|
78
78
|
expect(decorated_association.call).to be decorated
|
79
79
|
end
|
80
80
|
end
|