activemodel 4.0.13 → 4.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activemodel might be problematic. Click here for more details.

@@ -41,7 +41,7 @@ module ActiveModel
41
41
  end
42
42
 
43
43
  # Returns an Enumerable of all key attributes if any is set, regardless if
44
- # the object is persisted or not. If there no key attributes, returns +nil+.
44
+ # the object is persisted or not. Returns +nil+ if there are no key attributes.
45
45
  #
46
46
  # class Person < ActiveRecord::Base
47
47
  # end
@@ -62,7 +62,7 @@ module ActiveModel
62
62
  # person = Person.create
63
63
  # person.to_param # => "1"
64
64
  def to_param
65
- (persisted? && key = to_key) ? key.join('-') : nil
65
+ persisted? ? to_key.join('-') : nil
66
66
  end
67
67
 
68
68
  # Returns a +string+ identifying the path associated with the object.
@@ -14,13 +14,9 @@ module ActiveModel
14
14
  # track.
15
15
  # * Call <tt>attr_name_will_change!</tt> before each change to the tracked
16
16
  # attribute.
17
- #
18
- # If you wish to also track previous changes on save or update, you need to
19
- # add:
20
- #
21
- # @previously_changed = changes
22
- #
23
- # inside of your save or update method.
17
+ # * Call <tt>changes_applied</tt> after the changes are persisted.
18
+ # * Call <tt>reset_changes</tt> when you want to reset the changes
19
+ # information.
24
20
  #
25
21
  # A minimal implementation could be:
26
22
  #
@@ -39,8 +35,12 @@ module ActiveModel
39
35
  # end
40
36
  #
41
37
  # def save
42
- # @previously_changed = changes
43
- # @changed_attributes.clear
38
+ # # do persistence work
39
+ # changes_applied
40
+ # end
41
+ #
42
+ # def reload!
43
+ # reset_changes
44
44
  # end
45
45
  # end
46
46
  #
@@ -65,6 +65,12 @@ module ActiveModel
65
65
  # person.changed? # => false
66
66
  # person.name_changed? # => false
67
67
  #
68
+ # Reset the changes:
69
+ #
70
+ # person.previous_changes # => {"name" => ["Uncle Bob", "Bill"]}
71
+ # person.reload!
72
+ # person.previous_changes # => {}
73
+ #
68
74
  # Assigning the same value leaves the attribute unchanged:
69
75
  #
70
76
  # person.name = 'Bill'
@@ -91,7 +97,7 @@ module ActiveModel
91
97
 
92
98
  included do
93
99
  attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
94
- attribute_method_affix :prefix => 'reset_', :suffix => '!'
100
+ attribute_method_affix prefix: 'reset_', suffix: '!'
95
101
  end
96
102
 
97
103
  # Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
@@ -129,7 +135,7 @@ module ActiveModel
129
135
  # person.save
130
136
  # person.previous_changes # => {"name" => ["bob", "robert"]}
131
137
  def previous_changes
132
- @previously_changed
138
+ @previously_changed ||= {}
133
139
  end
134
140
 
135
141
  # Returns a hash of the attributes with unsaved changes indicating their original
@@ -139,14 +145,31 @@ module ActiveModel
139
145
  # person.name = 'robert'
140
146
  # person.changed_attributes # => {"name" => "bob"}
141
147
  def changed_attributes
142
- @changed_attributes ||= {}
148
+ @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
149
+ end
150
+
151
+ # Handle <tt>*_changed?</tt> for +method_missing+.
152
+ def attribute_changed?(attr) # :nodoc:
153
+ changed_attributes.include?(attr)
154
+ end
155
+
156
+ # Handle <tt>*_was</tt> for +method_missing+.
157
+ def attribute_was(attr) # :nodoc:
158
+ attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
143
159
  end
144
160
 
145
161
  private
146
162
 
