activemodel 3.2.22.5 → 4.0.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +85 -64
- data/MIT-LICENSE +1 -1
- data/README.rdoc +61 -24
- data/lib/active_model.rb +21 -11
- data/lib/active_model/attribute_methods.rb +150 -125
- data/lib/active_model/callbacks.rb +49 -34
- data/lib/active_model/conversion.rb +39 -19
- data/lib/active_model/deprecated_mass_assignment_security.rb +21 -0
- data/lib/active_model/dirty.rb +48 -32
- data/lib/active_model/errors.rb +176 -88
- data/lib/active_model/forbidden_attributes_protection.rb +27 -0
- data/lib/active_model/lint.rb +42 -55
- data/lib/active_model/locale/en.yml +3 -1
- data/lib/active_model/model.rb +97 -0
- data/lib/active_model/naming.rb +191 -51
- data/lib/active_model/railtie.rb +11 -1
- data/lib/active_model/secure_password.rb +55 -25
- data/lib/active_model/serialization.rb +51 -27
- data/lib/active_model/serializers/json.rb +83 -46
- data/lib/active_model/serializers/xml.rb +46 -12
- data/lib/active_model/test_case.rb +0 -12
- data/lib/active_model/translation.rb +9 -10
- data/lib/active_model/validations.rb +154 -52
- data/lib/active_model/validations/absence.rb +31 -0
- data/lib/active_model/validations/acceptance.rb +10 -22
- data/lib/active_model/validations/callbacks.rb +78 -25
- data/lib/active_model/validations/clusivity.rb +41 -0
- data/lib/active_model/validations/confirmation.rb +13 -23
- data/lib/active_model/validations/exclusion.rb +26 -55
- data/lib/active_model/validations/format.rb +44 -34
- data/lib/active_model/validations/inclusion.rb +22 -52
- data/lib/active_model/validations/length.rb +48 -49
- data/lib/active_model/validations/numericality.rb +30 -32
- data/lib/active_model/validations/presence.rb +12 -22
- data/lib/active_model/validations/validates.rb +68 -36
- data/lib/active_model/validations/with.rb +28 -23
- data/lib/active_model/validator.rb +22 -22
- data/lib/active_model/version.rb +4 -4
- metadata +23 -24
- data/lib/active_model/mass_assignment_security.rb +0 -237
- data/lib/active_model/mass_assignment_security/permission_set.rb +0 -40
- data/lib/active_model/mass_assignment_security/sanitizer.rb +0 -59
- data/lib/active_model/observer_array.rb +0 -147
- data/lib/active_model/observing.rb +0 -252
@@ -1,13 +1,12 @@
|
|
1
|
-
require 'active_support/core_ext/array/
|
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)
|
23
|
-
# both the
|
24
|
-
# you want callbacks on in a block so that the
|
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
|
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, :
|
55
|
+
# define_model_callbacks :create, only: [:after, :before]
|
45
56
|
#
|
46
|
-
# Would only create the after_create and before_create callback methods in
|
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,
|
56
|
-
# you want to overwrite a default. Besides that, it also accepts an
|
57
|
-
# where you can choose if you want all types (before,
|
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, :
|
71
|
+
# define_model_callbacks :initializer, only: :after
|
60
72
|
#
|
61
|
-
# Note, the <tt
|
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,
|
66
|
-
# define_model_callbacks :update,
|
67
|
-
# define_model_callbacks :destroy, :
|
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
|
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>,
|
72
|
-
# case the callback will call that class's <action>_<type> method
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
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
|
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 ==
|
22
|
-
# cm.to_key
|
23
|
-
# cm.to_param
|
24
|
-
# cm.
|
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
|
-
#
|
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
|
-
#
|
44
|
-
#
|
49
|
+
# person = Person.create
|
50
|
+
# person.to_key # => [1]
|
45
51
|
def to_key
|
46
|
-
|
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
|
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
|
data/lib/active_model/dirty.rb
CHANGED
@@ -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
|
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 # =>
|
61
|
-
# person.name_change # => [
|
57
|
+
# person.name_was # => "Uncle Bob"
|
58
|
+
# person.name_change # => ["Uncle Bob", "Bob"]
|
62
59
|
# person.name = 'Bill'
|
63
|
-
# person.name_change # => [
|
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 # => [
|
78
|
-
# person.changes # => {
|
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
|
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 # => [
|
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.
|
103
|
+
changed_attributes.present?
|
102
104
|
end
|
103
105
|
|
104
|
-
#
|
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 # => [
|
110
|
+
# person.changed # => ["name"]
|
108
111
|
def changed
|
109
112
|
changed_attributes.keys
|
110
113
|
end
|
111
114
|
|
112
|
-
#
|
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 # => {
|
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
|
-
#
|
121
|
-
#
|
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 # => {
|
130
|
+
# person.previous_changes # => {"name" => ["bob", "robert"]}
|
125
131
|
def previous_changes
|
126
132
|
@previously_changed
|
127
133
|
end
|
128
134
|
|
129
|
-
#
|
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
|
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
|
-
|
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
|