draper 3.0.0 → 4.0.2

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 (62) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +55 -0
  3. data/CHANGELOG.md +46 -0
  4. data/Gemfile +11 -2
  5. data/README.md +43 -5
  6. data/bin/bundle +114 -0
  7. data/bin/rake +29 -0
  8. data/draper.gemspec +9 -8
  9. data/lib/draper.rb +3 -0
  10. data/lib/draper/automatic_delegation.rb +4 -2
  11. data/lib/draper/collection_decorator.rb +4 -2
  12. data/lib/draper/compatibility/global_id.rb +22 -0
  13. data/lib/draper/configuration.rb +8 -0
  14. data/lib/draper/decoratable.rb +6 -7
  15. data/lib/draper/decorated_association.rb +0 -2
  16. data/lib/draper/decorator.rb +19 -13
  17. data/lib/draper/delegation.rb +1 -1
  18. data/lib/draper/finders.rb +0 -1
  19. data/lib/draper/helper_proxy.rb +2 -2
  20. data/lib/draper/lazy_helpers.rb +1 -3
  21. data/lib/draper/query_methods.rb +23 -0
  22. data/lib/draper/query_methods/load_strategy.rb +21 -0
  23. data/lib/draper/test_case.rb +3 -3
  24. data/lib/draper/undecorate.rb +1 -1
  25. data/lib/draper/version.rb +1 -1
  26. data/lib/draper/view_context/build_strategy.rb +1 -3
  27. data/lib/draper/view_helpers.rb +5 -5
  28. data/lib/generators/draper/install_generator.rb +1 -1
  29. data/lib/generators/mini_test/decorator_generator.rb +1 -1
  30. data/lib/generators/rails/decorator_generator.rb +1 -1
  31. data/lib/generators/rspec/decorator_generator.rb +6 -4
  32. data/lib/generators/test_unit/decorator_generator.rb +7 -4
  33. data/spec/draper/collection_decorator_spec.rb +5 -6
  34. data/spec/draper/configuration_spec.rb +32 -8
  35. data/spec/draper/decoratable_spec.rb +24 -4
  36. data/spec/draper/decorated_association_spec.rb +0 -2
  37. data/spec/draper/decorator_spec.rb +36 -3
  38. data/spec/draper/draper_spec.rb +1 -0
  39. data/spec/draper/factory_spec.rb +0 -4
  40. data/spec/draper/query_methods/load_strategy_spec.rb +26 -0
  41. data/spec/draper/query_methods_spec.rb +70 -0
  42. data/spec/dummy/app/assets/config/manifest.js +3 -0
  43. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  44. data/spec/dummy/app/jobs/publish_post_job.rb +7 -0
  45. data/spec/dummy/app/models/application_record.rb +3 -0
  46. data/spec/dummy/app/models/post.rb +1 -1
  47. data/spec/dummy/config/application.rb +0 -3
  48. data/spec/dummy/config/environments/development.rb +2 -0
  49. data/spec/dummy/config/environments/production.rb +2 -0
  50. data/spec/dummy/config/environments/test.rb +4 -0
  51. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  52. data/spec/dummy/config/storage.yml +7 -0
  53. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +1 -1
  54. data/spec/dummy/spec/jobs/publish_post_job_spec.rb +9 -0
  55. data/spec/dummy/spec/models/application_spec.rb +7 -0
  56. data/spec/dummy/spec/models/post_spec.rb +11 -2
  57. data/spec/generators/controller/controller_generator_spec.rb +1 -0
  58. data/spec/generators/decorator/decorator_generator_spec.rb +1 -1
  59. data/spec/integration/integration_spec.rb +1 -0
  60. data/spec/spec_helper.rb +9 -0
  61. metadata +72 -20
  62. data/.travis.yml +0 -9
@@ -2,6 +2,7 @@ module Draper
2
2
  class CollectionDecorator
3
3
  include Enumerable
4
4
  include Draper::ViewHelpers
5
+ include Draper::QueryMethods
5
6
  extend Draper::Delegation
6
7
 
7
8
  # @return the collection being decorated.
@@ -34,7 +35,7 @@ module Draper
34
35
  end
35
36
 
36
37
  class << self
37
- alias_method :decorate, :new
38
+ alias :decorate :new
38
39
  end
39
40
 
40
41
  # @return [Array] the decorated items.
@@ -58,11 +59,12 @@ module Draper
58
59
  true
59
60
  end
60
61
 
61
- alias_method :decorated_with?, :instance_of?
62
+ alias :decorated_with? :instance_of?
62
63
 
63
64
  def kind_of?(klass)
64
65
  decorated_collection.kind_of?(klass) || super
65
66
  end
67
+
66
68
  alias_method :is_a?, :kind_of?
67
69
 
68
70
  def replace(other)