147
- # Handle <tt>*_changed?</tt> for +method_missing+.
148
- def attribute_changed?(attr)
149
- changed_attributes.include?(attr)
163
+ # Removes current changes and makes them accessible through +previous_changes+.
164
+ def changes_applied
165
+ @previously_changed = changes
166
+ @changed_attributes = {}
167
+ end
168
+
169
+ # Removes all dirty data: current changes and previous changes
170
+ def reset_changes
171
+ @previously_changed = {}
172
+ @changed_attributes = {}
150
173
  end
151
174
 
152
175
  # Handle <tt>*_change</tt> for +method_missing+.
@@ -154,11 +177,6 @@ module ActiveModel
154
177
  [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
155
178
  end
156
179
 
157
- # Handle <tt>*_was</tt> for +method_missing+.
158
- def attribute_was(attr)
159
- attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
160
- end
161
-
162
180
  # Handle <tt>*_will_change!</tt> for +method_missing+.
163
181
  def attribute_will_change!(attr)
164
182
  return if attribute_changed?(attr)
@@ -231,7 +231,7 @@ module ActiveModel
231
231
  # # <error>name must be specified</error>
232
232
  # # </errors>
233
233
  def to_xml(options={})
234
- to_a.to_xml({ :root => "errors", :skip_types => true }.merge!(options))
234
+ to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
235
235
  end
236
236
 
237
237
  # Returns a Hash that can be used as the JSON representation for this
@@ -289,7 +289,7 @@ module ActiveModel
289
289
  # # => NameIsInvalid: name is invalid
290
290
  #
291
291
  # person.errors.messages # => {}
292
- def add(attribute, message = nil, options = {})
292
+ def add(attribute, message = :invalid, options = {})
293
293
  message = normalize_message(attribute, message, options)
294
294
  if exception = options[:strict]
295
295
  exception = ActiveModel::StrictValidationFailed if exception == true
@@ -331,7 +331,7 @@ module ActiveModel
331
331
  #
332
332
  # person.errors.add :name, :blank
333
333
  # person.errors.added? :name, :blank # => true
334
- def added?(attribute, message = nil, options = {})
334
+ def added?(attribute, message = :invalid, options = {})
335
335
  message = normalize_message(attribute, message, options)
336
336
  self[attribute].include? message
337
337
  end
@@ -370,11 +370,11 @@ module ActiveModel
370
370
  def full_message(attribute, message)
371
371
  return message if attribute == :base
372
372
  attr_name = attribute.to_s.tr('.', '_').humanize
373
- attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
373
+ attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
374
374
  I18n.t(:"errors.format", {
375
- :default => "%{attribute} %{message}",
376
- :attribute => attr_name,
377
- :message => message
375
+ default: "%{attribute} %{message}",
376
+ attribute: attr_name,
377
+ message: message
378
378
  })
379
379
  end
380
380
 
@@ -426,10 +426,10 @@ module ActiveModel
426
426
  value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
427
427
 
428
428
  options = {
429
- :default => defaults,
430
- :model => @base.class.model_name.human,
431
- :attribute => @base.class.human_attribute_name(attribute),
432
- :value => value
429
+ default: defaults,
430
+ model: @base.class.model_name.human,
431
+ attribute: @base.class.human_attribute_name(attribute),
432
+ value: value
433
433
  }.merge!(options)
434
434
 
435
435
  I18n.translate(key, options)
@@ -437,8 +437,6 @@ module ActiveModel
437
437
 
438
438
  private
439
439
  def normalize_message(attribute, message, options)
440
- message ||= :invalid
441
-
442
440
  case message
443
441
  when Symbol
444
442
  generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
@@ -23,6 +23,5 @@ module ActiveModel
23
23
  attributes
24
24
  end
25
25
  end
26
- alias :sanitize_forbidden_attributes :sanitize_for_mass_assignment
27
26
  end
28
27
  end
@@ -129,7 +129,7 @@ module ActiveModel
129
129
  #
130
130
  # Equivalent to +to_s+.
131
131
  delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
132
- :to_str, :to => :name
132
+ :to_str, to: :name
133
133
 
134
134
  # Returns a new ActiveModel::Name instance. By default, the +namespace+
135
135
  # and +name+ option will take the namespace and name of the given class
@@ -183,7 +183,7 @@ module ActiveModel
183
183
  defaults << options[:default] if options[:default]
184
184
  defaults << @human
185
185
 
186
- options = { :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults }.merge!(options.except(:default))
186
+ options = { scope: [@klass.i18n_scope, :models], count: 1, default: defaults }.merge!(options.except(:default))
187
187
  I18n.translate(defaults.shift, options)
188
188
  end
189
189
 
@@ -262,10 +262,10 @@ module ActiveModel
262
262
  # namespaced models regarding whether it's inside isolated engine.
263
263
  #
264
264
  # # For isolated engine:
265
- # ActiveModel::Naming.singular_route_key(Blog::Post) #=> "post"
265
+ # ActiveModel::Naming.singular_route_key(Blog::Post) # => "post"
266
266
  #
267
267
  # # For shared engine:
268
- # ActiveModel::Naming.singular_route_key(Blog::Post) #=> "blog_post"
268
+ # ActiveModel::Naming.singular_route_key(Blog::Post) # => "blog_post"
269
269
  def self.singular_route_key(record_or_class)
270
270
  model_name_from_record_or_class(record_or_class).singular_route_key
271
271
  end
@@ -274,10 +274,10 @@ module ActiveModel
274
274
  # namespaced models regarding whether it's inside isolated engine.
275
275
  #
276
276
  # # For isolated engine:
277
- # ActiveModel::Naming.route_key(Blog::Post) #=> "posts"
277
+ # ActiveModel::Naming.route_key(Blog::Post) # => "posts"
278
278
  #
279
279
  # # For shared engine:
280
- # ActiveModel::Naming.route_key(Blog::Post) #=> "blog_posts"
280
+ # ActiveModel::Naming.route_key(Blog::Post) # => "blog_posts"
281
281
  #
282
282
  # The route key also considers if the noun is uncountable and, in
283
283
  # such cases, automatically appends _index.
@@ -289,10 +289,10 @@ module ActiveModel
289
289
  # namespaced models regarding whether it's inside isolated engine.
290
290
  #
291
291
  # # For isolated engine:
292
- # ActiveModel::Naming.param_key(Blog::Post) #=> "post"
292
+ # ActiveModel::Naming.param_key(Blog::Post) # => "post"
293
293
  #
294
294
  # # For shared engine:
295
- # ActiveModel::Naming.param_key(Blog::Post) #=> "blog_post"
295
+ # ActiveModel::Naming.param_key(Blog::Post) # => "blog_post"
296
296
  def self.param_key(record_or_class)
297
297
  model_name_from_record_or_class(record_or_class).param_key
298
298
  end
@@ -2,7 +2,9 @@ module ActiveModel
2
2
  module SecurePassword
3
3
  extend ActiveSupport::Concern
4
4
 
5
- class << self; attr_accessor :min_cost; end
5
+ class << self
6
+ attr_accessor :min_cost # :nodoc:
7
+ end
6
8
  self.min_cost = false
7
9
 
8
10
  module ClassMethods
@@ -15,12 +17,12 @@ module ActiveModel
15
17
  # argument. You can add more validations by hand if need be.
16
18
  #
17
19
  # If you don't need the confirmation validation, just don't set any
18
- # value to the password_confirmation attribute and the the validation
20
+ # value to the password_confirmation attribute and the validation
19
21
  # will not be triggered.
20
22
  #
21
- # You need to add bcrypt (~> 3.1.7) to Gemfile to use #has_secure_password:
23
+ # You need to add bcrypt-ruby (~> 3.1.2) to Gemfile to use #has_secure_password:
22
24
  #
23
- # gem 'bcrypt', '~> 3.1.7'
25
+ # gem 'bcrypt-ruby', '~> 3.1.2'
24
26
  #
25
27
  # Example using Active Record (which automatically includes ActiveModel::SecurePassword):
26
28
  #
@@ -40,13 +42,13 @@ module ActiveModel
40
42
  # User.find_by(name: 'david').try(:authenticate, 'notright') # => false
41
43
  # User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user
42
44
  def has_secure_password(options = {})
43
- # Load bcrypt gem only when has_secure_password is used.
45
+ # Load bcrypt-ruby only when has_secure_password is used.
44
46
  # This is to avoid ActiveModel (and by extension the entire framework)
45
47
  # being dependent on a binary library.
46
48
  begin
47
49
  require 'bcrypt'
48
50
  rescue LoadError
49
- $stderr.puts "You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install"
51
+ $stderr.puts "You don't have bcrypt-ruby installed in your application. Please add it to your Gemfile and run bundle install"
50
52
  raise
51
53
  end
52
54
 
@@ -55,9 +57,9 @@ module ActiveModel
55
57
  include InstanceMethodsOnActivation
56
58
 
57
59
  if options.fetch(:validations, true)
58
- validates_confirmation_of :password, if: lambda { |m| m.password.present? }
59
- validates_presence_of :password, :on => :create
60
- validates_presence_of :password_confirmation, if: lambda { |m| m.password.present? }
60
+ validates_confirmation_of :password, if: :should_confirm_password?
61
+ validates_presence_of :password, on: :create
62
+ validates_presence_of :password_confirmation, if: :should_confirm_password?
61
63
 
62
64
  before_create { raise "Password digest missing on new record" if password_digest.blank? }
63
65
  end
@@ -108,6 +110,12 @@ module ActiveModel
108
110
  def password_confirmation=(unencrypted_password)
109
111
  @password_confirmation = unencrypted_password
110
112
  end
113
+
114
+ private
115
+
116
+ def should_confirm_password?
117
+ password_confirmation && password.present?
118
+ end
111
119
  end
112
120
  end
113
121
  end
@@ -109,7 +109,7 @@ module ActiveModel
109
109
  #
110
110
  # def attributes=(hash)
111
111
  # hash.each do |key, value|
112
- # instance_variable_set("@#{key}", value)
112
+ # send("#{key}=", value)
113
113
  # end
114
114
  # end
115
115
  #
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/class/attribute_accessors'
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
2
  require 'active_support/core_ext/array/conversions'
3
3
  require 'active_support/core_ext/hash/conversions'
4
4
  require 'active_support/core_ext/hash/slice'
@@ -79,7 +79,7 @@ module ActiveModel
79
79
  require 'builder' unless defined? ::Builder
80
80
 
81
81
  options[:indent] ||= 2
82
- options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
82
+ options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])
83
83
 
