activemodel 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +85 -64
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +61 -24
  5. data/lib/active_model.rb +21 -11
  6. data/lib/active_model/attribute_methods.rb +150 -125
  7. data/lib/active_model/callbacks.rb +49 -34
  8. data/lib/active_model/conversion.rb +39 -19
  9. data/lib/active_model/deprecated_mass_assignment_security.rb +21 -0
  10. data/lib/active_model/dirty.rb +48 -32
  11. data/lib/active_model/errors.rb +176 -88
  12. data/lib/active_model/forbidden_attributes_protection.rb +27 -0
  13. data/lib/active_model/lint.rb +42 -55
  14. data/lib/active_model/locale/en.yml +3 -1
  15. data/lib/active_model/model.rb +97 -0
  16. data/lib/active_model/naming.rb +191 -51
  17. data/lib/active_model/railtie.rb +11 -1
  18. data/lib/active_model/secure_password.rb +55 -25
  19. data/lib/active_model/serialization.rb +51 -27
  20. data/lib/active_model/serializers/json.rb +83 -46
  21. data/lib/active_model/serializers/xml.rb +46 -12
  22. data/lib/active_model/test_case.rb +0 -12
  23. data/lib/active_model/translation.rb +9 -10
  24. data/lib/active_model/validations.rb +154 -52
  25. data/lib/active_model/validations/absence.rb +31 -0
  26. data/lib/active_model/validations/acceptance.rb +10 -22
  27. data/lib/active_model/validations/callbacks.rb +78 -25
  28. data/lib/active_model/validations/clusivity.rb +41 -0
  29. data/lib/active_model/validations/confirmation.rb +13 -23
  30. data/lib/active_model/validations/exclusion.rb +26 -55
  31. data/lib/active_model/validations/format.rb +44 -34
  32. data/lib/active_model/validations/inclusion.rb +22 -52
  33. data/lib/active_model/validations/length.rb +48 -49
  34. data/lib/active_model/validations/numericality.rb +30 -32
  35. data/lib/active_model/validations/presence.rb +12 -22
  36. data/lib/active_model/validations/validates.rb +68 -36
  37. data/lib/active_model/validations/with.rb +28 -23
  38. data/lib/active_model/validator.rb +22 -22
  39. data/lib/active_model/version.rb +4 -4
  40. metadata +23 -24
  41. data/lib/active_model/mass_assignment_security.rb +0 -237
  42. data/lib/active_model/mass_assignment_security/permission_set.rb +0 -40
  43. data/lib/active_model/mass_assignment_security/sanitizer.rb +0 -59
  44. data/lib/active_model/observer_array.rb +0 -147
  45. data/lib/active_model/observing.rb +0 -252
@@ -1,13 +1,12 @@
1
- require 'active_support/core_ext/array/wrap'
2
- require 'active_support/callbacks'
1
+ require 'active_support/core_ext/array/extract_options'
3
2
 
4
3
  module ActiveModel
5
- # == Active Model Callbacks
4
+ # == Active \Model \Callbacks
6
5
  #
7
6
  # Provides an interface for any class to have Active Record like callbacks.
8
7
  #
9
8
  # Like the Active Record methods, the callback chain is aborted as soon as
10
- # one of the methods in the chain returns false.
9
+ # one of the methods in the chain returns +false+.
11
10
  #
12
11
  # First, extend ActiveModel::Callbacks from the class you are creating:
13
12
  #
@@ -19,9 +18,10 @@ module ActiveModel
19
18
  #
20
19
  # define_model_callbacks :create, :update
21
20
  #
22
- # This will provide all three standard callbacks (before, around and after) for
23
- # both the :create and :update methods. To implement, you need to wrap the methods
24
- # you want callbacks on in a block so that the callbacks get a chance to fire:
21
+ # This will provide all three standard callbacks (before, around and after)
22
+ # for both the <tt>:create</tt> and <tt>:update</tt> methods. To implement,
23
+ # you need to wrap the methods you want callbacks on in a block so that the
24
+ # callbacks get a chance to fire:
25
25
  #
26
26
  # def create
27
27
  # run_callbacks :create do
@@ -29,8 +29,8 @@ module ActiveModel
29
29
  # end
30
30
  # end
31
31
  #
