draper 3.0.0.pre1 → 3.0.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +16 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +24 -0
  4. data/.rubocop.yml +11 -0
  5. data/.travis.yml +3 -2
  6. data/CHANGELOG.md +20 -0
  7. data/Guardfile +5 -5
  8. data/README.md +27 -5
  9. data/Rakefile +1 -2
  10. data/draper.gemspec +1 -0
  11. data/lib/draper.rb +7 -2
  12. data/lib/draper/automatic_delegation.rb +5 -3
  13. data/lib/draper/collection_decorator.rb +1 -11
  14. data/lib/draper/compatibility/api_only.rb +23 -0
  15. data/lib/draper/configuration.rb +15 -0
  16. data/lib/draper/decoratable.rb +2 -2
  17. data/lib/draper/decorator.rb +4 -12
  18. data/lib/draper/finders.rb +0 -0
  19. data/lib/draper/helper_proxy.rb +1 -8
  20. data/lib/draper/railtie.rb +12 -13
  21. data/lib/draper/tasks/test.rake +1 -1
  22. data/lib/draper/test/devise_helper.rb +1 -8
  23. data/lib/draper/test/minitest_integration.rb +0 -0
  24. data/lib/draper/test/rspec_integration.rb +0 -0
  25. data/lib/draper/undecorate.rb +8 -0
  26. data/lib/draper/version.rb +1 -1
  27. data/lib/draper/view_context.rb +3 -19
  28. data/lib/draper/view_context/build_strategy.rb +11 -2
  29. data/lib/generators/controller_override.rb +2 -2
  30. data/lib/generators/draper/install_generator.rb +14 -0
  31. data/lib/generators/draper/templates/application_decorator.rb +8 -0
  32. data/lib/generators/mini_test/decorator_generator.rb +1 -1
  33. data/lib/generators/rails/decorator_generator.rb +1 -8
  34. data/lib/generators/rspec/templates/decorator_spec.rb +1 -1
  35. data/spec/draper/collection_decorator_spec.rb +11 -26
  36. data/spec/draper/configuration_spec.rb +25 -0
  37. data/spec/draper/decoratable_spec.rb +28 -13
  38. data/spec/draper/decorated_association_spec.rb +9 -9
  39. data/spec/draper/decorates_assigned_spec.rb +6 -6
  40. data/spec/draper/decorator_spec.rb +104 -89
  41. data/spec/draper/draper_spec.rb +24 -0
  42. data/spec/draper/factory_spec.rb +24 -24
  43. data/spec/draper/finders_spec.rb +21 -21
  44. data/spec/draper/helper_proxy_spec.rb +2 -2
  45. data/spec/draper/lazy_helpers_spec.rb +2 -2
  46. data/spec/draper/undecorate_chain_spec.rb +20 -0
  47. data/spec/draper/view_context/build_strategy_spec.rb +26 -10
  48. data/spec/draper/view_context_spec.rb +49 -21
  49. data/spec/dummy/app/controllers/base_controller.rb +4 -0
  50. data/spec/dummy/app/controllers/posts_controller.rb +2 -2
  51. data/spec/dummy/app/decorators/post_decorator.rb +0 -0
  52. data/spec/dummy/config/boot.rb +1 -1
  53. data/spec/dummy/config/initializers/draper.rb +3 -0
  54. data/spec/dummy/db/schema.rb +4 -4
  55. data/spec/dummy/fast_spec/post_decorator_spec.rb +1 -1
  56. data/spec/dummy/lib/tasks/test.rake +1 -1
  57. data/spec/dummy/spec/decorators/devise_spec.rb +0 -9
  58. data/spec/dummy/spec/decorators/post_decorator_spec.rb +2 -2
  59. data/spec/dummy/test/decorators/minitest/devise_test.rb +0 -9
  60. data/spec/dummy/test/decorators/minitest/view_context_test.rb +3 -3
  61. data/spec/dummy/test/decorators/test_unit/devise_test.rb +0 -9
  62. data/spec/generators/controller/controller_generator_spec.rb +3 -3
  63. data/spec/generators/decorator/decorator_generator_spec.rb +11 -10
  64. data/spec/generators/install/install_generator_spec.rb +19 -0
  65. data/spec/spec_helper.rb +4 -3
  66. data/spec/support/shared_examples/view_helpers.rb +8 -8
  67. metadata +38 -7
  68. data/spec/dummy/app/controllers/application_controller.rb +0 -4