84
84
  @builder = options[:builder]
85
85
  @builder.instruct! unless options[:skip_instruct]
@@ -88,8 +88,8 @@ module ActiveModel
88
88
  root = ActiveSupport::XmlMini.rename_key(root, options)
89
89
 
90
90
  args = [root]
91
- args << {:xmlns => options[:namespace]} if options[:namespace]
92
- args << {:type => options[:type]} if options[:type] && !options[:skip_types]
91
+ args << { xmlns: options[:namespace] } if options[:namespace]
92
+ args << { type: options[:type] } if options[:type] && !options[:skip_types]
93
93
 
94
94
  @builder.tag!(*args) do
95
95
  add_attributes_and_methods
@@ -132,7 +132,7 @@ module ActiveModel
132
132
  records = records.to_ary
133
133
 
134
134
  tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
135
- type = options[:skip_types] ? { } : {:type => "array"}
135
+ type = options[:skip_types] ? { } : { type: "array" }
136
136
  association_name = association.to_s.singularize
137
137
  merged_options[:root] = association_name
138
138
 
@@ -145,7 +145,7 @@ module ActiveModel
145
145
  record_type = {}
146
146
  else
147
147
  record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
148
- record_type = {:type => record_class}
148
+ record_type = { type: record_class }
149
149
  end