32
- # Then in your class, you can use the +before_create+, +after_create+ and +around_create+
33
- # methods, just as you would in an Active Record module.
32
+ # Then in your class, you can use the +before_create+, +after_create+ and
33
+ # +around_create+ methods, just as you would in an Active Record module.
34
34
  #
35
35
  # before_create :action_before_create
36
36
  #
@@ -38,39 +38,52 @@ module ActiveModel
38
38
  # # Your code here
39
39
  # end
40
40
  #
41
+ # When defining an around callback remember to yield to the block, otherwise
42
+ # it won't be executed:
43
+ #
44
+ # around_create :log_status
45
+ #
46
+ # def log_status
47
+ # puts 'going to call the block...'
48
+ # yield
49
+ # puts 'block successfully called.'
50
+ # end
51
+ #
41
52
  # You can choose not to have all three callbacks by passing a hash to the
42
- # define_model_callbacks method.
53
+ # +define_model_callbacks+ method.
43
54
  #
44
- # define_model_callbacks :create, :only => [:after, :before]
55
+ # define_model_callbacks :create, only: [:after, :before]
45
56
  #
46
- # Would only create the after_create and before_create callback methods in your
47
- # class.
57
+ # Would only create the +after_create+ and +before_create+ callback methods in
58
+ # your class.
48
59
  module Callbacks
49
- def self.extended(base)
60
+ def self.extended(base) #:nodoc:
50
61
  base.class_eval do
51
62
  include ActiveSupport::Callbacks
52
63
  end
53
64
  end
54
65
 
55
- # define_model_callbacks accepts the same options define_callbacks does, in case
56
- # you want to overwrite a default. Besides that, it also accepts an :only option,
57
- # where you can choose if you want all types (before, around or after) or just some.
66
+ # define_model_callbacks accepts the same options +define_callbacks+ does,
67
+ # in case you want to overwrite a default. Besides that, it also accepts an
68
+ # <tt>:only</tt> option, where you can choose if you want all types (before,
69
+ # around or after) or just some.
58
70
  #
59
- # define_model_callbacks :initializer, :only => :after
71
+ # define_model_callbacks :initializer, only: :after
60
72
  #
61
- # Note, the <tt>:only => <type></tt> hash will apply to all callbacks defined on
62
- # that method call. To get around this you can call the define_model_callbacks
73
+ # Note, the <tt>only: <type></tt> hash will apply to all callbacks defined
74
+ # on that method call. To get around this you can call the define_model_callbacks
63
75
  # method as many times as you need.
64
76
  #
65
- # define_model_callbacks :create, :only => :after
66
- # define_model_callbacks :update, :only => :before
67
- # define_model_callbacks :destroy, :only => :around
77
+ # define_model_callbacks :create, only: :after
78
+ # define_model_callbacks :update, only: :before
79
+ # define_model_callbacks :destroy, only: :around
68
80
  #
69
- # Would create +after_create+, +before_update+ and +around_destroy+ methods only.
81
+ # Would create +after_create+, +before_update+ and +around_destroy+ methods
82
+ # only.
70
83
  #
71
- # You can pass in a class to before_<type>, after_<type> and around_<type>, in which
72
- # case the callback will call that class's <action>_<type> method passing the object
73
- # that the callback is being called on.
84
+ # You can pass in a class to before_<type>, after_<type> and around_<type>,
85
+ # in which case the callback will call that class's <action>_<type> method
86
+ # passing the object that the callback is being called on.
74
87
  #
75
88
  # class MyModel
76
89
  # extend ActiveModel::Callbacks
@@ -84,16 +97,16 @@ module ActiveModel
84
97
  # # obj is the MyModel instance that the callback is being called on
85
98
  # end
86
99
  # end
87
- #
88
100
  def define_model_callbacks(*callbacks)
89
101
  options = callbacks.extract_options!
90
102
  options = {
91
- :terminator => "result == false",
92
- :scope => [:kind, :name],
93
- :only => [:before, :around, :after]
94
- }.merge(options)
103
+ :terminator => "result == false",
104
+ :skip_after_callbacks_if_terminated => true,
105
+ :scope => [:kind, :name],
106
+ :only => [:before, :around, :after]
107
+ }.merge!(options)
95
108
 
