draper 1.2.0 → 1.2.1

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.
@@ -18,7 +18,7 @@ module Draper
18
18
 
19
19
  # @private
20
20
  def delegatable?(method)
21
- source.respond_to?(method)
21
+ object.respond_to?(method)
22
22
  end
23
23
 
24
24
  module ClassMethods
@@ -26,7 +26,7 @@ module Draper
26
26
  def method_missing(method, *args, &block)
27
27
  return super unless delegatable?(method)
28
28
 
29
- source_class.send(method, *args, &block)
29
+ object_class.send(method, *args, &block)
30
30
  end
31
31
 
32
32
  # Checks if the decorator responds to a class method, or is able to proxy
@@ -37,7 +37,7 @@ module Draper
37
37
 
38
38
  # @private
39
39
  def delegatable?(method)
40
- source_class? && source_class.respond_to?(method)
40
+ object_class? && object_class.respond_to?(method)
41
41
  end
42
42
 
43
43
  # @private
@@ -15,19 +15,17 @@ module Draper
15
15
  array_methods = Array.instance_methods - Object.instance_methods
16
16
  delegate :==, :as_json, *array_methods, to: :decorated_collection
17
17
 
18
- # @param [Enumerable] source
18
+ # @param [Enumerable] object
19
19
  # collection to decorate.
20
20
  # @option options [Class, nil] :with (nil)
21
- # the decorator class used to decorate each item. When `nil`, it is
22
- # inferred from the collection decorator class if possible (e.g.
23
- # `ProductsDecorator` maps to `ProductDecorator`), otherwise each item's
21
+ # the decorator class used to decorate each item. When `nil`, each item's
24
22
  # {Decoratable#decorate decorate} method will be used.
25
23
  # @option options [Hash] :context ({})
26
24
  # extra data to be stored in the collection decorator and used in
27
25
  # user-defined methods, and passed to each item's decorator.
28
- def initialize(source, options = {})
26
+ def initialize(object, options = {})
29
27
  options.assert_valid_keys(:with, :context)
30
- @source = source
28
+ @object = object
31
29
  @decorator_class = options[:with]
32
30
  @context = options.fetch(:context, {})
33
31
  end
@@ -38,7 +36,7 @@ module Draper
38
36
 
39
37
  # @return [Array] the decorated items.
40
38
  def decorated_collection
41
- @decorated_collection ||= source.map{|item| decorate_item(item)}
39
+ @decorated_collection ||= object.map{|item| decorate_item(item)}
42
40
  end
43
41
 
44
42
  # Delegated to the decorated collection when using the block form
@@ -48,12 +46,13 @@ module Draper
48
46
  if block_given?
49
47
  decorated_collection.find(*args, &block)
50
48
  else
51
- decorator_class.find(*args)
49
+ ActiveSupport::Deprecation.warn("Using ActiveRecord's `find` on a CollectionDecorator is deprecated. Call `find` on a model, and then decorate the result", caller)
50
+ decorate_item(object.find(*args))
52
51
  end
53
52
  end
54
53
 
55
54
  def to_s
56
- "#<#{self.class.name} of #{decorator_class || "inferred decorators"} for #{source.inspect}>"
55
+ "#<#{self.class.name} of #{decorator_class || "inferred decorators"} for #{object.inspect}>"
57
56
  end
58
57
 
59
58
  def context=(value)
@@ -73,10 +72,15 @@ module Draper
73
72
  end
74
73
  alias_method :is_a?, :kind_of?
75
74
 
75
+ def replace(other)
76
+ decorated_collection.replace(other)
77
+ self
78
+ end
79
+
76
80
  protected
77
81
 
78
82
  # @return the collection being decorated.
79
- attr_reader :source
83
+ attr_reader :object
80
84
 
81
85
  # Decorates the given item.
82
86
  def decorate_item(item)
@@ -73,7 +73,7 @@ module Draper
73
73
  #
74
74
  # @return [Boolean]
75
75
  def ===(other)
76
- super || (other.respond_to?(:source) && super(other.source))
76
+ super || (other.respond_to?(:object) && super(other.object))
77
77
  end
78
78
 
79
79
  end
@@ -19,7 +19,7 @@ module Draper
19
19
  # @private
20
20
  def self.test_for_decorator(object, other)
21
21
  other.respond_to?(:decorated?) && other.decorated? &&
22
- other.respond_to?(:source) && test(object, other.source)
22
+ other.respond_to?(:object) && test(object, other.object)
23
23
  end
24
24
  end
25
25
  end
@@ -25,7 +25,7 @@ module Draper
25
25
  attr_reader :factory, :owner, :association, :scope