150
150
 
151
151
  record.to_xml merged_options.merge(record_type)
@@ -205,7 +205,7 @@ module ActiveModel
205
205
  Serializer.new(self, options).serialize(&block)
206
206
  end
207
207
 
208
- # Sets the model +attributes+ from a JSON string. Returns +self+.
208
+ # Sets the model +attributes+ from an XML string. Returns +self+.
209
209
  #
210
210
  # class Person
211
211
  # include ActiveModel::Serializers::Xml
@@ -41,7 +41,7 @@ module ActiveModel
41
41
  #
42
42
  # Specify +options+ with additional translating options.
43
43
  def human_attribute_name(attribute, options = {})
44
- options = { :count => 1 }.merge!(options)
44
+ options = { count: 1 }.merge!(options)
45
45
  parts = attribute.to_s.split(".")
46
46
  attribute = parts.pop
47
47
  namespace = parts.join("/") unless parts.empty?
@@ -46,7 +46,7 @@ module ActiveModel
46
46
  include HelperMethods
47
47
 
48
48
  attr_accessor :validation_context
49
- define_callbacks :validate, :scope => :name
49
+ define_callbacks :validate, scope: :name
50
50
 
51
51
  class_attribute :_validators
52
52
  self._validators = Hash.new { |h,k| h[k] = [] }
