activemodel 3.0.pre → 3.0.0.rc
Sign up to get free protection for your applications and to get access to all the features.
- 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
|