activemodel 3.0.pre → 3.0.0.rc
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.
- data/CHANGELOG +44 -1
- data/MIT-LICENSE +1 -1
- data/README.rdoc +184 -0
- data/lib/active_model.rb +29 -19
- data/lib/active_model/attribute_methods.rb +167 -46
- data/lib/active_model/callbacks.rb +134 -0
- data/lib/active_model/conversion.rb +41 -1
- data/lib/active_model/deprecated_error_methods.rb +1 -1
- data/lib/active_model/dirty.rb +56 -12
- data/lib/active_model/errors.rb +205 -46
- data/lib/active_model/lint.rb +53 -17
- data/lib/active_model/locale/en.yml +26 -23
- data/lib/active_model/mass_assignment_security.rb +160 -0
- data/lib/active_model/mass_assignment_security/permission_set.rb +40 -0
- data/lib/active_model/mass_assignment_security/sanitizer.rb +23 -0
- data/lib/active_model/naming.rb +70 -5
- data/lib/active_model/observing.rb +40 -16
- data/lib/active_model/railtie.rb +2 -0
- data/lib/active_model/serialization.rb +59 -0
- data/lib/active_model/serializers/json.rb +17 -11
- data/lib/active_model/serializers/xml.rb +66 -123
- data/lib/active_model/test_case.rb +0 -2
- data/lib/active_model/translation.rb +64 -0
- data/lib/active_model/validations.rb +150 -68
- data/lib/active_model/validations/acceptance.rb +53 -33
- data/lib/active_model/validations/callbacks.rb +57 -0
- data/lib/active_model/validations/confirmation.rb +41 -23
- data/lib/active_model/validations/exclusion.rb +18 -13
- data/lib/active_model/validations/format.rb +28 -24
- data/lib/active_model/validations/inclusion.rb +18 -13
- data/lib/active_model/validations/length.rb +67 -65
- data/lib/active_model/validations/numericality.rb +83 -58
- data/lib/active_model/validations/presence.rb +10 -8
- data/lib/active_model/validations/validates.rb +110 -0
- data/lib/active_model/validations/with.rb +90 -23
- data/lib/active_model/validator.rb +186 -0
- data/lib/active_model/version.rb +3 -2
- metadata +79 -20
- data/README +0 -21
- data/lib/active_model/state_machine.rb +0 -70
- data/lib/active_model/state_machine/event.rb +0 -62
- data/lib/active_model/state_machine/machine.rb +0 -75
- data/lib/active_model/state_machine/state.rb +0 -47
- data/lib/active_model/state_machine/state_transition.rb +0 -40
- data/lib/active_model/validations_repair_helper.rb +0 -35
data/CHANGELOG
CHANGED
@@ -1,4 +1,47 @@
|
|
1
|
-
*
|
1
|
+
*Rails 3.0.0 [release candidate] (July 26th, 2010)*
|
2
|
+
|
3
|
+
* Added ActiveModel::MassAssignmentSecurity [Eric Chapweske, Josh Kalderimis]
|
4
|
+
|
5
|
+
|
6
|
+
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
|
7
|
+
|
8
|
+
* JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh]
|
9
|
+
|
10
|
+
|
11
|
+
*Rails 3.0.0 [beta 3] (April 13th, 2010)*
|
12
|
+
|
13
|
+
* No changes
|
14
|
+
|
15
|
+
|
16
|
+
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
|
17
|
+
|
18
|
+
* #new_record? and #destroyed? were removed from ActiveModel::Lint. Use
|
19
|
+
persisted? instead. A model is persisted if it's not a new_record? and it was
|
20
|
+
not destroyed? [MG]
|
21
|
+
|
22
|
+
* Added validations reflection in ActiveModel::Validations [JV]
|
23
|
+
|
24
|
+
Model.validators
|
25
|
+
Model.validators_on(:field)
|
26
|
+
|
27
|
+
* #to_key was added to ActiveModel::Lint so we can generate DOM IDs for
|
28
|
+
AMo objects with composite keys [MG]
|
29
|
+
|
30
|
+
|
31
|
+
*Rails 3.0.0 [beta 1] (February 4, 2010)*
|
32
|
+
|
33
|
+
* ActiveModel::Observer#add_observer!
|
34
|
+
|
35
|
+
It has a custom hook to define after_find that should really be in a
|
36
|
+
ActiveRecord::Observer subclass:
|
37
|
+
|
38
|
+
def add_observer!(klass)
|
39
|
+
klass.add_observer(self)
|
40
|
+
klass.class_eval 'def after_find() end' unless
|
41
|
+
klass.respond_to?(:after_find)
|
42
|
+
end
|
43
|
+
|
44
|
+
* Change the ActiveModel::Base.include_root_in_json default to true for Rails 3 [DHH]
|
2
45
|
|
3
46
|
* Add validates_format_of :without => /regexp/ option. #430 [Elliot Winkler, Peer Allan]
|
4
47
|
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
= Active Model -- model interfaces for Rails
|
2
|
+
|
3
|
+
Active Model provides a known set of interfaces for usage in model classes.
|
4
|
+
They allow for Action Pack helpers to interact with non-ActiveRecord models,
|
5
|
+
for example. Active Model also helps building custom ORMs for use outside of
|
6
|
+
the Rails framework.
|
7
|
+
|
8
|
+
Prior to Rails 3.0, if a plugin or gem developer wanted to have an object
|
9
|
+
interact with Action Pack helpers, it was required to either copy chunks of
|
10
|
+
code from Rails, or monkey patch entire helpers to make them handle objects
|
11
|
+
that did not exacly conform to the Active Record interface. This would result
|
12
|
+
in code duplication and fragile applications that broke on upgrades.
|
13
|
+
|
14
|
+
Active Model solves this. You can include functionality from the following
|
15
|
+
modules:
|
16
|
+
|
17
|
+
* Add attribute magic to objects
|
18
|
+
|
19
|
+
class Person
|
20
|
+
include ActiveModel::AttributeMethods
|
21
|
+
|
22
|
+
attribute_method_prefix 'clear_'
|
23
|
+
define_attribute_methods [:name, :age]
|
24
|
+
|
25
|
+
attr_accessor :name, :age
|
26
|
+
|
27
|
+
def clear_attribute(attr)
|
28
|
+
send("#{attr}=", nil)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
person.clear_name
|
33
|
+
person.clear_age
|
34
|
+
|
35
|
+
{Learn more}[link:classes/ActiveModel/AttributeMethods.html]
|
36
|
+
|
37
|
+
* Callbacks for certain operations
|
38
|
+
|
39
|
+
class Person
|
40
|
+
extend ActiveModel::Callbacks
|
41
|
+
define_model_callbacks :create
|
42
|
+
|
43
|
+
def create
|
44
|
+
_run_create_callbacks do
|
45
|
+
# Your create action methods here
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
This generates +before_create+, +around_create+ and +after_create+
|
51
|
+
class methods that wrap your create method.
|
52
|
+
|
53
|
+
{Learn more}[link:classes/ActiveModel/CallBacks.html]
|
54
|
+
|
55
|
+
* Tracking value changes
|
56
|
+
|
57
|
+
The ActiveModel::Dirty module allows for tracking attribute changes:
|
58
|
+
|
59
|
+
person = Person.new
|
60
|
+
person.name # => nil
|
61
|
+
person.changed? # => false
|
62
|
+
person.name = 'bob'
|
63
|
+
person.changed? # => true
|
64
|
+
person.changed # => ['name']
|
65
|
+
person.changes # => { 'name' => [nil, 'bob'] }
|
66
|
+
person.name = 'robert'
|
67
|
+
person.save
|
68
|
+
person.previous_changes # => {'name' => ['bob, 'robert']}
|
69
|
+
|
70
|
+
{Learn more}[link:classes/ActiveModel/Dirty.html]
|
71
|
+
|
72
|
+
* Adding +errors+ interface to objects
|
73
|
+
|
74
|
+
Exposing error messages allows objects to interact with Action Pack
|
75
|
+
helpers seamlessly.
|
76
|
+
|
77
|
+
class Person
|
78
|
+
|
79
|
+
def initialize
|
80
|
+
@errors = ActiveModel::Errors.new(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
attr_accessor :name
|
84
|
+
attr_reader :errors
|
85
|
+
|
86
|
+
def validate!
|
87
|
+
errors.add(:name, "can not be nil") if name == nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def ErrorsPerson.human_attribute_name(attr, options = {})
|
91
|
+
"Name"
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
person.errors.full_messages
|
97
|
+
# => ["Name Can not be nil"]
|
98
|
+
|
99
|
+
person.errors.full_messages
|
100
|
+
# => ["Name Can not be nil"]
|
101
|
+
|
102
|
+
{Learn more}[link:classes/ActiveModel/Errors.html]
|
103
|
+
|
104
|
+
* Model name introspection
|
105
|
+
|
106
|
+
class NamedPerson
|
107
|
+
extend ActiveModel::Naming
|
108
|
+
end
|
109
|
+
|
110
|
+
NamedPerson.model_name #=> "NamedPerson"
|
111
|
+
NamedPerson.model_name.human #=> "Named person"
|
112
|
+
|
113
|
+
{Learn more}[link:classes/ActiveModel/Naming.html]
|
114
|
+
|
115
|
+
* Observer support
|
116
|
+
|
117
|
+
ActiveModel::Observers allows your object to implement the Observer
|
118
|
+
pattern in a Rails App and take advantage of all the standard observer
|
119
|
+
functions.
|
120
|
+
|
121
|
+
{Learn more}[link:classes/ActiveModel/Observer.html]
|
122
|
+
|
123
|
+
* Making objects serializable
|
124
|
+
|
125
|
+
ActiveModel::Serialization provides a standard interface for your object
|
126
|
+
to provide +to_json+ or +to_xml+ serialization.
|
127
|
+
|
128
|
+
s = SerialPerson.new
|
129
|
+
s.serializable_hash # => {"name"=>nil}
|
130
|
+
s.to_json # => "{\"name\":null}"
|
131
|
+
s.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
132
|
+
|
133
|
+
{Learn more}[link:classes/ActiveModel/Serialization.html]
|
134
|
+
|
135
|
+
* Internationalization (i18n) support
|
136
|
+
|
137
|
+
class Person
|
138
|
+
extend ActiveModel::Translation
|
139
|
+
end
|
140
|
+
|
141
|
+
Person.human_attribute_name('my_attribute')
|
142
|
+
#=> "My attribute"
|
143
|
+
|
144
|
+
{Learn more}[link:classes/ActiveModel/Translation.html]
|
145
|
+
|
146
|
+
* Validation support
|
147
|
+
|
148
|
+
class Person
|
149
|
+
include ActiveModel::Validations
|
150
|
+
|
151
|
+
attr_accessor :first_name, :last_name
|
152
|
+
|
153
|
+
validates_each :first_name, :last_name do |record, attr, value|
|
154
|
+
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
person = Person.new
|
159
|
+
person.first_name = 'zoolander'
|
160
|
+
person.valid? #=> false
|
161
|
+
|
162
|
+
{Learn more}[link:classes/ActiveModel/Validations.html]
|
163
|
+
|
164
|
+
* Custom validators
|
165
|
+
|
166
|
+
class Person
|
167
|
+
include ActiveModel::Validations
|
168
|
+
validates_with HasNameValidator
|
169
|
+
attr_accessor :name
|
170
|
+
end
|
171
|
+
|
172
|
+
class HasNameValidator < ActiveModel::Validator
|
173
|
+
def validate(record)
|
174
|
+
record.errors[:name] = "must exist" if record.name.blank?
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
p = ValidatorPerson.new
|
179
|
+
p.valid? #=> false
|
180
|
+
p.errors.full_messages #=> ["Name must exist"]
|
181
|
+
p.name = "Bob"
|
182
|
+
p.valid? #=> true
|
183
|
+
|
184
|
+
{Learn more}[link:classes/ActiveModel/Validator.html]
|
data/lib/active_model.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2004-
|
2
|
+
# Copyright (c) 2004-2010 David Heinemeier Hansson
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -21,32 +21,42 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
|
24
|
-
activesupport_path =
|
25
|
-
$:.unshift(activesupport_path) if File.directory?(activesupport_path)
|
24
|
+
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
|
25
|
+
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
|
26
26
|
require 'active_support'
|
27
27
|
|
28
|
+
|
28
29
|
module ActiveModel
|
29
|
-
|
30
|
-
|
31
|
-
autoload :
|
32
|
-
autoload :
|
33
|
-
autoload :
|
34
|
-
autoload :
|
30
|
+
extend ActiveSupport::Autoload
|
31
|
+
|
32
|
+
autoload :AttributeMethods
|
33
|
+
autoload :BlockValidator, 'active_model/validator'
|
34
|
+
autoload :Callbacks
|
35
|
+
autoload :Conversion
|
36
|
+
autoload :DeprecatedErrorMethods
|
37
|
+
autoload :Dirty
|
38
|
+
autoload :EachValidator, 'active_model/validator'
|
39
|
+
autoload :Errors
|
40
|
+
autoload :Lint
|
41
|
+
autoload :MassAssignmentSecurity
|
35
42
|
autoload :Name, 'active_model/naming'
|
36
|
-
autoload :Naming
|
43
|
+
autoload :Naming
|
37
44
|
autoload :Observer, 'active_model/observing'
|
38
|
-
autoload :Observing
|
39
|
-
autoload :Serialization
|
40
|
-
autoload :
|
41
|
-
autoload :
|
42
|
-
autoload :
|
43
|
-
autoload :
|
44
|
-
autoload :
|
45
|
+
autoload :Observing
|
46
|
+
autoload :Serialization
|
47
|
+
autoload :TestCase
|
48
|
+
autoload :Translation
|
49
|
+
autoload :VERSION
|
50
|
+
autoload :Validations
|
51
|
+
autoload :Validator
|
45
52
|
|
46
53
|
module Serializers
|
47
|
-
|
48
|
-
|
54
|
+
extend ActiveSupport::Autoload
|
55
|
+
|
56
|
+
autoload :JSON
|
57
|
+
autoload :Xml
|
49
58
|
end
|
50
59
|
end
|
51
60
|
|
61
|
+
require 'active_support/i18n'
|
52
62
|
I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
|
@@ -4,39 +4,108 @@ require 'active_support/core_ext/class/inheritable_attributes'
|
|
4
4
|
module ActiveModel
|
5
5
|
class MissingAttributeError < NoMethodError
|
6
6
|
end
|
7
|
-
|
7
|
+
# == Active Model Attribute Methods
|
8
|
+
#
|
9
|
+
# <tt>ActiveModel::AttributeMethods</tt> provides a way to add prefixes and suffixes
|
10
|
+
# to your methods as well as handling the creation of Active Record like class methods
|
11
|
+
# such as +table_name+.
|
12
|
+
#
|
13
|
+
# The requirements to implement ActiveModel::AttributeMethods are to:
|
14
|
+
#
|
15
|
+
# * <tt>include ActiveModel::AttributeMethods</tt> in your object
|
16
|
+
# * Call each Attribute Method module method you want to add, such as
|
17
|
+
# attribute_method_suffix or attribute_method_prefix
|
18
|
+
# * Call <tt>define_attribute_methods</tt> after the other methods are
|
19
|
+
# called.
|
20
|
+
# * Define the various generic +_attribute+ methods that you have declared
|
21
|
+
#
|
22
|
+
# A minimal implementation could be:
|
23
|
+
#
|
24
|
+
# class Person
|
25
|
+
# include ActiveModel::AttributeMethods
|
26
|
+
#
|
27
|
+
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
|
28
|
+
# attribute_method_suffix '_contrived?'
|
29
|
+
# attribute_method_prefix 'clear_'
|
30
|
+
# define_attribute_methods ['name']
|
31
|
+
#
|
32
|
+
# attr_accessor :name
|
33
|
+
#
|
34
|
+
# private
|
35
|
+
#
|
36
|
+
# def attribute_contrived?(attr)
|
37
|
+
# true
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# def clear_attribute(attr)
|
41
|
+
# send("#{attr}=", nil)
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# def reset_attribute_to_default!(attr)
|
45
|
+
# send("#{attr}=", "Default Name")
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# Notice that whenever you include ActiveModel::AttributeMethods in your class,
|
50
|
+
# it requires you to implement a <tt>attributes</tt> methods which returns a hash
|
51
|
+
# with each attribute name in your model as hash key and the attribute value as
|
52
|
+
# hash value.
|
53
|
+
#
|
54
|
+
# Hash keys must be strings.
|
55
|
+
#
|
8
56
|
module AttributeMethods
|
9
57
|
extend ActiveSupport::Concern
|
10
58
|
|
11
|
-
# Declare and check for suffixed attribute methods.
|
12
59
|
module ClassMethods
|
13
|
-
# Defines an "attribute" method (like +inheritance_column+ or
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
60
|
+
# Defines an "attribute" method (like +inheritance_column+ or +table_name+).
|
61
|
+
# A new (class) method will be created with the given name. If a value is
|
62
|
+
# specified, the new method will return that value (as a string).
|
63
|
+
# Otherwise, the given block will be used to compute the value of the
|
64
|
+
# method.
|
18
65
|
#
|
19
|
-
# The original method will be aliased, with the new name being
|
20
|
-
#
|
21
|
-
#
|
66
|
+
# The original method will be aliased, with the new name being prefixed
|
67
|
+
# with "original_". This allows the new method to access the original
|
68
|
+
# value.
|
22
69
|
#
|
23
70
|
# Example:
|
24
71
|
#
|
25
|
-
# class
|
72
|
+
# class Person
|
73
|
+
#
|
74
|
+
# include ActiveModel::AttributeMethods
|
75
|
+
#
|
76
|
+
# cattr_accessor :primary_key
|
77
|
+
# cattr_accessor :inheritance_column
|
78
|
+
#
|
26
79
|
# define_attr_method :primary_key, "sysid"
|
27
80
|
# define_attr_method( :inheritance_column ) do
|
28
81
|
# original_inheritance_column + "_id"
|
29
82
|
# end
|
83
|
+
#
|
30
84
|
# end
|
85
|
+
#
|
86
|
+
# Provides you with:
|
87
|
+
#
|
88
|
+
# AttributePerson.primary_key
|
89
|
+
# # => "sysid"
|
90
|
+
# AttributePerson.inheritance_column = 'address'
|
91
|
+
# AttributePerson.inheritance_column
|
92
|
+
# # => 'address_id'
|
31
93
|
def define_attr_method(name, value=nil, &block)
|
32
|
-
sing =
|
33
|
-
sing.
|
94
|
+
sing = singleton_class
|
95
|
+
sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
|
96
|
+
if method_defined?(:original_#{name})
|
97
|
+
undef :original_#{name}
|
98
|
+
end
|
99
|
+
alias_method :original_#{name}, :#{name}
|
100
|
+
eorb
|
34
101
|
if block_given?
|
35
102
|
sing.send :define_method, name, &block
|
36
103
|
else
|
37
104
|
# use eval instead of a block to work around a memory leak in dev
|
38
105
|
# mode in fcgi
|
39
|
-
sing.class_eval
|
106
|
+
sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
|
107
|
+
def #{name}; #{value.to_s.inspect}; end
|
108
|
+
eorb
|
40
109
|
end
|
41
110
|
end
|
42
111
|
|
@@ -49,24 +118,30 @@ module ActiveModel
|
|
49
118
|
#
|
50
119
|
# #{prefix}attribute(#{attr}, *args, &block)
|
51
120
|
#
|
52
|
-
# An <tt>#{prefix}attribute</tt>
|
53
|
-
# the +attr+ argument.
|
121
|
+
# An instance method <tt>#{prefix}attribute</tt> must exist and accept
|
122
|
+
# at least the +attr+ argument.
|
54
123
|
#
|
55
124
|
# For example:
|
56
125
|
#
|
57
|
-
# class Person
|
126
|
+
# class Person
|
127
|
+
#
|
128
|
+
# include ActiveModel::AttributeMethods
|
129
|
+
# attr_accessor :name
|
58
130
|
# attribute_method_prefix 'clear_'
|
131
|
+
# define_attribute_methods [:name]
|
59
132
|
#
|
60
133
|
# private
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
134
|
+
#
|
135
|
+
# def clear_attribute(attr)
|
136
|
+
# send("#{attr}=", nil)
|
137
|
+
# end
|
64
138
|
# end
|
65
139
|
#
|
66
|
-
# person = Person.
|
67
|
-
# person.name
|
140
|
+
# person = Person.new
|
141
|
+
# person.name = "Bob"
|
142
|
+
# person.name # => "Bob"
|
68
143
|
# person.clear_name
|
69
|
-
# person.name # =>
|
144
|
+
# person.name # => nil
|
70
145
|
def attribute_method_prefix(*prefixes)
|
71
146
|
attribute_method_matchers.concat(prefixes.map { |prefix| AttributeMethodMatcher.new :prefix => prefix })
|
72
147
|
undefine_attribute_methods
|
@@ -86,18 +161,24 @@ module ActiveModel
|
|
86
161
|
#
|
87
162
|
# For example:
|
88
163
|
#
|
89
|
-
# class Person
|
164
|
+
# class Person
|
165
|
+
#
|
166
|
+
# include ActiveModel::AttributeMethods
|
167
|
+
# attr_accessor :name
|
90
168
|
# attribute_method_suffix '_short?'
|
169
|
+
# define_attribute_methods [:name]
|
91
170
|
#
|
92
171
|
# private
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
172
|
+
#
|
173
|
+
# def attribute_short?(attr)
|
174
|
+
# send(attr).length < 5
|
175
|
+
# end
|
96
176
|
# end
|
97
177
|
#
|
98
|
-
# person = Person.
|
99
|
-
# person.name
|
100
|
-
# person.
|
178
|
+
# person = Person.new
|
179
|
+
# person.name = "Bob"
|
180
|
+
# person.name # => "Bob"
|
181
|
+
# person.name_short? # => true
|
101
182
|
def attribute_method_suffix(*suffixes)
|
102
183
|
attribute_method_matchers.concat(suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix })
|
103
184
|
undefine_attribute_methods
|
@@ -118,16 +199,21 @@ module ActiveModel
|
|
118
199
|
#
|
119
200
|
# For example:
|
120
201
|
#
|
121
|
-
# class Person
|
202
|
+
# class Person
|
203
|
+
#
|
204
|
+
# include ActiveModel::AttributeMethods
|
205
|
+
# attr_accessor :name
|
122
206
|
# attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
|
207
|
+
# define_attribute_methods [:name]
|
123
208
|
#
|
124
209
|
# private
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
210
|
+
#
|
211
|
+
# def reset_attribute_to_default!(attr)
|
212
|
+
# ...
|
213
|
+
# end
|
128
214
|
# end
|
129
215
|
#
|
130
|
-
# person = Person.
|
216
|
+
# person = Person.new
|
131
217
|
# person.name # => 'Gem'
|
132
218
|
# person.reset_name_to_default!
|
133
219
|
# person.name # => 'Gemma'
|
@@ -138,7 +224,7 @@ module ActiveModel
|
|
138
224
|
|
139
225
|
def alias_attribute(new_name, old_name)
|
140
226
|
attribute_method_matchers.each do |matcher|
|
141
|
-
module_eval <<-STR, __FILE__, __LINE__+1
|
227
|
+
module_eval <<-STR, __FILE__, __LINE__ + 1
|
142
228
|
def #{matcher.method_name(new_name)}(*args)
|
143
229
|
send(:#{matcher.method_name(old_name)}, *args)
|
144
230
|
end
|
@@ -146,6 +232,30 @@ module ActiveModel
|
|
146
232
|
end
|
147
233
|
end
|
148
234
|
|
235
|
+
# Declares a the attributes that should be prefixed and suffixed by
|
236
|
+
# ActiveModel::AttributeMethods.
|
237
|
+
#
|
238
|
+
# To use, pass in an array of attribute names (as strings or symbols),
|
239
|
+
# be sure to declare +define_attribute_methods+ after you define any
|
240
|
+
# prefix, suffix or affix methods, or they will not hook in.
|
241
|
+
#
|
242
|
+
# class Person
|
243
|
+
#
|
244
|
+
# include ActiveModel::AttributeMethods
|
245
|
+
# attr_accessor :name, :age, :address
|
246
|
+
# attribute_method_prefix 'clear_'
|
247
|
+
#
|
248
|
+
# # Call to define_attribute_methods must appear after the
|
249
|
+
# # attribute_method_prefix, attribute_method_suffix or
|
250
|
+
# # attribute_method_affix declares.
|
251
|
+
# define_attribute_methods [:name, :age, :address]
|
252
|
+
#
|
253
|
+
# private
|
254
|
+
#
|
255
|
+
# def clear_attribute(attr)
|
256
|
+
# ...
|
257
|
+
# end
|
258
|
+
# end
|
149
259
|
def define_attribute_methods(attr_names)
|
150
260
|
return if attribute_methods_generated?
|
151
261
|
attr_names.each do |attr_name|
|
@@ -156,8 +266,13 @@ module ActiveModel
|
|
156
266
|
if respond_to?(generate_method)
|
157
267
|
send(generate_method, attr_name)
|
158
268
|
else
|
159
|
-
|
160
|
-
|
269
|
+
method_name = matcher.method_name(attr_name)
|
270
|
+
|
271
|
+
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
272
|
+
if method_defined?(:#{method_name})
|
273
|
+
undef :#{method_name}
|
274
|
+
end
|
275
|
+
def #{method_name}(*args)
|
161
276
|
send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
|
162
277
|
end
|
163
278
|
STR
|
@@ -168,6 +283,7 @@ module ActiveModel
|
|
168
283
|
@attribute_methods_generated = true
|
169
284
|
end
|
170
285
|
|
286
|
+
# Removes all the preiously dynamically defined methods from the class
|
171
287
|
def undefine_attribute_methods
|
172
288
|
generated_attribute_methods.module_eval do
|
173
289
|
instance_methods.each { |m| undef_method(m) }
|
@@ -175,6 +291,7 @@ module ActiveModel
|
|
175
291
|
@attribute_methods_generated = nil
|
176
292
|
end
|
177
293
|
|
294
|
+
# Returns true if the attribute methods defined have been generated.
|
178
295
|
def generated_attribute_methods #:nodoc:
|
179
296
|
@generated_attribute_methods ||= begin
|
180
297
|
mod = Module.new
|
@@ -183,6 +300,7 @@ module ActiveModel
|
|
183
300
|
end
|
184
301
|
end
|
185
302
|
|
303
|
+
# Returns true if the attribute methods defined have been generated.
|
186
304
|
def attribute_methods_generated?
|
187
305
|
@attribute_methods_generated ||= nil
|
188
306
|
end
|
@@ -226,14 +344,17 @@ module ActiveModel
|
|
226
344
|
end
|
227
345
|
end
|
228
346
|
|
229
|
-
# Allows access to the object attributes, which are held in the
|
230
|
-
# were first-class methods. So a
|
231
|
-
# Person
|
232
|
-
#
|
233
|
-
#
|
347
|
+
# Allows access to the object attributes, which are held in the
|
348
|
+
# <tt>@attributes</tt> hash, as though they were first-class methods. So a
|
349
|
+
# Person class with a name attribute can use Person#name and Person#name=
|
350
|
+
# and never directly use the attributes hash -- except for multiple assigns
|
351
|
+
# with ActiveRecord#attributes=. A Milestone class can also ask
|
352
|
+
# Milestone#completed? to test that the completed attribute is not +nil+
|
353
|
+
# or 0.
|
234
354
|
#
|
235
|
-
# It's also possible to instantiate related objects, so a Client class
|
236
|
-
# table with a +master_id+ foreign key can
|
355
|
+
# It's also possible to instantiate related objects, so a Client class
|
356
|
+
# belonging to the clients table with a +master_id+ foreign key can
|
357
|
+
# instantiate master through Client#master.
|
237
358
|
def method_missing(method_id, *args, &block)
|
238
359
|
method_name = method_id.to_s
|
239
360
|
if match = match_attribute_method?(method_name)
|
@@ -252,7 +373,7 @@ module ActiveModel
|
|
252
373
|
return true
|
253
374
|
elsif !include_private_methods && super(method, true)
|
254
375
|
# If we're here then we haven't found among non-private methods
|
255
|
-
# but found among all methods. Which means that given method is private.
|
376
|
+
# but found among all methods. Which means that the given method is private.
|
256
377
|
return false
|
257
378
|
elsif match_attribute_method?(method.to_s)
|
258
379
|
return true
|