@@ -142,7 +142,9 @@ module ActiveModel
142
142
  if options.key?(:on)
143
143
  options = options.dup
144
144
  options[:if] = Array(options[:if])
145
- options[:if].unshift("validation_context == :#{options[:on]}")
145
+ options[:if].unshift lambda { |o|
146
+ o.validation_context == options[:on]
147
+ }
146
148
  end
147
149
  args << options
148
150
  set_callback(:validate, *args, &block)
@@ -226,7 +228,6 @@ module ActiveModel
226
228
  # Person.validators_on(:name)
227
229
  # # => [
228
230
  # # #<ActiveModel::Validations::PresenceValidator:0x007fe604914e60 @attributes=[:name], @options={}>,
229
- # # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={in:0..99}>
230
231
  # # ]
231
232
  def validators_on(*attributes)
232
233
  attributes.flat_map do |attribute|
@@ -3,7 +3,8 @@ module ActiveModel
3
3
  module Validations
4
4
  class AcceptanceValidator < EachValidator # :nodoc:
5
5
  def initialize(options)
6
- super({ :allow_nil => true, :accept => "1" }.merge!(options))
6
+ super({ allow_nil: true, accept: "1" }.merge!(options))
7
+ setup!(options[:class])
7
8
  end
8
9
 
9
10
  def validate_each(record, attribute, value)
@@ -12,7 +13,8 @@ module ActiveModel
12
13
  end
13
14
  end
14
15
 
15
- def setup(klass)
16
+ private
17
+ def setup!(klass)
16
18
  attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
17
19
  attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
18
20
  klass.send(:attr_reader, *attr_readers)
@@ -22,7 +22,10 @@ module ActiveModel
22
22
 
23
23
  included do
24
24
  include ActiveSupport::Callbacks
25
- define_callbacks :validation, :terminator => "result == false", :skip_after_callbacks_if_terminated => true, :scope => [:kind, :name]
25
+ define_callbacks :validation,
26
+ terminator: ->(_,result) { result == false },
27
+ skip_after_callbacks_if_terminated: true,
28
+ scope: [:kind, :name]
26
29
  end
27
30
 
28
31
  module ClassMethods
@@ -55,7 +58,9 @@ module ActiveModel
55
58
  if options.is_a?(Hash) && options[:on]
56
59
  options[:if] = Array(options[:if])
57
60
  options[:on] = Array(options[:on])
58
- options[:if].unshift("#{options[:on]}.include? self.validation_context")
61
+ options[:if].unshift lambda { |o|
62
+ options[:on].include? o.validation_context
63
+ }
59
64
  end
60
65
  set_callback(:validation, :before, *args, &block)
61
66
  end