draper 2.1.0 → 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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +16 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +24 -0
  4. data/.gitignore +3 -1
  5. data/.rubocop.yml +11 -0
  6. data/.travis.yml +3 -7
  7. data/CHANGELOG.md +26 -0
  8. data/Gemfile +4 -5
  9. data/Guardfile +5 -5
  10. data/README.md +61 -11
  11. data/Rakefile +1 -1
  12. data/draper.gemspec +12 -10
  13. data/lib/draper.rb +8 -3
  14. data/lib/draper/automatic_delegation.rb +5 -3
  15. data/lib/draper/collection_decorator.rb +1 -11
  16. data/lib/draper/compatibility/api_only.rb +23 -0
  17. data/lib/draper/configuration.rb +15 -0
  18. data/lib/draper/decoratable.rb +3 -4
  19. data/lib/draper/decorator.rb +4 -24
  20. data/lib/draper/finders.rb +0 -0
  21. data/lib/draper/helper_proxy.rb +1 -8
  22. data/lib/draper/railtie.rb +12 -21
  23. data/lib/draper/tasks/test.rake +2 -15
  24. data/lib/draper/test/devise_helper.rb +1 -8
  25. data/lib/draper/test/minitest_integration.rb +0 -0
  26. data/lib/draper/test/rspec_integration.rb +1 -5
  27. data/lib/draper/test_case.rb +4 -8
  28. data/lib/draper/undecorate.rb +8 -0
  29. data/lib/draper/version.rb +1 -1
  30. data/lib/draper/view_context.rb +3 -19
  31. data/lib/draper/view_context/build_strategy.rb +11 -2
  32. data/lib/generators/controller_override.rb +2 -2
  33. data/lib/generators/draper/install_generator.rb +14 -0
  34. data/lib/generators/draper/templates/application_decorator.rb +8 -0
  35. data/lib/generators/mini_test/decorator_generator.rb +1 -1
  36. data/lib/generators/rails/decorator_generator.rb +1 -8
  37. data/lib/generators/rspec/templates/decorator_spec.rb +2 -2
  38. data/spec/draper/collection_decorator_spec.rb +11 -26
  39. data/spec/draper/configuration_spec.rb +25 -0
  40. data/spec/draper/decoratable_spec.rb +29 -16
  41. data/spec/draper/decorated_association_spec.rb +9 -9
  42. data/spec/draper/decorates_assigned_spec.rb +6 -6
  43. data/spec/draper/decorator_spec.rb +112 -89
  44. data/spec/draper/draper_spec.rb +24 -0
  45. data/spec/draper/factory_spec.rb +26 -26
  46. data/spec/draper/finders_spec.rb +21 -21
  47. data/spec/draper/helper_proxy_spec.rb +3 -3
  48. data/spec/draper/lazy_helpers_spec.rb +2 -2
  49. data/spec/draper/undecorate_chain_spec.rb +20 -0
  50. data/spec/draper/view_context/build_strategy_spec.rb +26 -10
  51. data/spec/draper/view_context_spec.rb +49 -21
  52. data/spec/dummy/app/controllers/base_controller.rb +4 -0
  53. data/spec/dummy/app/controllers/posts_controller.rb +2 -2
  54. data/spec/dummy/app/decorators/post_decorator.rb +0 -0
  55. data/spec/dummy/app/views/posts/_post.html.erb +8 -6
  56. data/spec/dummy/config/boot.rb +1 -1
  57. data/spec/dummy/config/initializers/draper.rb +3 -0
  58. data/spec/dummy/config/mongoid.yml +104 -41
  59. data/spec/dummy/db/schema.rb +4 -4
  60. data/spec/dummy/fast_spec/post_decorator_spec.rb +1 -1
  61. data/spec/dummy/lib/tasks/test.rake +1 -1
  62. data/spec/dummy/spec/decorators/active_model_serializers_spec.rb +4 -8
  63. data/spec/dummy/spec/decorators/devise_spec.rb +0 -9
  64. data/spec/dummy/spec/decorators/post_decorator_spec.rb +2 -4
  65. data/spec/dummy/spec/mailers/post_mailer_spec.rb +0 -8
  66. data/spec/dummy/spec/shared_examples/decoratable.rb +0 -2
  67. data/spec/dummy/test/decorators/minitest/devise_test.rb +0 -9
  68. data/spec/dummy/test/decorators/minitest/view_context_test.rb +3 -3
  69. data/spec/dummy/test/decorators/test_unit/devise_test.rb +0 -9
  70. data/spec/dummy/test/decorators/test_unit/view_context_test.rb +1 -1
  71. data/spec/generators/controller/controller_generator_spec.rb +3 -3
  72. data/spec/generators/decorator/decorator_generator_spec.rb +14 -12
  73. data/spec/generators/install/install_generator_spec.rb +19 -0
  74. data/spec/integration/integration_spec.rb +11 -8
  75. data/spec/performance/benchmark.rb +1 -1
  76. data/spec/spec_helper.rb +4 -4
  77. data/spec/support/matchers/have_text.rb +2 -2
  78. data/spec/support/shared_examples/view_helpers.rb +8 -8
  79. metadata +71 -29
  80. data/gemfiles/4.0.gemfile +0 -3
  81. data/gemfiles/4.1.gemfile +0 -3
  82. data/gemfiles/4.2.gemfile +0 -3
  83. data/spec/dummy/app/controllers/application_controller.rb +0 -4