96
- types = Array.wrap(options.delete(:only))
109
+ types = Array(options.delete(:only))
97
110
 
98
111
  callbacks.each do |callback|
99
112
  define_callbacks(callback, options)
@@ -104,6 +117,8 @@ module ActiveModel
104
117
  end
105
118
  end
106
119
 
120
+ private
121
+
107
122
  def _define_before_model_callback(klass, callback) #:nodoc:
108
123
  klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
109
124
  def self.before_#{callback}(*args, &block)
@@ -125,7 +140,7 @@ module ActiveModel
125
140
  def self.after_#{callback}(*args, &block)
126
141
  options = args.extract_options!
127
142
  options[:prepend] = true
128
- options[:if] = Array.wrap(options[:if]) << "!halted && value != false"
143
+ options[:if] = Array(options[:if]) << "value != false"
129
144
  set_callback(:#{callback}, :after, *(args << options), &block)
130
145
  end
131
146
  CALLBACK
@@ -1,8 +1,5 @@
1
- require 'active_support/concern'
2
- require 'active_support/inflector'
3
-
4
1
  module ActiveModel
5
- # == Active Model Conversions
2
+ # == Active \Model Conversions
6
3
  #
7
4
  # Handles default conversions: to_model, to_key, to_param, and to_partial_path.
8
5
  #
@@ -18,17 +15,23 @@ module ActiveModel
18
15
  # end
19
16
  #
20
17
  # cm = ContactMessage.new
21
- # cm.to_model == self # => true
22
- # cm.to_key # => nil
23
- # cm.to_param # => nil
24
- # cm.to_path # => "contact_messages/contact_message"
25
- #
18
+ # cm.to_model == cm # => true
19
+ # cm.to_key # => nil
20
+ # cm.to_param # => nil
21
+ # cm.to_partial_path # => "contact_messages/contact_message"
26
22
  module Conversion
27
23
  extend ActiveSupport::Concern
28
24
 
29
25
  # If your object is already designed to implement all of the Active Model
30
26
  # you can use the default <tt>:to_model</tt> implementation, which simply
31
- # returns self.
27
+ # returns +self+.
28
+ #
29
+ # class Person
30
+ # include ActiveModel::Conversion
31
+ # end
32
+ #
33
+ # person = Person.new
34
+ # person.to_model == person # => true
32
35
  #
33
36
  # If your model does not act like an Active Model object, then you should
34
37
  # define <tt>:to_model</tt> yourself returning a proxy object that wraps
@@ -37,29 +40,46 @@ module ActiveModel
37
40
  self
38
41
  end
39
42
 
40
- # Returns an Enumerable of all key attributes if any is set, regardless
41
- # if the object is persisted or not.
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+.
45
+ #
46
+ # class Person < ActiveRecord::Base
47
+ # end
42
48
  #
43
- # Note the default implementation uses persisted? just because all objects
44
- # in Ruby 1.8.x responds to <tt>:id</tt>.
49
+ # person = Person.create
50
+ # person.to_key # => [1]
45
51
  def to_key
46
- persisted? ? [id] : nil
52
+ key = respond_to?(:id) && id
53
+ key ? [key] : nil
47
54
  end
48
55
 
49
- # Returns a string representing the object's key suitable for use in URLs,
50
- # or nil if <tt>persisted?</tt> is false.
56
+ # Returns a +string+ representing the object's key suitable for use in URLs,
57
+ # or +nil+ if <tt>persisted?</tt> is +false+.
58
+ #
59
+ # class Person < ActiveRecord::Base
60
+ # end
61
+ #
62
+ # person = Person.create
63
+ # person.to_param # => "1"
51
64
  def to_param
52
65
  persisted? ? to_key.join('-') : nil
53
66
  end
54
67
 
55
- # Returns a string identifying the path associated with the object.
68
+ # Returns a +string+ identifying the path associated with the object.
56
69
  # ActionPack uses this to find a suitable partial to represent the object.
70
+ #
71
+ # class Person
72
+ # include ActiveModel::Conversion
73
+ # end
74
+ #
75
+ # person = Person.new
76
+ # person.to_partial_path # => "people/person"
57
77
  def to_partial_path
58
78
  self.class._to_partial_path
59
79
  end
60
80
 
61
81
  module ClassMethods #:nodoc:
62
- # Provide a class level cache for the to_path. This is an
82
+ # Provide a class level cache for #to_partial_path. This is an
63
83
  # internal method and should not be accessed directly.
64
84
  def _to_partial_path #:nodoc:
65
85
  @_to_partial_path ||= begin
@@ -0,0 +1,21 @@
1
+ module ActiveModel
2
+ module DeprecatedMassAssignmentSecurity # :nodoc:
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods # :nodoc:
6
+ def attr_protected(*args)
7
+ raise "`attr_protected` is extracted out of Rails into a gem. " \
8
+ "Please use new recommended protection model for params" \
9
+ "(strong_parameters) or add `protected_attributes` to your " \
10
+ "Gemfile to use old one."
11
+ end
12
+
13
+ def attr_accessible(*args)
14
+ raise "`attr_accessible` is extracted out of Rails into a gem. " \
15
+ "Please use new recommended protection model for params" \
16
+ "(strong_parameters) or add `protected_attributes` to your " \
17
+ "Gemfile to use old one."
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,23 +1,22 @@
1
- require 'active_model/attribute_methods'
2
1
  require 'active_support/hash_with_indifferent_access'
3
2
  require 'active_support/core_ext/object/duplicable'
4
3
 
5
4
  module ActiveModel
6
- # == Active Model Dirty
5
+ # == Active \Model \Dirty
7
6
  #
8
7
  # Provides a way to track changes in your object in the same way as
9
8
  # Active Record does.
10
9
  #
11
10
  # The requirements for implementing ActiveModel::Dirty are:
12
11
  #
13
- # * <tt>include ActiveModel::Dirty</tt> in your object
12
+ # * <tt>include ActiveModel::Dirty</tt> in your object.
14
13
  # * Call <tt>define_attribute_methods</tt> passing each method you want to
15
- # track
14
+ # track.
16
15
  # * Call <tt>attr_name_will_change!</tt> before each change to the tracked
17
- # attribute
16
+ # attribute.
18
17
  #
19
18
  # If you wish to also track previous changes on save or update, you need to
20
- # add
19
+ # add:
21
20
  #
22
21
  # @previously_changed = changes
23
22
  #
@@ -26,10 +25,9 @@ module ActiveModel
26
25
  # A minimal implementation could be:
27
26
  #
28
27
  # class Person
29
- #
30
28
  # include ActiveModel::Dirty
31
29
  #
32
- # define_attribute_methods [:name]
30
+ # define_attribute_methods :name
33
31
  #
34
32
  # def name
35
33
  # @name
@@ -44,46 +42,49 @@ module ActiveModel
44
42
  # @previously_changed = changes
45
43
  # @changed_attributes.clear
46
44
  # end
47
- #
48
45
  # end
49
46
  #
50
- # == Examples:
51
- #
52
47
  # A newly instantiated object is unchanged:
48
+ #
53
49
  # person = Person.find_by_name('Uncle Bob')
54
50
  # person.changed? # => false
55
51
  #
56
52
  # Change the name:
53
+ #
57
54
  # person.name = 'Bob'
58
55
  # person.changed? # => true
59
56
  # person.name_changed? # => true
60
- # person.name_was # => 'Uncle Bob'
61
- # person.name_change # => ['Uncle Bob', 'Bob']
57
+ # person.name_was # => "Uncle Bob"
58
+ # person.name_change # => ["Uncle Bob", "Bob"]
62
59
  # person.name = 'Bill'
63
- # person.name_change # => ['Uncle Bob', 'Bill']
60
+ # person.name_change # => ["Uncle Bob", "Bill"]
64
61
  #
65
62
  # Save the changes:
63
+ #
66
64
  # person.save
67
65
  # person.changed? # => false
68
66
  # person.name_changed? # => false
69
67
  #
70
68
  # Assigning the same value leaves the attribute unchanged:
69
+ #
71
70
  # person.name = 'Bill'
72
71
  # person.name_changed? # => false
73
72
  # person.name_change # => nil
74
73
  #
75
74
  # Which attributes have changed?
75
+ #
76
76
  # person.name = 'Bob'
77
- # person.changed # => ['name']
78
- # person.changes # => { 'name' => ['Bill', 'Bob'] }
77
+ # person.changed # => ["name"]
78
+ # person.changes # => {"name" => ["Bill", "Bob"]}
79
79
  #
80
80
  # If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
81
- # to mark that the attribute is changing. Otherwise ActiveModel can't track changes to
82
- # in-place attributes.
81
+ # to mark that the attribute is changing. Otherwise ActiveModel can't track
82
+ # changes to in-place attributes.
83
83
  #
84
84
  # person.name_will_change!
85
+ # person.name_change # => ["Bill", "Bill"]
85
86
  # person.name << 'y'
86
- # person.name_change # => ['Bill', 'Billy']
87
+ # person.name_change # => ["Bill", "Billy"]
87
88
  module Dirty
88
89
  extend ActiveSupport::Concern
89
90
  include ActiveModel::AttributeMethods
@@ -93,40 +94,50 @@ module ActiveModel
93
94
  attribute_method_affix :prefix => 'reset_', :suffix => '!'
94
95
  end
95
96
 
96
- # Returns true if any attribute have unsaved changes, false otherwise.
97
+ # Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
98
+ #
97
99
  # person.changed? # => false
98
100
  # person.name = 'bob'
99
101
  # person.changed? # => true
100
102
  def changed?
101
- changed_attributes.any?
103
+ changed_attributes.present?
102
104
  end
103
105
 
104
- # List of attributes with unsaved changes.
106
+ # Returns an array with the name of the attributes with unsaved changes.
107
+ #
105
108
  # person.changed # => []
106
109
  # person.name = 'bob'
107
- # person.changed # => ['name']
110
+ # person.changed # => ["name"]
108
111
  def changed
109
112
  changed_attributes.keys
110
113
  end
111
114
 
112
- # Map of changed attrs => [original value, new value].
115
+ # Returns a hash of changed attributes indicating their original
116
+ # and new values like <tt>attr => [original value, new value]</tt>.
117
+ #
113
118
  # person.changes # => {}
114
119
  # person.name = 'bob'
115
- # person.changes # => { 'name' => ['bill', 'bob'] }
120
+ # person.changes # => { "name" => ["bill", "bob"] }
116
121
  def changes
117
- HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
122
+ ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
118
123
  end
119
124
 
120
- # Map of attributes that were changed when the model was saved.
121
- # person.name # => 'bob'
125
+ # Returns a hash of attributes that were changed before the model was saved.
126
+ #
127
+ # person.name # => "bob"
122
128
  # person.name = 'robert'
123
129
  # person.save
124
- # person.previous_changes # => {'name' => ['bob, 'robert']}
130
+ # person.previous_changes # => {"name" => ["bob", "robert"]}
125
131
  def previous_changes
126
132
  @previously_changed
127
133
  end
128
134
 
129
- # Map of change <tt>attr => original value</tt>.
135
+ # Returns a hash of the attributes with unsaved changes indicating their original
136
+ # values like <tt>attr => original value</tt>.
137
+ #
138
+ # person.name # => "bob"
139
+ # person.name = 'robert'
140
+ # person.changed_attributes # => {"name" => "bob"}
130
141
  def changed_attributes
131
142
  @changed_attributes ||= {}
132
143
  end
@@ -150,18 +161,23 @@ module ActiveModel
150
161
 
151
162
  # Handle <tt>*_will_change!</tt> for +method_missing+.
152
163
  def attribute_will_change!(attr)
164
+ return if attribute_changed?(attr)
165
+
153
166
  begin
154
167
  value = __send__(attr)
155
168
  value = value.duplicable? ? value.clone : value
156
169
  rescue TypeError, NoMethodError
157
170
  end
158
171
 
159
- changed_attributes[attr] = value unless changed_attributes.include?(attr)
172
+ changed_attributes[attr] = value
160
173
  end
161
174
 
162
175
  # Handle <tt>reset_*!</tt> for +method_missing+.
163
176
  def reset_attribute!(attr)
164
- __send__("#{attr}=", changed_attributes[attr]) if attribute_changed?(attr)
177
+ if attribute_changed?(attr)
178
+ __send__("#{attr}=", changed_attributes[attr])
179
+ changed_attributes.delete(attr)
180
+ end
165
181
  end
166
182
  end
167
183
  end