@@ -0,0 +1,22 @@
1
+ module Draper
2
+ module Compatibility
3
+ # [Active Job](http://edgeguides.rubyonrails.org/active_job_basics.html) allows you to pass
4
+ # ActiveRecord objects to background tasks directly and performs the necessary serialization
5
+ # and deserialization. In order to do this, arguments to a background job must implement
6
+ # [Global ID](https://github.com/rails/globalid).
7
+ #
8
+ # This compatibility patch implements Global ID for decorated objects by delegating to the object
9
+ # that is decorated. This means you can pass decorated objects to background jobs, but
10
+ # the object won't be decorated when it is deserialized. This patch is meant as an intermediate
11
+ # fix until we can find a way to deserialize the decorated object correctly.
12
+ module GlobalID
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ include ::GlobalID::Identification
17
+
18
+ delegate :to_global_id, :to_signed_global_id, to: :object
19
+ end
20
+ end
21
+ end
22
+ end
@@ -11,5 +11,13 @@ module Draper
11
11
  def default_controller=(controller)
12
12
  @@default_controller = controller
13
13
  end
14
+
15
+ def default_query_methods_strategy
16
+ @@default_query_methods_strategy ||= :active_record
17
+ end
18
+
19
+ def default_query_methods_strategy=(strategy)
20
+ @@default_query_methods_strategy = strategy
21
+ end
14
22
  end
15
23
  end
@@ -48,7 +48,6 @@ module Draper
48
48
  end
49
49
 
50
50
  module ClassMethods
51
-
52
51
  # Decorates a collection of objects. Used at the end of a scope chain.
53
52
  #
54
53
  # @example
@@ -69,16 +68,16 @@ module Draper
69
68
  # `Product` maps to `ProductDecorator`).
70
69
  #
71
70
  # @return [Class] the inferred decorator class.
72
- def decorator_class
71
+ def decorator_class(called_on = self)
73
72
  prefix = respond_to?(:model_name) ? model_name : name
74
73
  decorator_name = "#{prefix}Decorator"
75
- decorator_name.constantize
76
- rescue NameError => error
77
- raise unless error.missing_name?(decorator_name)
74
+ decorator_name_constant = decorator_name.safe_constantize
75
+ return decorator_name_constant unless decorator_name_constant.nil?
76
+
78
77
  if superclass.respond_to?(:decorator_class)
79
- superclass.decorator_class
78
+ superclass.decorator_class(called_on)
80
79
  else
81
- raise Draper::UninferrableDecoratorError.new(self)
80
+ raise Draper::UninferrableDecoratorError.new(called_on)
82
81
  end
83
82
  end
84
83
 
@@ -1,7 +1,6 @@
1
1
  module Draper
2
2
  # @private
3
3
  class DecoratedAssociation
4
-
5
4
  def initialize(owner, association, options)
6
5
  options.assert_valid_keys(:with, :scope, :context)
7
6
 
@@ -30,6 +29,5 @@ module Draper
30
29
 
31
30
  @decorated = factory.decorate(associated, context_args: owner.context)
32
31
  end
33
-
34
32
  end
35
33
  end
@@ -1,6 +1,9 @@
1
+ require 'draper/compatibility/global_id'
2
+
1
3
  module Draper
2
4
  class Decorator
3
5
  include Draper::ViewHelpers
6
+ include Draper::Compatibility::GlobalID if defined?(GlobalID)
4
7
  extend Draper::Delegation
5
8
 
6
9
  include ActiveModel::Serialization
@@ -9,7 +12,8 @@ module Draper
9
12
 
10
13
  # @return the object being decorated.
11
14
  attr_reader :object
12
- alias_method :model, :object
15
+
16
+ alias :model :object
13
17
 
14
18
  # @return [Hash] extra data to be used in user-defined methods.
15
19
  attr_accessor :context
@@ -33,7 +37,7 @@ module Draper
33
37
  end
34
38
 
35
39
  class << self
36
- alias_method :decorate, :new
40
+ alias :decorate :new
37
41
  end
38
42
 
39
43
  # Automatically delegates instance methods to the source object. Class
@@ -187,7 +191,8 @@ module Draper
187
191
  def kind_of?(klass)
188
192
  super || object.kind_of?(klass)
189
193
  end
190
- alias_method :is_a?, :kind_of?
194
+
195
+ alias :is_a? :kind_of?
191
196
 
192
197
  # Checks if `self.instance_of?(klass)` or `object.instance_of?(klass)`
193
198
  #
@@ -222,9 +227,9 @@ module Draper
222
227
  # @return [Class] the class created by {decorate_collection}.
223
228
  def self.collection_decorator_class
224
229
  name = collection_decorator_name