@@ -0,0 +1,23 @@
1
+ module Draper
2
+ module Compatibility
3
+ # Draper expects your `ApplicationController` to include `ActionView::Rendering`. The
4
+ # `ApplicationController` generated by Rails 5 API-only applications (created with
5
+ # `rails new --api`) don't by default. However, including `ActionView::Rendering` in
6
+ # `ApplicatonController` breaks `render :json` due to `render_to_body` being overridden.
7
+ #
8
+ # This compatibility patch fixes the issue by restoring the original `render_to_body`
9
+ # method after including `ActionView::Rendering`. Ultimately, including `ActionView::Rendering`
10
+ # in an ActionController::API may not be supported functionality by Rails (see Rails issue
11
+ # for more detail: https://github.com/rails/rails/issues/27211). This hack is meant to be a
12
+ # temporary solution until we can find a way to not rely on the controller layer.
13
+ module ApiOnly
14
+ extend ActiveSupport::Concern
15
+
16
+ included do
17
+ alias :previous_render_to_body :render_to_body
18
+ include ActionView::Rendering
19
+ alias :render_to_body :previous_render_to_body
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module Draper
2
+ module Configuration
3
+ def configure
4
+ yield self
5
+ end
6
+
7
+ def default_controller
8
+ @@default_controller ||= ApplicationController
9
+ end
10
+
11
+ def default_controller=(controller)
12
+ @@default_controller = controller
13
+ end
14
+ end
15
+ end
@@ -56,8 +56,7 @@ module Draper
56
56
  # @param [Hash] options
57
57
  # see {Decorator.decorate_collection}.
58
58
  def decorate(options = {})
59
- collection = Rails::VERSION::MAJOR >= 4 ? all : scoped
60
- decorator_class.decorate_collection(collection, options.reverse_merge(with: nil))
59
+ decorator_class.decorate_collection(all, options.reverse_merge(with: nil))
61
60
  end
62
61
 
63
62
  def decorator_class?
@@ -75,10 +74,10 @@ module Draper
75
74
  decorator_name = "#{prefix}Decorator"
76
75
  decorator_name.constantize
77
76
  rescue NameError => error
77
+ raise unless error.missing_name?(decorator_name)
78
78
  if superclass.respond_to?(:decorator_class)
79
79
  superclass.decorator_class
80
80
  else
81
- raise unless error.missing_name?(decorator_name)
82
81
  raise Draper::UninferrableDecoratorError.new(self)
83
82
  end
84
83
  end
@@ -87,7 +86,7 @@ module Draper
87
86
  #
88
87
  # @return [Boolean]
89
88
  def ===(other)
90
- super || (other.respond_to?(:object) && super(other.object))
89
+ super || (other.is_a?(Draper::Decorator) && super(other.object))
91
90
  end
92
91
 
93
92
  end
@@ -10,8 +10,6 @@ module Draper
10
10
  # @return the object being decorated.
11
11
  attr_reader :object
12
12
  alias_method :model, :object
13
- alias_method :source, :object # TODO: deprecate this
14
- alias_method :to_source, :object # TODO: deprecate this
15
13
 
16
14
  # @return [Hash] extra data to be used in user-defined methods.