26
26
 
27
27
  def decorate
28
- associated = owner.source.send(association)
28
+ associated = owner.object.send(association)
29
29
  associated = associated.send(scope) if scope
30
30
 
31
31
  @decorated = factory.decorate(associated, context_args: owner.context)
@@ -8,9 +8,10 @@ module Draper
8
8
  include ActiveModel::Serializers::Xml
9
9
 
10
10
  # @return the object being decorated.
11
- attr_reader :source
12
- alias_method :model, :source
13
- alias_method :to_source, :source
11
+ attr_reader :object
12
+ alias_method :model, :object
13
+ alias_method :source, :object # TODO: deprecate this
14
+ alias_method :to_source, :object # TODO: deprecate this
14
15
 
15
16
  # @return [Hash] extra data to be used in user-defined methods.
16
17
  attr_accessor :context
@@ -21,16 +22,16 @@ module Draper
21
22
  # decorator to an instance of itself will create a decorator with the same
22
23
  # source as the original, rather than redecorating the other instance.
23
24
  #
24
- # @param [Object] source
25
+ # @param [Object] object
25
26
  # object to decorate.
26
27
  # @option options [Hash] :context ({})
27
28
  # extra data to be stored in the decorator and used in user-defined
28
29
  # methods.
29
- def initialize(source, options = {})
30
+ def initialize(object, options = {})
30
31
  options.assert_valid_keys(:context)
31
- @source = source
32
+ @object = object
32
33
  @context = options.fetch(:context, {})
33
- handle_multiple_decoration(options) if source.instance_of?(self.class)
34
+ handle_multiple_decoration(options) if object.instance_of?(self.class)
34
35
  end
35
36
 
36
37
  class << self
@@ -38,7 +39,7 @@ module Draper
38
39
  end
39
40
 
40
41
  # Automatically delegates instance methods to the source object. Class
41
- # methods will be delegated to the {source_class}, if it is set.
42
+ # methods will be delegated to the {object_class}, if it is set.
42
43
  #
43
44
  # @return [void]
44
45
  def self.delegate_all
@@ -51,11 +52,11 @@ module Draper
51
52
  # source (including when using {decorates_finders}), and the source class
52
53
  # cannot be inferred from the decorator class (e.g. `ProductDecorator`
53
54
  # maps to `Product`).
54
- # @param [String, Symbol, Class] source_class
55
+ # @param [String, Symbol, Class] object_class
55
56
  # source class (or class name) that corresponds to this decorator.
56
57
  # @return [void]
57
- def self.decorates(source_class)
58
- @source_class = source_class.to_s.camelize.constantize
58
+ def self.decorates(object_class)
59
+ @object_class = object_class.to_s.camelize.constantize
59
60
  end
60
61
 
61
62
  # Returns the source class corresponding to the decorator class, as set by
@@ -63,22 +64,27 @@ module Draper
63
64
  # `ProductDecorator` maps to `Product`).
64
65
  #
65
66
  # @return [Class] the source class that corresponds to this decorator.
66
- def self.source_class
67
- @source_class ||= inferred_source_class
67
+ def self.object_class
68
+ @object_class ||= inferred_object_class
68
69
  end
69
70
 
70
- # Checks whether this decorator class has a corresponding {source_class}.
71
- def self.source_class?
72
- source_class
71
+ # Checks whether this decorator class has a corresponding {object_class}.
72
+ def self.object_class?
73
+ object_class
73
74
  rescue Draper::UninferrableSourceError
74
75
  false
75
76
  end
76
77
 
78
+ class << self # TODO deprecate this
79
+ alias_method :source_class, :object_class
80
+ alias_method :source_class?, :object_class?
81
+ end
82
+
77
83
  # Automatically decorates ActiveRecord finder methods, so that you can use
78
84
  # `ProductDecorator.find(id)` instead of
79
85
  # `ProductDecorator.decorate(Product.find(id))`.
80
86
  #
81
- # Finder methods are applied to the {source_class}.
87
+ # Finder methods are applied to the {object_class}.
82
88
  #
83
89
  # @return [void]
84
90
  def self.decorates_finders
@@ -126,22 +132,22 @@ module Draper
126
132
  # maps to `ProductsDecorator`), but otherwise defaults to
127
133
  # {Draper::CollectionDecorator}.
128
134
  #
129
- # @param [Object] source
135
+ # @param [Object] object
130
136
  # collection to decorate.
131
137
  # @option options [Class, nil] :with (self)
132
138
  # the decorator class used to decorate each item. When `nil`, it is
133
139
  # inferred from each item.
134
140
  # @option options [Hash] :context