225
- name.constantize
226
- rescue NameError
227
- Draper::CollectionDecorator
230
+ name_constant = name&.safe_constantize
231
+
232
+ name_constant || Draper::CollectionDecorator
228
233
  end
229
234
 
230
235
  private
@@ -239,22 +244,23 @@ module Draper
239
244
  end
240
245
 
241
246
  def self.object_class_name
242
- raise NameError if name.nil? || name.demodulize !~ /.+Decorator$/
247
+ return nil if name.nil? || name.demodulize !~ /.+Decorator$/
243
248
  name.chomp("Decorator")
244
249
  end
245
250
 
246
251
  def self.inferred_object_class
247
252
  name = object_class_name
248
- name.constantize
249
- rescue NameError => error
250
- raise if name && !error.missing_name?(name)
253
+ name_constant = name&.safe_constantize
254
+ return name_constant unless name_constant.nil?
255
+
251
256
  raise Draper::UninferrableObjectError.new(self)
252
257
  end
253
258
 
254
259
  def self.collection_decorator_name
255
- plural = object_class_name.pluralize
256
- raise NameError if plural == object_class_name
257
- "#{plural}Decorator"
260
+ singular = object_class_name
261
+ plural = singular&.pluralize
262
+
263
+ "#{plural}Decorator" unless plural == singular
258
264
  end
259
265
 
260
266
  def handle_multiple_decoration(options)
@@ -7,7 +7,7 @@ module Draper
7
7
  # @return [void]
8
8
  def delegate(*methods)
9
9
  options = methods.extract_options!
10
- super *methods, options.reverse_merge(to: :object)
10
+ super(*methods, **options.reverse_merge(to: :object))
11
11
  end
12
12
  end
13
13
  end
@@ -3,7 +3,6 @@ module Draper
3
3
  # do not have to extend this module directly; it is extended by
4
4
  # {Decorator.decorates_finders}.
5
5
  module Finders
6
-
7
6
  def find(id, options = {})
8
7
  decorate(object_class.find(id), options)
9
8
  end
@@ -2,14 +2,13 @@ module Draper
2
2
  # Provides access to helper methods - both Rails built-in helpers, and those
3
3
  # defined in your application.
4
4
  class HelperProxy
5
-
6
5
  # @overload initialize(view_context)
7
6
  def initialize(view_context)
8
7
  @view_context = view_context
9
8
  end
10
9
 
11
10
  # Sends helper methods to the view context.
12
- def method_missing(method, *args, &block)
11
+ ruby2_keywords def method_missing(method, *args, &block)
13
12
  self.class.define_proxy method
14
13
  send(method, *args, &block)
15
14
  end
@@ -32,6 +31,7 @@ module Draper
32
31
  define_method name do |*args, &block|
33
32
  view_context.send(name, *args, &block)
34
33
  end
34
+ ruby2_keywords name
35
35
  end
36
36
  end
37
37
  end
@@ -3,13 +3,11 @@ module Draper
3
3
  # so that you can stop typing `h.` everywhere, at the cost of mixing in a
4
4
  # bazillion methods.
5
5
  module LazyHelpers
6
-
7
6
  # Sends missing methods to the {HelperProxy}.
8
- def method_missing(method, *args, &block)
7
+ ruby2_keywords def method_missing(method, *args, &block)
9
8
  helpers.send(method, *args, &block)
10
9
  rescue NoMethodError
11
10
  super
12
11
  end
13
-
14
12
  end
15
13
  end
@@ -0,0 +1,23 @@
1
+ require_relative 'query_methods/load_strategy'
2
+
3
+ module Draper
4
+ module QueryMethods
5
+ # Proxies missing query methods to the source class if the strategy allows.
6
+ ruby2_keywords def method_missing(method, *args, &block)
7
+ return super unless strategy.allowed? method
8
+
9
+ object.send(method, *args, &block).decorate(with: decorator_class, context: context)
10
+ end
11
+
12
+ def respond_to_missing?(method, include_private = false)
13
+ strategy.allowed?(method) || super
14
+ end
15
+
16
+ private
17
+
18
+ # Configures the strategy used to proxy the query methods, which defaults to `:active_record`.
19
+ def strategy
20
+ @strategy ||= LoadStrategy.new(Draper.default_query_methods_strategy)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module Draper
2
+ module QueryMethods
3
+ module LoadStrategy
4
+ def self.new(name)
5
+ const_get(name.to_s.camelize).new
6
+ end
7
+
8
+ class ActiveRecord
9
+ def allowed?(method)
10
+ ::ActiveRecord::Relation::VALUE_METHODS.include? method
11
+ end
12
+ end
13
+
14
+ class Mongoid
15
+ def allowed?(method)
16
+ raise NotImplementedError
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -3,9 +3,9 @@ module Draper
3
3
 
4
4
  class TestCase < ::ActiveSupport::TestCase