17
15
  attr_accessor :context
@@ -72,15 +70,10 @@ module Draper
72
70
  # Checks whether this decorator class has a corresponding {object_class}.
73
71
  def self.object_class?
74
72
  object_class
75
- rescue Draper::UninferrableSourceError
73
+ rescue Draper::UninferrableObjectError
76
74
  false
77
75
  end
78
76
 
79
- class << self # TODO deprecate this
80
- alias_method :source_class, :object_class
81
- alias_method :source_class?, :object_class?
82
- end
83
-
84
77
  # Automatically decorates ActiveRecord finder methods, so that you can use
85
78
  # `ProductDecorator.find(id)` instead of
86
79
  # `ProductDecorator.decorate(Product.find(id))`.
@@ -182,7 +175,7 @@ module Draper
182
175
 
183
176
  # Returns a unique hash for a decorated object based on
184
177
  # the decorator class and the object being decorated.
185
- #
178
+ #
186
179
  # @return [Fixnum]
187
180
  def hash
188
181
  self.class.hash ^ object.hash
@@ -203,18 +196,6 @@ module Draper
203
196
  super || object.instance_of?(klass)
204
197
  end
205
198
 
206
- if RUBY_VERSION < "2.0"
207
- # nasty hack to stop 1.9.x using the delegated `to_s` in `inspect`
208
- alias_method :_to_s, :to_s
209
-
210
- def inspect
211
- ivars = instance_variables.map do |name|
212
- "#{name}=#{instance_variable_get(name).inspect}"
213
- end
214
- _to_s.insert(-2, " #{ivars.join(", ")}")
215
- end
216
- end
217
-
218
199
  delegate :to_s
219
200
 
220
201
  # In case object is nil
@@ -242,8 +223,7 @@ module Draper
242
223
  def self.collection_decorator_class
243
224
  name = collection_decorator_name
244
225
  name.constantize
245
- rescue NameError => error
246
- raise if name && !error.missing_name?(name)
226
+ rescue NameError
247
227
  Draper::CollectionDecorator
248
228
  end
249
229
 
@@ -268,7 +248,7 @@ module Draper
268
248
  name.constantize
269
249
  rescue NameError => error
270
250
  raise if name && !error.missing_name?(name)
271
- raise Draper::UninferrableSourceError.new(self)
251
+ raise Draper::UninferrableObjectError.new(self)
272
252
  end
273
253
 
274
254
  def self.collection_decorator_name
File without changes
@@ -4,9 +4,7 @@ module Draper
4
4
  class HelperProxy
5
5
 
6
6
  # @overload initialize(view_context)
7
- def initialize(view_context = nil)
8
- view_context ||= current_view_context # backwards compatibility
9
-
7
+ def initialize(view_context)
10
8
  @view_context = view_context
11
9
  end
12
10
 
@@ -35,10 +33,5 @@ module Draper
35
33
  view_context.send(name, *args, &block)
36
34
  end
37
35
  end
38
-
39
- def current_view_context
40
- ActiveSupport::Deprecation.warn("wrong number of arguments (0 for 1) passed to Draper::HelperProxy.new", caller[1..-1])
41
- Draper::ViewContext.current.view_context
42
- end
43
36
  end
44
37
  end
@@ -3,8 +3,6 @@ require 'rails/railtie'
3
3
  module ActiveModel
4
4
  class Railtie < Rails::Railtie
5
5
  generators do |app|
6
- app ||= Rails.application # Rails 3.0.x does not yield `app`
7
-
8
6
  Rails::Generators.configure! app.config.generators
9
7
  require_relative '../generators/controller_override'
10
8
  end
@@ -13,7 +11,6 @@ end
13
11
 
14
12
  module Draper
15
13
  class Railtie < Rails::Railtie
16
-
17
14
  config.after_initialize do |app|
18
15
  app.config.paths.add 'app/decorators', eager_load: true
19
16
 
@@ -23,19 +20,19 @@ module Draper
23
20
  end
24
21
  end
25
22
 
26
- initializer "draper.setup_action_controller" do |app|
23
+ initializer 'draper.setup_action_controller' do
27
24
  ActiveSupport.on_load :action_controller do
28
25
  Draper.setup_action_controller self
29
26
  end
30
27
  end
31
28
 