135
141
  # extra data to be stored in the collection decorator.
136
- def self.decorate_collection(source, options = {})
142
+ def self.decorate_collection(object, options = {})
137
143
  options.assert_valid_keys(:with, :context)
138
- collection_decorator_class.new(source, options.reverse_merge(with: self))
144
+ collection_decorator_class.new(object, options.reverse_merge(with: self))
139
145
  end
140
146
 
141
147
  # @return [Array<Class>] the list of decorators that have been applied to
142
148
  # the object.
143
149
  def applied_decorators
144
- chain = source.respond_to?(:applied_decorators) ? source.applied_decorators : []
150
+ chain = object.respond_to?(:applied_decorators) ? object.applied_decorators : []
145
151
  chain << self.class
146
152
  end
147
153
 
@@ -159,30 +165,30 @@ module Draper
159
165
  true
160
166
  end
161
167
 
162
- # Compares the source with a possibly-decorated object.
168
+ # Compares the source object with a possibly-decorated object.
163
169
  #
164
170
  # @return [Boolean]
165
171
  def ==(other)
166
- Draper::Decoratable::Equality.test(source, other)
172
+ Draper::Decoratable::Equality.test(object, other)
167
173
  end
168
174
 
169
- # Checks if `self.kind_of?(klass)` or `source.kind_of?(klass)`
175
+ # Checks if `self.kind_of?(klass)` or `object.kind_of?(klass)`
170
176
  #
171
177
  # @param [Class] klass
172
178
  def kind_of?(klass)
173
- super || source.kind_of?(klass)
179
+ super || object.kind_of?(klass)
174
180
  end
175
181
  alias_method :is_a?, :kind_of?
176
182
 
177
- # Checks if `self.instance_of?(klass)` or `source.instance_of?(klass)`
183
+ # Checks if `self.instance_of?(klass)` or `object.instance_of?(klass)`
178
184
  #
179
185
  # @param [Class] klass
180
186
  def instance_of?(klass)
181
- super || source.instance_of?(klass)
187
+ super || object.instance_of?(klass)
182
188
  end
183
189
 
184
- # In case source is nil
185
- delegate :present?
190
+ # In case object is nil
191
+ delegate :present?, :blank?
186
192
 
187
193
  # ActiveModel compatibility
188
194
  # @private
@@ -190,17 +196,17 @@ module Draper
190
196
  self
191
197
  end
192
198
 
193
- # @return [Hash] the source's attributes, sliced to only include those
199
+ # @return [Hash] the object's attributes, sliced to only include those
194
200
  # implemented by the decorator.
195
201
  def attributes
196
- source.attributes.select {|attribute, _| respond_to?(attribute) }
202
+ object.attributes.select {|attribute, _| respond_to?(attribute) }
197
203
  end
198
204
 
199
205
  # ActiveModel compatibility
200
206
  delegate :to_param, :to_partial_path
201
207
 
202
208
  # ActiveModel compatibility
203
- singleton_class.delegate :model_name, to: :source_class
209
+ singleton_class.delegate :model_name, to: :object_class
204
210
 
205
211
  # @return [Class] the class created by {decorate_collection}.
206
212
  def self.collection_decorator_class
@@ -213,13 +219,13 @@ module Draper
213
219
 
214
220
  private
215
221
 
216
- def self.source_name
222
+ def self.object_class_name
217
223
  raise NameError if name.nil? || name.demodulize !~ /.+Decorator$/
218
224
  name.chomp("Decorator")
219
225
  end
220
226
 
221
- def self.inferred_source_class
222
- name = source_name
227
+ def self.inferred_object_class
228
+ name = object_class_name
223
229
  name.constantize
224
230
  rescue NameError => error
225
231
  raise if name && !error.missing_name?(name)
@@ -227,15 +233,15 @@ module Draper
227
233
  end
228
234
 
229
235
  def self.collection_decorator_name
230
- plural = source_name.pluralize
231
- raise NameError if plural == source_name
236
+ plural = object_class_name.pluralize
237
+ raise NameError if plural == object_class_name
232
238
  "#{plural}Decorator"
233
239
  end
234
240
 
235
241
  def handle_multiple_decoration(options)
236
- if source.applied_decorators.last == self.class
237
- @context = source.context unless options.has_key?(:context)
238
- @source = source.source
242
+ if object.applied_decorators.last == self.class
243
+ @context = object.context unless options.has_key?(:context)
244
+ @object = object.object
239
245
  else
240
246
  warn "Reapplying #{self.class} decorator to target that is already decorated with it. Call stack:\n#{caller(1).join("\n")}"
241
247
  end