@@ -2,7 +2,7 @@ require 'rake/testtask'
2
2
  require 'rails/test_unit/railtie'
3
3
 
4
4
  namespace :test do
5
- Rake::TestTask.new(:decorators => "test:prepare") do |t|
5
+ Rake::TestTask.new(decorators: "test:prepare") do |t|
6
6
  t.libs << "test"
7
7
  t.pattern = "test/decorators/**/*_test.rb"
8
8
  end
@@ -1,14 +1,7 @@
1
1
  module Draper
2
2
  module DeviseHelper
3
3
  def sign_in(resource_or_scope, resource = nil)
4
- scope = begin
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
@@ -6,4 +6,12 @@ module Draper
6
6
  object
7
7
  end
8
8
  end
9
+
10
+ def self.undecorate_chain(object)
11
+ if object.respond_to?(:decorated?) && object.decorated?
12
+ undecorate_chain(object.object)
13
+ else
14
+ object
15
+ end
16
+ end
9
17
  end
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = "3.0.0.pre1"
2
+ VERSION = '3.0.0'
3
3
  end
@@ -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
- (Draper::ViewContext.controller || ApplicationController.new).tap do |controller|
41
- controller.request ||= ActionController::TestRequest.create
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
@@ -0,0 +1,8 @@
1
+ class ApplicationDecorator < Draper::Decorator
2
+ # Define methods for all decorated objects.
3
+ # Helpers are accessed through `helpers` (aka `h`). For example:
4
+ #
5
+ # def percent_amount
6
+ # h.number_to_percentage object.amount, precision: 2
7
+ # end
8
+ end
@@ -7,7 +7,7 @@ module MiniTest
7
7
  File.expand_path('../templates', __FILE__)
8
8
  end
9
9
 
10
- class_option :spec, :type => :boolean, :default => false, :desc => "Use MiniTest::Spec DSL"
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
- class DecoratorGenerator < NamedBase
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
@@ -1,4 +1,4 @@
1
1
  require 'rails_helper'
2
2
 
3
- describe <%= class_name %>Decorator do
3
+ RSpec.describe <%= class_name %>Decorator do
4
4
  end
@@ -63,7 +63,7 @@ module Draper
63
63
  it "does not trigger decoration" do
64
64
  decorator = CollectionDecorator.new([])
65
65
 
66
- decorator.should_not_receive(:decorated_collection)
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.should_receive(:delegate).with(:foo, :bar, to: :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.should_receive(:delegate).with(:foo, :bar, to: :baz)
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
- context "with a block" do
125
- it "decorates Enumerable#find" do
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
- object.should_receive(:find).and_return(found)
140
- ActiveSupport::Deprecation.silence do
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.should_receive(:to_ary).and_return(:delegated)
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.decorated_collection.should_receive(:[]).with(42).and_return(:delegated)
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.should_receive(:kind_of?).with(Array).and_return("true")
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.stub(:kind_of?).with(::Draper::CollectionDecorator).and_return(false)
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.stub decorator_class: OtherDecorator
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.should_receive(:decorator_class).and_return(:some_decorator)
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.should_receive(:==).and_return(true)
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.should_receive(:==).and_return(false)
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 = double(object: Product.new)
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 = double(object: Class.new(Product).new)
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 = double(object: Model.new)
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.stub all: scoped
139
+ allow(Product).to receive(:all).and_return(scoped)
134
140
 
135
- Product.decorator_class.should_receive(:decorate_collection).with(scoped, with: nil).and_return(:decorated_collection)
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.stub all: []
147
+ allow(Product).to receive(:all).and_return([])
142
148
 
143
- Product.decorator_class.should_receive(:decorate_collection).with([], options)
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.stub(:model_name).and_return("Namespaced::Other")
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.any_instance.stub(:constantize) { Draper::Base }
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.should_receive(:new).with(options)
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.should_receive(:new).with(with: nil, context: anything())
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.should_receive(:new) do |options|
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.stub new: 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.should_receive(:decorate).with(associated, context_args: owner_context).and_return(decorated)
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.stub new: 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.should_receive(:decorate).once.and_return(decorated)
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.stub new: 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.should_receive(:decorate).with(scoped, anything()).and_return(decorated)
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