32
- initializer "draper.setup_action_mailer" do |app|
29
+ initializer 'draper.setup_action_mailer' do
33
30
  ActiveSupport.on_load :action_mailer do
34
31
  Draper.setup_action_mailer self
35
32
  end
36
33
  end
37
34
 
38
- initializer "draper.setup_orm" do |app|
35
+ initializer 'draper.setup_orm' do
39
36
  [:active_record, :mongoid].each do |orm|
40
37
  ActiveSupport.on_load orm do
41
38
  Draper.setup_orm self
@@ -43,28 +40,22 @@ module Draper
43
40
  end
44
41
  end
45
42
 
46
- initializer "draper.setup_active_model_serializers" do |app|
47
- ActiveSupport.on_load :active_model_serializers do
48
- if defined?(ActiveModel::ArraySerializerSupport)
49
- Draper::CollectionDecorator.send :include, ActiveModel::ArraySerializerSupport
50
- end
51
- end
52
- end
53
-
54
- initializer "draper.minitest-rails_integration" do |app|
43
+ initializer 'draper.minitest-rails_integration' do
55
44
  ActiveSupport.on_load :minitest do
56
- require "draper/test/minitest_integration"
45
+ require 'draper/test/minitest_integration'
57
46
  end
58
47
  end
59
48
 
60
- console do
49
+ def initialize_view_context
61
50
  require 'action_controller/test_case'
62
- ApplicationController.new.view_context
51
+ Draper.default_controller.new.view_context
63
52
  Draper::ViewContext.build
64
53
  end
65
54
 
66
- rake_tasks do
67
- Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f }
68
- end
55
+ console { initialize_view_context }
56
+
57
+ runner { initialize_view_context }
58
+
59
+ rake_tasks { Dir[File.join(File.dirname(__FILE__), 'tasks/*.rake')].each { |f| load f } }
69
60
  end
70
61
  end
@@ -1,22 +1,9 @@
1
1
  require 'rake/testtask'
2
-
3
- test_task = if Rails.version.to_f < 3.2
4
- require 'rails/test_unit/railtie'
5
- Rake::TestTask
6
- else
7
- require 'rails/test_unit/sub_test_task'
8
- Rails::SubTestTask
9
- end
2
+ require 'rails/test_unit/railtie'
10
3
 
11
4
  namespace :test do
12
- test_task.new(:decorators => "test:prepare") do |t|
5
+ Rake::TestTask.new(decorators: "test:prepare") do |t|
13
6
  t.libs << "test"
14
7
  t.pattern = "test/decorators/**/*_test.rb"
15
8
  end
16
9
  end
17
-
18
- if Rails.version.to_f < 4.2 && Rake::Task.task_defined?('test:run')
19
- Rake::Task['test:run'].enhance do
20
- Rake::Task['test:decorators'].invoke
21
- end
22
- 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
@@ -7,11 +7,7 @@ module Draper
7
7
  end
8
8
 
9
9
  RSpec.configure do |config|
10
- if RSpec::Core::Version::STRING.starts_with?("3")
11
- config.include DecoratorExampleGroup, file_path: %r{spec/decorators}, type: :decorator
12
- else
13
- config.include DecoratorExampleGroup, example_group: {file_path: %r{spec/decorators}}, type: :decorator
14
- end
10
+ config.include DecoratorExampleGroup, file_path: %r{spec/decorators}, type: :decorator
15
11
 
16
12
  [:decorator, :controller, :mailer].each do |type|
17
13
  config.before(:each, type: type) { Draper::ViewContext.clear! }
@@ -29,14 +29,10 @@ module Draper
29
29
  end
30
30
  end
31
31
 
32
- if defined?(ActionController::TestCase)
33
- class ActionController::TestCase
34
- include Draper::TestCase::ViewContextTeardown
35
- end
32
+ if defined? ActionController::TestCase
33
+ ActionController::TestCase.include Draper::TestCase::ViewContextTeardown
36
34
  end
37
35
 
38
- if defined?(ActionMailer::TestCase)
39
- class ActionMailer::TestCase
40
- include Draper::TestCase::ViewContextTeardown
41
- end
36
+ if defined? ActionMailer::TestCase
37
+ ActionMailer::TestCase.include Draper::TestCase::ViewContextTeardown
42
38
  end
@@ -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 = "2.1.0"
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.new if defined?(ActionController::TestRequest)
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