activerecord 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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,9 +1,8 @@
|
|
1
1
|
require 'active_support/core_ext/enumerable'
|
2
|
-
require 'active_support/deprecation'
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
4
|
# = Active Record Attribute Methods
|
6
|
-
module AttributeMethods
|
5
|
+
module AttributeMethods
|
7
6
|
extend ActiveSupport::Concern
|
8
7
|
include ActiveModel::AttributeMethods
|
9
8
|
|
@@ -16,76 +15,45 @@ module ActiveRecord
|
|
16
15
|
include TimeZoneConversion
|
17
16
|
include Dirty
|
18
17
|
include Serialization
|
19
|
-
include DeprecatedUnderscoreRead
|
20
|
-
|
21
|
-
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
22
|
-
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
|
23
|
-
# (Alias for the protected read_attribute method).
|
24
|
-
def [](attr_name)
|
25
|
-
read_attribute(attr_name)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
29
|
-
# (Alias for the protected write_attribute method).
|
30
|
-
def []=(attr_name, value)
|
31
|
-
write_attribute(attr_name, value)
|
32
|
-
end
|
33
18
|
end
|
34
19
|
|
35
20
|
module ClassMethods
|
36
21
|
# Generates all the attribute related methods for columns in the database
|
37
22
|
# accessors, mutators and query methods.
|
38
|
-
def define_attribute_methods
|
39
|
-
unless defined?(@attribute_methods_mutex)
|
40
|
-
msg = "It looks like something (probably a gem/plugin) is overriding the " \
|
41
|
-
"ActiveRecord::Base.inherited method. It is important that this hook executes so " \
|
42
|
-
"that your models are set up correctly. A workaround has been added to stop this " \
|
43
|
-
"causing an error in 3.2, but future versions will simply not work if the hook is " \
|
44
|
-
"overridden. If you are using Kaminari, please upgrade as it is known to have had " \
|
45
|
-
"this problem.\n\n"
|
46
|
-
msg << "The following may help track down the problem:"
|
47
|
-
|
48
|
-
meth = method(:inherited)
|
49
|
-
if meth.respond_to?(:source_location)
|
50
|
-
msg << " #{meth.source_location.inspect}"
|
51
|
-
else
|
52
|
-
msg << " #{meth.inspect}"
|
53
|
-
end
|
54
|
-
msg << "\n\n"
|
55
|
-
|
56
|
-
ActiveSupport::Deprecation.warn(msg)
|
57
|
-
|
58
|
-
@attribute_methods_mutex = Mutex.new
|
59
|
-
end
|
60
|
-
|
23
|
+
def define_attribute_methods # :nodoc:
|
61
24
|
# Use a mutex; we don't want two thread simaltaneously trying to define
|
62
25
|
# attribute methods.
|
63
26
|
@attribute_methods_mutex.synchronize do
|
64
27
|
return if attribute_methods_generated?
|
65
28
|
superclass.define_attribute_methods unless self == base_class
|
66
29
|
super(column_names)
|
67
|
-
column_names.each { |name| define_external_attribute_method(name) }
|
68
30
|
@attribute_methods_generated = true
|
69
31
|
end
|
70
32
|
end
|
71
33
|
|
72
|
-
def attribute_methods_generated?
|
34
|
+
def attribute_methods_generated? # :nodoc:
|
73
35
|
@attribute_methods_generated ||= false
|
74
36
|
end
|
75
37
|
|
76
|
-
|
77
|
-
|
78
|
-
# which is fast and won't give any false positives from the ancestors (because
|
79
|
-
# there are no ancestors).
|
80
|
-
def generated_external_attribute_methods
|
81
|
-
@generated_external_attribute_methods ||= Module.new { extend self }
|
82
|
-
end
|
83
|
-
|
84
|
-
def undefine_attribute_methods
|
85
|
-
super
|
38
|
+
def undefine_attribute_methods # :nodoc:
|
39
|
+
super if attribute_methods_generated?
|
86
40
|
@attribute_methods_generated = false
|
87
41
|
end
|
88
42
|
|
43
|
+
# Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
|
44
|
+
# \Active \Record method is defined in the model, otherwise +false+.
|
45
|
+
#
|
46
|
+
# class Person < ActiveRecord::Base
|
47
|
+
# def save
|
48
|
+
# 'already defined by Active Record'
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# Person.instance_method_already_implemented?(:save)
|
53
|
+
# # => ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord
|
54
|
+
#
|
55
|
+
# Person.instance_method_already_implemented?(:name)
|
56
|
+
# # => false
|
89
57
|
def instance_method_already_implemented?(method_name)
|
90
58
|
if dangerous_attribute_method?(method_name)
|
91
59
|
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord"
|
@@ -102,11 +70,11 @@ module ActiveRecord
|
|
102
70
|
|
103
71
|
# A method name is 'dangerous' if it is already defined by Active Record, but
|
104
72
|
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
|
105
|
-
def dangerous_attribute_method?(name)
|
73
|
+
def dangerous_attribute_method?(name) # :nodoc:
|
106
74
|
method_defined_within?(name, Base)
|
107
75
|
end
|
108
76
|
|
109
|
-
def method_defined_within?(name, klass, sup = klass.superclass)
|
77
|
+
def method_defined_within?(name, klass, sup = klass.superclass) # :nodoc:
|
110
78
|
if klass.method_defined?(name) || klass.private_method_defined?(name)
|
111
79
|
if sup.method_defined?(name) || sup.private_method_defined?(name)
|
112
80
|
klass.instance_method(name).owner != sup.instance_method(name).owner
|
@@ -118,13 +86,27 @@ module ActiveRecord
|
|
118
86
|
end
|
119
87
|
end
|
120
88
|
|
89
|
+
# Returns +true+ if +attribute+ is an attribute method and table exists,
|
90
|
+
# +false+ otherwise.
|
91
|
+
#
|
92
|
+
# class Person < ActiveRecord::Base
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# Person.attribute_method?('name') # => true
|
96
|
+
# Person.attribute_method?(:age=) # => true
|
97
|
+
# Person.attribute_method?(:nothing) # => false
|
121
98
|
def attribute_method?(attribute)
|
122
99
|
super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
|
123
100
|
end
|
124
101
|
|
125
|
-
# Returns an array of column names as strings if it's not
|
126
|
-
#
|
127
|
-
#
|
102
|
+
# Returns an array of column names as strings if it's not an abstract class and
|
103
|
+
# table exists. Otherwise it returns an empty array.
|
104
|
+
#
|
105
|
+
# class Person < ActiveRecord::Base
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# Person.attribute_names
|
109
|
+
# # => ["id", "created_at", "updated_at", "name", "age"]
|
128
110
|
def attribute_names
|
129
111
|
@attribute_names ||= if !abstract_class? && table_exists?
|
130
112
|
column_names
|
@@ -136,7 +118,7 @@ module ActiveRecord
|
|
136
118
|
|
137
119
|
# If we haven't generated any methods yet, generate them, then
|
138
120
|
# see if we've created the method we're looking for.
|
139
|
-
def method_missing(method, *args, &block)
|
121
|
+
def method_missing(method, *args, &block) # :nodoc:
|
140
122
|
unless self.class.attribute_methods_generated?
|
141
123
|
self.class.define_attribute_methods
|
142
124
|
|
@@ -150,7 +132,7 @@ module ActiveRecord
|
|
150
132
|
end
|
151
133
|
end
|
152
134
|
|
153
|
-
def attribute_missing(match, *args, &block)
|
135
|
+
def attribute_missing(match, *args, &block) # :nodoc:
|
154
136
|
if self.class.columns_hash[match.attr_name]
|
155
137
|
ActiveSupport::Deprecation.warn(
|
156
138
|
"The method `#{match.method_name}', matching the attribute `#{match.attr_name}' has " \
|
@@ -164,26 +146,64 @@ module ActiveRecord
|
|
164
146
|
super
|
165
147
|
end
|
166
148
|
|
149
|
+
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
|
150
|
+
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
|
151
|
+
# which will all return +true+. It also define the attribute methods if they have
|
152
|
+
# not been generated.
|
153
|
+
#
|
154
|
+
# class Person < ActiveRecord::Base
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# person = Person.new
|
158
|
+
# person.respond_to(:name) # => true
|
159
|
+
# person.respond_to(:name=) # => true
|
160
|
+
# person.respond_to(:name?) # => true
|
161
|
+
# person.respond_to('age') # => true
|
162
|
+
# person.respond_to('age=') # => true
|
163
|
+
# person.respond_to('age?') # => true
|
164
|
+
# person.respond_to(:nothing) # => false
|
167
165
|
def respond_to?(name, include_private = false)
|
168
166
|
self.class.define_attribute_methods unless self.class.attribute_methods_generated?
|
169
167
|
super
|
170
168
|
end
|
171
169
|
|
172
|
-
# Returns true if the given attribute is in the attributes hash
|
170
|
+
# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
|
171
|
+
#
|
172
|
+
# class Person < ActiveRecord::Base
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# person = Person.new
|
176
|
+
# person.has_attribute?(:name) # => true
|
177
|
+
# person.has_attribute?('age') # => true
|
178
|
+
# person.has_attribute?(:nothing) # => false
|
173
179
|
def has_attribute?(attr_name)
|
174
180
|
@attributes.has_key?(attr_name.to_s)
|
175
181
|
end
|
176
182
|
|
177
183
|
# Returns an array of names for the attributes available on this object.
|
184
|
+
#
|
185
|
+
# class Person < ActiveRecord::Base
|
186
|
+
# end
|
187
|
+
#
|
188
|
+
# person = Person.new
|
189
|
+
# person.attribute_names
|
190
|
+
# # => ["id", "created_at", "updated_at", "name", "age"]
|
178
191
|
def attribute_names
|
179
192
|
@attributes.keys
|
180
193
|
end
|
181
194
|
|
182
195
|
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
|
196
|
+
#
|
197
|
+
# class Person < ActiveRecord::Base
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# person = Person.create(name: 'Francesco', age: 22)
|
201
|
+
# person.attributes
|
202
|
+
# # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
|
183
203
|
def attributes
|
184
|
-
|
185
|
-
|
186
|
-
|
204
|
+
attribute_names.each_with_object({}) { |name, attrs|
|
205
|
+
attrs[name] = read_attribute(name)
|
206
|
+
}
|
187
207
|
end
|
188
208
|
|
189
209
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
@@ -192,13 +212,13 @@ module ActiveRecord
|
|
192
212
|
# <tt>:db</tt> format. Other attributes return the value of
|
193
213
|
# <tt>#inspect</tt> without modification.
|
194
214
|
#
|
195
|
-
# person = Person.create!(:
|
215
|
+
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
196
216
|
#
|
197
217
|
# person.attribute_for_inspect(:name)
|
198
|
-
# # =>
|
218
|
+
# # => "\"David Heinemeier Hansson David Heinemeier Hansson D...\""
|
199
219
|
#
|
200
220
|
# person.attribute_for_inspect(:created_at)
|
201
|
-
# # =>
|
221
|
+
# # => "\"2012-10-22 00:15:07\""
|
202
222
|
def attribute_for_inspect(attr_name)
|
203
223
|
value = read_attribute(attr_name)
|
204
224
|
|
@@ -211,65 +231,148 @@ module ActiveRecord
|
|
211
231
|
end
|
212
232
|
end
|
213
233
|
|
214
|
-
# Returns true if the specified +attribute+ has been set by the user or by a
|
215
|
-
# nil nor empty
|
234
|
+
# Returns +true+ if the specified +attribute+ has been set by the user or by a
|
235
|
+
# database load and is neither +nil+ nor <tt>empty?</tt> (the latter only applies
|
236
|
+
# to objects that respond to <tt>empty?</tt>, most notably Strings). Otherwise, +false+.
|
237
|
+
# Note that it always returns +true+ with boolean attributes.
|
238
|
+
#
|
239
|
+
# class Task < ActiveRecord::Base
|
240
|
+
# end
|
241
|
+
#
|
242
|
+
# person = Task.new(title: '', is_done: false)
|
243
|
+
# person.attribute_present?(:title) # => false
|
244
|
+
# person.attribute_present?(:is_done) # => true
|
245
|
+
# person.name = 'Francesco'
|
246
|
+
# person.is_done = true
|
247
|
+
# person.attribute_present?(:title) # => true
|
248
|
+
# person.attribute_present?(:is_done) # => true
|
216
249
|
def attribute_present?(attribute)
|
217
250
|
value = read_attribute(attribute)
|
218
251
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
219
252
|
end
|
220
253
|
|
221
|
-
# Returns the column object for the named attribute.
|
254
|
+
# Returns the column object for the named attribute. Returns +nil+ if the
|
255
|
+
# named attribute not exists.
|
256
|
+
#
|
257
|
+
# class Person < ActiveRecord::Base
|
258
|
+
# end
|
259
|
+
#
|
260
|
+
# person = Person.new
|
261
|
+
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
262
|
+
# # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
263
|
+
#
|
264
|
+
# person.column_for_attribute(:nothing)
|
265
|
+
# # => nil
|
222
266
|
def column_for_attribute(name)
|
267
|
+
# FIXME: should this return a null object for columns that don't exist?
|
223
268
|
self.class.columns_hash[name.to_s]
|
224
269
|
end
|
225
270
|
|
271
|
+
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
272
|
+
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). It raises
|
273
|
+
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
|
274
|
+
#
|
275
|
+
# Alias for the <tt>read_attribute</tt> method.
|
276
|
+
#
|
277
|
+
# class Person < ActiveRecord::Base
|
278
|
+
# belongs_to :organization
|
279
|
+
# end
|
280
|
+
#
|
281
|
+
# person = Person.new(name: 'Francesco', age: '22')
|
282
|
+
# person[:name] # => "Francesco"
|
283
|
+
# person[:age] # => 22
|
284
|
+
#
|
285
|
+
# person = Person.select('id').first
|
286
|
+
# person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
|
287
|
+
# person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
|
288
|
+
def [](attr_name)
|
289
|
+
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
|
290
|
+
end
|
291
|
+
|
292
|
+
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
293
|
+
# (Alias for the protected <tt>write_attribute</tt> method).
|
294
|
+
#
|
295
|
+
# class Person < ActiveRecord::Base
|
296
|
+
# end
|
297
|
+
#
|
298
|
+
# person = Person.new
|
299
|
+
# person[:age] = '22'
|
300
|
+
# person[:age] # => 22
|
301
|
+
# person[:age] # => Fixnum
|
302
|
+
def []=(attr_name, value)
|
303
|
+
write_attribute(attr_name, value)
|
304
|
+
end
|
305
|
+
|
226
306
|
protected
|
227
307
|
|
228
|
-
def clone_attributes(reader_method = :read_attribute, attributes = {})
|
308
|
+
def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
|
229
309
|
attribute_names.each do |name|
|
230
310
|
attributes[name] = clone_attribute_value(reader_method, name)
|
231
311
|
end
|
232
312
|
attributes
|
233
313
|
end
|
234
314
|
|
235
|
-
def clone_attribute_value(reader_method, attribute_name)
|
315
|
+
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
236
316
|
value = send(reader_method, attribute_name)
|
237
317
|
value.duplicable? ? value.clone : value
|
238
318
|
rescue TypeError, NoMethodError
|
239
319
|
value
|
240
320
|
end
|
241
321
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
attrs = {}
|
246
|
-
klass = self.class
|
247
|
-
arel_table = klass.arel_table
|
322
|
+
def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
|
323
|
+
arel_attributes_with_values(attributes_for_create(attribute_names))
|
324
|
+
end
|
248
325
|
|
249
|
-
|
250
|
-
|
326
|
+
def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
|
327
|
+
arel_attributes_with_values(attributes_for_update(attribute_names))
|
328
|
+
end
|
251
329
|
|
252
|
-
|
330
|
+
def attribute_method?(attr_name) # :nodoc:
|
331
|
+
defined?(@attributes) && @attributes.include?(attr_name)
|
332
|
+
end
|
253
333
|
|
254
|
-
|
255
|
-
@attributes[name].serialized_value
|
256
|
-
else
|
257
|
-
# FIXME: we need @attributes to be used consistently.
|
258
|
-
# If the values stored in @attributes were already type
|
259
|
-
# casted, this code could be simplified
|
260
|
-
read_attribute(name)
|
261
|
-
end
|
334
|
+
private
|
262
335
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
336
|
+
# Returns a Hash of the Arel::Attributes and attribute values that have been
|
337
|
+
# type casted for use in an Arel insert/update method.
|
338
|
+
def arel_attributes_with_values(attribute_names)
|
339
|
+
attrs = {}
|
340
|
+
arel_table = self.class.arel_table
|
267
341
|
|
342
|
+
attribute_names.each do |name|
|
343
|
+
attrs[arel_table[name]] = typecasted_attribute_value(name)
|
344
|
+
end
|
268
345
|
attrs
|
269
346
|
end
|
270
347
|
|
271
|
-
|
272
|
-
|
348
|
+
# Filters the primary keys and readonly attributes from the attribute names.
|
349
|
+
def attributes_for_update(attribute_names)
|
350
|
+
attribute_names.select do |name|
|
351
|
+
column_for_attribute(name) && !pk_attribute?(name) && !readonly_attribute?(name)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
# Filters out the primary keys, from the attribute names, when the primary
|
356
|
+
# key is to be generated (e.g. the id attribute has no value).
|
357
|
+
def attributes_for_create(attribute_names)
|
358
|
+
attribute_names.select do |name|
|
359
|
+
column_for_attribute(name) && !(pk_attribute?(name) && id.nil?)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def readonly_attribute?(name)
|
364
|
+
self.class.readonly_attributes.include?(name)
|
365
|
+
end
|
366
|
+
|
367
|
+
def pk_attribute?(name)
|
368
|
+
column_for_attribute(name).primary
|
369
|
+
end
|
370
|
+
|
371
|
+
def typecasted_attribute_value(name)
|
372
|
+
# FIXME: we need @attributes to be used consistently.
|
373
|
+
# If the values stored in @attributes were already typecasted, this code
|
374
|
+
# could be simplified
|
375
|
+
read_attribute(name)
|
273
376
|
end
|
274
377
|
end
|
275
378
|
end
|
@@ -1,5 +1,28 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module AttributeMethods
|
3
|
+
# = Active Record Attribute Methods Before Type Cast
|
4
|
+
#
|
5
|
+
# <tt>ActiveRecord::AttributeMethods::BeforeTypeCast</tt> provides a way to
|
6
|
+
# read the value of the attributes before typecasting and deserialization.
|
7
|
+
#
|
8
|
+
# class Task < ActiveRecord::Base
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# task = Task.new(id: '1', completed_on: '2012-10-21')
|
12
|
+
# task.id # => 1
|
13
|
+
# task.completed_on # => Sun, 21 Oct 2012
|
14
|
+
#
|
15
|
+
# task.attributes_before_type_cast
|
16
|
+
# # => {"id"=>"1", "completed_on"=>"2012-10-21", ... }
|
17
|
+
# task.read_attribute_before_type_cast('id') # => "1"
|
18
|
+
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
19
|
+
#
|
20
|
+
# In addition to #read_attribute_before_type_cast and #attributes_before_type_cast,
|
21
|
+
# it declares a method for all attributes with the <tt>*_before_type_cast</tt>
|
22
|
+
# suffix.
|
23
|
+
#
|
24
|
+
# task.id_before_type_cast # => "1"
|
25
|
+
# task.completed_on_before_type_cast # => "2012-10-21"
|
3
26
|
module BeforeTypeCast
|
4
27
|
extend ActiveSupport::Concern
|
5
28
|
|
@@ -7,11 +30,31 @@ module ActiveRecord
|
|
7
30
|
attribute_method_suffix "_before_type_cast"
|
8
31
|
end
|
9
32
|
|
33
|
+
# Returns the value of the attribute identified by +attr_name+ before
|
34
|
+
# typecasting and deserialization.
|
35
|
+
#
|
36
|
+
# class Task < ActiveRecord::Base
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# task = Task.new(id: '1', completed_on: '2012-10-21')
|
40
|
+
# task.read_attribute('id') # => 1
|
41
|
+
# task.read_attribute_before_type_cast('id') # => '1'
|
42
|
+
# task.read_attribute('completed_on') # => Sun, 21 Oct 2012
|
43
|
+
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
10
44
|
def read_attribute_before_type_cast(attr_name)
|
11
45
|
@attributes[attr_name]
|
12
46
|
end
|
13
47
|
|
14
48
|
# Returns a hash of attributes before typecasting and deserialization.
|
49
|
+
#
|
50
|
+
# class Task < ActiveRecord::Base
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
|
54
|
+
# task.attributes
|
55
|
+
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
|
56
|
+
# task.attributes_before_type_cast
|
57
|
+
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
15
58
|
def attributes_before_type_cast
|
16
59
|
@attributes
|
17
60
|
end
|
@@ -20,11 +63,7 @@ module ActiveRecord
|
|
20
63
|
|
21
64
|
# Handle *_before_type_cast for method_missing.
|
22
65
|
def attribute_before_type_cast(attribute_name)
|
23
|
-
|
24
|
-
read_attribute_before_type_cast(self.class.primary_key)
|
25
|
-
else
|
26
|
-
read_attribute_before_type_cast(attribute_name)
|
27
|
-
end
|
66
|
+
read_attribute_before_type_cast(attribute_name)
|
28
67
|
end
|
29
68
|
end
|
30
69
|
end
|