5
5
  module ViewContextTeardown
6
- def teardown
7
- super
6
+ def before_setup
8
7
  Draper::ViewContext.clear!
8
+ super
9
9
  end
10
10
  end
11
11
 
@@ -21,7 +21,7 @@ module Draper
21
21
  end
22
22
 
23
23
  include Draper::ViewHelpers::ClassMethods
24
- alias_method :helper, :helpers
24
+ alias :helper :helpers
25
25
  end
26
26
 
27
27
  include Behavior
@@ -6,7 +6,7 @@ module Draper
6
6
  object
7
7
  end
8
8
  end
9
-
9
+
10
10
  def self.undecorate_chain(object)
11
11
  if object.respond_to?(:decorated?) && object.decorated?
12
12
  undecorate_chain(object.object)
@@ -1,3 +1,3 @@
1
1
  module Draper
2
- VERSION = '3.0.0'
2
+ VERSION = '4.0.2'
3
3
  end
@@ -2,7 +2,6 @@ module Draper
2
2
  module ViewContext
3
3
  # @private
4
4
  module BuildStrategy
5
-
6
5
  def self.new(name, &block)
7
6
  const_get(name.to_s.camelize).new(&block)
8
7
  end
@@ -13,7 +12,7 @@ module Draper
13
12
  end
14
13
 
15
14
  def call
16
- view_context_class.new
15
+ view_context_class.respond_to?(:empty) ? view_context_class.empty : view_context_class.new
17
16
  end
18
17
 
19
18
  private
@@ -51,7 +50,6 @@ module Draper
51
50
  ActionController::TestRequest.method(:create).parameters.first == [:req, :controller_class]
52
51
  end
53
52
  end
54
-
55
53
  end
56
54
  end
57
55
  end
@@ -5,7 +5,6 @@ module Draper
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  module ClassMethods
8
-
9
8
  # Access the helpers proxy to call built-in and user-defined
10
9
  # Rails helpers from a class context.
11
10
  #
@@ -13,8 +12,8 @@ module Draper
13
12
  def helpers
14
13
  Draper::ViewContext.current
15
14
  end
16
- alias_method :h, :helpers
17
15
 
16
+ alias :h :helpers
18
17
  end
19
18
 
20
19
  # Access the helpers proxy to call built-in and user-defined
@@ -24,14 +23,15 @@ module Draper
24
23
  def helpers
25
24
  Draper::ViewContext.current
26
25
  end
27
- alias_method :h, :helpers
26
+
27
+ alias :h :helpers
28
28
 
29
29
  # Alias for `helpers.localize`, since localize is something that's used
30
30
  # quite often. Further aliased to `l` for convenience.
31
- def localize(*args)
31
+ ruby2_keywords def localize(*args)
32
32
  helpers.localize(*args)
33
33
  end
34
- alias_method :l, :localize
35
34
 
35
+ alias :l :localize
36
36
  end
37
37
  end
@@ -1,7 +1,7 @@
1
1
  module Draper
2
2
  module Generators
3
3
  class InstallGenerator < Rails::Generators::Base
4
- source_root File.expand_path('../templates', __FILE__)
4
+ source_root File.expand_path("templates", __dir__)
5
5
 
6
6
  desc 'Creates an ApplicationDecorator, if none exists.'
7
7
 
@@ -4,7 +4,7 @@ module MiniTest
4
4
  module Generators
5
5
  class DecoratorGenerator < Base
6
6
  def self.source_root
7
- File.expand_path('../templates', __FILE__)
7
+ File.expand_path("templates", __dir__)
8
8
  end
9
9
 
10
10
  class_option :spec, type: :boolean, default: false, desc: "Use MiniTest::Spec DSL"
@@ -1,7 +1,7 @@
1
1
  module Rails
2
2
  module Generators
3
3
  class DecoratorGenerator < NamedBase
4
- source_root File.expand_path("../templates", __FILE__)
4
+ source_root File.expand_path("templates", __dir__)
5
5
  check_class_collision suffix: "Decorator"
6
6
 
7
7
  class_option :parent, type: :string, desc: "The parent class for the generated decorator"
@@ -1,9 +1,11 @@
1
1
  module Rspec
2
- class DecoratorGenerator < ::Rails::Generators::NamedBase
3
- source_root File.expand_path('../templates', __FILE__)
2
+ module Generators
3
+ class DecoratorGenerator < ::Rails::Generators::NamedBase
4
+ source_root File.expand_path("templates", __dir__)
4
5
 
5
- def create_spec_file
6
- template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{singular_name}_decorator_spec.rb")
6
+ def create_spec_file
7
+ template 'decorator_spec.rb', File.join('spec/decorators', class_path, "#{singular_name}_decorator_spec.rb")
8
+ end
7
9
  end
8
10
  end
9
11
  end