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.
- 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
|