draper 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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/