@@ -2,12 +2,12 @@ module Draper
2
2
  module Delegation
3
3
  # @overload delegate(*methods, options = {})
4
4
  # Overrides {http://api.rubyonrails.org/classes/Module.html#method-i-delegate Module.delegate}
5
- # to make `:source` the default delegation target.
5
+ # to make `:object` the default delegation target.
6
6
  #
7
7
  # @return [void]
8
8
  def delegate(*methods)
9
9
  options = methods.extract_options!
10
- super *methods, options.reverse_merge(to: :source)
10
+ super *methods, options.reverse_merge(to: :object)
11
11
  end
12
12
  end
13
13
  end
@@ -18,7 +18,7 @@ module Draper
18
18
  # Decorates an object, inferring whether to create a singular or collection
19
19
  # decorator from the type of object passed.
20
20
  #
21
- # @param [Object] source
21
+ # @param [Object] object
22
22
  # object to decorate.
23
23
  # @option options [Hash] context
24
24
  # extra data to be stored in the decorator. Overrides any context passed
@@ -26,9 +26,9 @@ module Draper
26
26
  # @option options [Object, Array] context_args (nil)
27
27
  # argument(s) to be passed to the context proc.
28
28
  # @return [Decorator, CollectionDecorator] the decorated object.
29
- def decorate(source, options = {})
30
- return nil if source.nil?
31
- Worker.new(decorator_class, source).call(options.reverse_merge(default_options))
29
+ def decorate(object, options = {})
30
+ return nil if object.nil?
31
+ Worker.new(decorator_class, object).call(options.reverse_merge(default_options))
32
32
  end
33
33
 
34
34
  private
@@ -37,29 +37,33 @@ module Draper
37
37
 
38
38
  # @private
39
39
  class Worker
40
- def initialize(decorator_class, source)
40
+ def initialize(decorator_class, object)
41
41
  @decorator_class = decorator_class
42
- @source = source
42
+ @object = object
43
43
  end
44
44
 
45
45
  def call(options)
46
46
  update_context options
47
- decorator.call(source, options)
47
+ decorator.call(object, options)
48
48
  end
49
49
 
50
50
  def decorator
51
51
  return decorator_method(decorator_class) if decorator_class
52
- return source_decorator if decoratable?
52
+ return object_decorator if decoratable?
53
53
  return decorator_method(Draper::CollectionDecorator) if collection?
54
- raise Draper::UninferrableDecoratorError.new(source.class)
54
+ raise Draper::UninferrableDecoratorError.new(object.class)
55
55
  end
56
56
 
57
57
  private
58
58
 
59
- attr_reader :decorator_class, :source
59
+ attr_reader :decorator_class, :object
60
60
 
61
- def source_decorator
62
- ->(source, options) { source.decorate(options) }
61
+ def object_decorator
62
+ if collection?
63
+ ->(object, options) { object.decorator_class.decorate_collection(object, options.reverse_merge(with: nil))}
64
+ else
65
+ ->(object, options) { object.decorate(options) }
66
+ end
63
67
  end
64
68
 
65
69
  def decorator_method(klass)
@@ -71,11 +75,11 @@ module Draper
71
75
  end
72
76
 
73
77
  def collection?
74
- source.respond_to?(:first)
78
+ object.respond_to?(:first)
75
79
  end
76
80
 
77
81
  def decoratable?
78
- source.respond_to?(:decorate)
82
+ object.respond_to?(:decorate)
79
83
  end
80
84
 
81
85
  def update_context(options)
@@ -5,26 +5,26 @@ module Draper
5
5
  module Finders
6
6
 
7
7
  def find(id, options = {})
8
- decorate(source_class.find(id), options)
8
+ decorate(object_class.find(id), options)
9
9
  end
10
10
 
11
11
  def all(options = {})
12
- decorate_collection(source_class.all, options)
12
+ decorate_collection(object_class.all, options)
13
13
  end
14
14
 
15
15
  def first(options = {})
16
- decorate(source_class.first, options)
16
+ decorate(object_class.first, options)
17
17
  end
18
18
 
19
19
  def last(options = {})
20
- decorate(source_class.last, options)
20
+ decorate(object_class.last, options)
21
21
  end
22
22
 
23
23
  # Decorates dynamic finder methods (`find_all_by_` and friends).
24
24
  def method_missing(method, *args, &block)
25
25
  return super unless method =~ /^find_(all_|last_|or_(initialize_|create_))?by_/
26
26
 
27
- result = source_class.send(method, *args, &block)
27
+ result = object_class.send(method, *args, &block)
28
28
  options = args.extract_options!
29
29
 
30
30
  if method =~ /^find_all/