activerecord 4.1.16 → 4.2.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.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2185
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +2 -1
  5. data/lib/active_record/aggregations.rb +12 -8
  6. data/lib/active_record/associations.rb +58 -33
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/association_scope.rb +53 -21
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +16 -5
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  13. data/lib/active_record/associations/builder/has_one.rb +2 -2
  14. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  15. data/lib/active_record/associations/collection_association.rb +32 -44
  16. data/lib/active_record/associations/collection_proxy.rb +1 -10
  17. data/lib/active_record/associations/has_many_association.rb +60 -14
  18. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  19. data/lib/active_record/associations/has_one_association.rb +0 -1
  20. data/lib/active_record/associations/join_dependency.rb +7 -9
  21. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/preloader/association.rb +9 -5
  24. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  25. data/lib/active_record/associations/singular_association.rb +16 -1
  26. data/lib/active_record/associations/through_association.rb +6 -22
  27. data/lib/active_record/attribute.rb +131 -0
  28. data/lib/active_record/attribute_assignment.rb +19 -11
  29. data/lib/active_record/attribute_decorators.rb +66 -0
  30. data/lib/active_record/attribute_methods.rb +53 -90
  31. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  32. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  33. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  34. data/lib/active_record/attribute_methods/read.rb +14 -57
  35. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  36. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  37. data/lib/active_record/attribute_methods/write.rb +8 -23
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attribute_set/builder.rb +32 -0
  40. data/lib/active_record/attributes.rb +122 -0
  41. data/lib/active_record/autosave_association.rb +11 -21
  42. data/lib/active_record/base.rb +9 -19
  43. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  44. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  53. data/lib/active_record/connection_adapters/column.rb +13 -244
  54. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  55. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  56. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  57. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  58. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  60. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  90. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  92. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  94. data/lib/active_record/connection_handling.rb +1 -1
  95. data/lib/active_record/core.rb +119 -22
  96. data/lib/active_record/counter_cache.rb +60 -6
  97. data/lib/active_record/enum.rb +9 -10
  98. data/lib/active_record/errors.rb +27 -26
  99. data/lib/active_record/explain.rb +1 -1
  100. data/lib/active_record/fixtures.rb +52 -45
  101. data/lib/active_record/gem_version.rb +3 -3
  102. data/lib/active_record/inheritance.rb +33 -8
  103. data/lib/active_record/integration.rb +4 -4
  104. data/lib/active_record/locking/optimistic.rb +34 -16
  105. data/lib/active_record/migration.rb +22 -32
  106. data/lib/active_record/migration/command_recorder.rb +19 -2
  107. data/lib/active_record/migration/join_table.rb +1 -1
  108. data/lib/active_record/model_schema.rb +39 -48
  109. data/lib/active_record/nested_attributes.rb +8 -18
  110. data/lib/active_record/persistence.rb +39 -22
  111. data/lib/active_record/query_cache.rb +3 -3
  112. data/lib/active_record/querying.rb +1 -8
  113. data/lib/active_record/railtie.rb +17 -10
  114. data/lib/active_record/railties/databases.rake +47 -42
  115. data/lib/active_record/readonly_attributes.rb +0 -1
  116. data/lib/active_record/reflection.rb +225 -92
  117. data/lib/active_record/relation.rb +35 -11
  118. data/lib/active_record/relation/batches.rb +0 -2
  119. data/lib/active_record/relation/calculations.rb +28 -32
  120. data/lib/active_record/relation/delegation.rb +1 -1
  121. data/lib/active_record/relation/finder_methods.rb +42 -20
  122. data/lib/active_record/relation/merger.rb +0 -1
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  125. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  126. data/lib/active_record/relation/query_methods.rb +98 -62
  127. data/lib/active_record/relation/spawn_methods.rb +6 -7
  128. data/lib/active_record/result.rb +16 -9
  129. data/lib/active_record/sanitization.rb +8 -1
  130. data/lib/active_record/schema.rb +0 -1
  131. data/lib/active_record/schema_dumper.rb +51 -9
  132. data/lib/active_record/schema_migration.rb +4 -0
  133. data/lib/active_record/scoping/default.rb +5 -4
  134. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  135. data/lib/active_record/statement_cache.rb +79 -5
  136. data/lib/active_record/store.rb +5 -5
  137. data/lib/active_record/tasks/database_tasks.rb +37 -5
  138. data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  140. data/lib/active_record/timestamp.rb +9 -7
  141. data/lib/active_record/transactions.rb +35 -21
  142. data/lib/active_record/type.rb +20 -0
  143. data/lib/active_record/type/binary.rb +40 -0
  144. data/lib/active_record/type/boolean.rb +19 -0
  145. data/lib/active_record/type/date.rb +46 -0
  146. data/lib/active_record/type/date_time.rb +43 -0
  147. data/lib/active_record/type/decimal.rb +40 -0
  148. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  149. data/lib/active_record/type/float.rb +19 -0
  150. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  151. data/lib/active_record/type/integer.rb +23 -0
  152. data/lib/active_record/type/mutable.rb +16 -0
  153. data/lib/active_record/type/numeric.rb +36 -0
  154. data/lib/active_record/type/serialized.rb +51 -0
  155. data/lib/active_record/type/string.rb +36 -0
  156. data/lib/active_record/type/text.rb +11 -0
  157. data/lib/active_record/type/time.rb +26 -0
  158. data/lib/active_record/type/time_value.rb +38 -0
  159. data/lib/active_record/type/type_map.rb +48 -0
  160. data/lib/active_record/type/value.rb +101 -0
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record/validations/uniqueness.rb +9 -23
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -11,6 +11,15 @@ module ActiveRecord
11
11
  # If the passed hash responds to <tt>permitted?</tt> method and the return value
12
12
  # of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
13
13
  # exception is raised.
14
+ #
15
+ # cat = Cat.new(name: "Gorby", status: "yawning")
16
+ # cat.attributes # => { "name" => "Gorby", "status" => "yawning", "created_at" => nil, "updated_at" => nil}
17
+ # cat.assign_attributes(status: "sleeping")
18
+ # cat.attributes # => { "name" => "Gorby", "status" => "sleeping", "created_at" => nil, "updated_at" => nil }
19
+ #
20
+ # New attributes will be persisted in the database when the object is saved.
21
+ #
22
+ # Aliased to <tt>attributes=</tt>.
14
23
  def assign_attributes(new_attributes)
15
24
  if !new_attributes.respond_to?(:stringify_keys)
16
25
  raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
@@ -43,7 +52,7 @@ module ActiveRecord
43
52
 
44
53
  def _assign_attribute(k, v)
45
54
  public_send("#{k}=", v)
46
- rescue NoMethodError, NameError
55
+ rescue NoMethodError
47
56
  if respond_to?("#{k}=")
48
57
  raise
49
58
  else
@@ -106,7 +115,7 @@ module ActiveRecord
106
115
  end
107
116
 
108
117
  class MultiparameterAttribute #:nodoc:
109
- attr_reader :object, :name, :values, :column
118
+ attr_reader :object, :name, :values, :cast_type
110
119
 
111
120
  def initialize(object, name, values)
112
121
  @object = object
@@ -117,22 +126,22 @@ module ActiveRecord
117
126
  def read_value
118
127
  return if values.values.compact.empty?
119
128
 
120
- @column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name)
121
- klass = column.klass
129
+ @cast_type = object.type_for_attribute(name)
130
+ klass = cast_type.klass
122
131
 
123
132
  if klass == Time
124
133
  read_time
125
134
  elsif klass == Date
126
135
  read_date
127
136
  else
128
- read_other(klass)
137
+ read_other
129
138
  end
130
139
  end
131
140
 
132
141
  private
133
142
 
134
143
  def instantiate_time_object(set_values)
135
- if object.class.send(:create_time_zone_conversion_attribute?, name, column)
144
+ if object.class.send(:create_time_zone_conversion_attribute?, name, cast_type)
136
145
  Time.zone.local(*set_values)
137
146
  else
138
147
  Time.send(object.class.default_timezone, *set_values)
@@ -140,9 +149,9 @@ module ActiveRecord
140
149
  end
141
150
 
142
151
  def read_time
143
- # If column is a :time (and not :date or :timestamp) there is no need to validate if
152
+ # If column is a :time (and not :date or :datetime) there is no need to validate if
144
153
  # there are year/month/day fields
145
- if column.type == :time
154
+ if cast_type.type == :time
146
155
  # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
147
156
  { 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
148
157
  values[key] ||= value
@@ -172,13 +181,12 @@ module ActiveRecord
172
181
  end
173
182
  end
174
183
 
175
- def read_other(klass)
184
+ def read_other
176
185
  max_position = extract_max_param
177
186
  positions = (1..max_position)
178
187
  validate_required_parameters!(positions)
179
188
 
180
- set_values = values.values_at(*positions)
181
- klass.new(*set_values)
189
+ values.slice(*positions)
182
190
  end
183
191
 
184
192
  # Checks whether some blank date parameter exists. Note that this is different
@@ -0,0 +1,66 @@
1
+ module ActiveRecord
2
+ module AttributeDecorators # :nodoc:
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :attribute_type_decorations, instance_accessor: false # :internal:
7
+ self.attribute_type_decorations = TypeDecorator.new
8
+ end
9
+
10
+ module ClassMethods # :nodoc:
11
+ def decorate_attribute_type(column_name, decorator_name, &block)
12
+ matcher = ->(name, _) { name == column_name.to_s }
13
+ key = "_#{column_name}_#{decorator_name}"
14
+ decorate_matching_attribute_types(matcher, key, &block)
15
+ end
16
+
17
+ def decorate_matching_attribute_types(matcher, decorator_name, &block)
18
+ clear_caches_calculated_from_columns
19
+ decorator_name = decorator_name.to_s
20
+
21
+ # Create new hashes so we don't modify parent classes
22
+ self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
23
+ end
24
+
25
+ private
26
+
27
+ def add_user_provided_columns(*)
28
+ super.map do |column|
29
+ decorated_type = attribute_type_decorations.apply(column.name, column.cast_type)
30
+ column.with_type(decorated_type)
31
+ end
32
+ end
33
+ end
34
+
35
+ class TypeDecorator # :nodoc:
36
+ delegate :clear, to: :@decorations
37
+
38
+ def initialize(decorations = {})
39
+ @decorations = decorations
40
+ end
41
+
42
+ def merge(*args)
43
+ TypeDecorator.new(@decorations.merge(*args))
44
+ end
45
+
46
+ def apply(name, type)
47
+ decorations = decorators_for(name, type)
48
+ decorations.inject(type) do |new_type, block|
49
+ block.call(new_type)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def decorators_for(name, type)
56
+ matching(name, type).map(&:last)
57
+ end
58
+
59
+ def matching(name, type)
60
+ @decorations.values.select do |(matcher, _)|
61
+ matcher.call(name, type)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -18,6 +18,8 @@ module ActiveRecord
18
18
  include TimeZoneConversion
19
19
  include Dirty
20
20
  include Serialization
21
+
22
+ delegate :column_for_attribute, to: :class
21
23
  end
22
24
 
23
25
  AttrNames = Module.new {
@@ -29,7 +31,7 @@ module ActiveRecord
29
31
  end
30
32
  }
31
33
 
32
- BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
34
+ BLACKLISTED_CLASS_METHODS = %w(private public protected)
33
35
 
34
36
  class AttributeMethodCache
35
37
  def initialize
@@ -48,7 +50,11 @@ module ActiveRecord
48
50
  end
49
51
 
50
52
  private
51
- def method_body; raise NotImplementedError; end
53
+
54
+ # Override this method in the subclasses for method body.
55
+ def method_body(method_name, const_name)
56
+ raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
57
+ end
52
58
  end
53
59
 
54
60
  class GeneratedAttributeMethods < Module; end # :nodoc:
@@ -63,15 +69,13 @@ module ActiveRecord
63
69
  @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
64
70
  @attribute_methods_generated = false
65
71
  include @generated_attribute_methods
66
-
67
- super
68
72
  end
69
73
 
70
74
  # Generates all the attribute related methods for columns in the database
71
75
  # accessors, mutators and query methods.
72
76
  def define_attribute_methods # :nodoc:
73
77
  return false if @attribute_methods_generated
74
- # Use a mutex; we don't want two thread simultaneously trying to define
78
+ # Use a mutex; we don't want two threads simultaneously trying to define
75
79
  # attribute methods.
76
80
  generated_attribute_methods.synchronize do
77
81
  return false if @attribute_methods_generated
@@ -84,7 +88,7 @@ module ActiveRecord
84
88
 
85
89
  def undefine_attribute_methods # :nodoc:
86
90
  generated_attribute_methods.synchronize do
87
- super if defined?(@attribute_methods_generated) && @attribute_methods_generated
91
+ super if @attribute_methods_generated
88
92
  @attribute_methods_generated = false
89
93
  end
90
94
  end
@@ -105,7 +109,7 @@ module ActiveRecord
105
109
  # # => false
106
110
  def instance_method_already_implemented?(method_name)
107
111
  if dangerous_attribute_method?(method_name)
108
- raise DangerousAttributeError, "#{method_name} is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name."
112
+ raise DangerousAttributeError, "#{method_name} is defined by Active Record"
109
113
  end
110
114
 
111
115
  if superclass == Base
@@ -155,16 +159,6 @@ module ActiveRecord
155
159
  end
156
160
  end
157
161
 
158
- def find_generated_attribute_method(method_name) # :nodoc:
159
- klass = self
160
- until klass == Base
161
- gen_methods = klass.generated_attribute_methods
162
- return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
163
- klass = klass.superclass
164
- end
165
- nil
166
- end
167
-
168
162
  # Returns +true+ if +attribute+ is an attribute method and table exists,
169
163
  # +false+ otherwise.
170
164
  #
@@ -193,24 +187,29 @@ module ActiveRecord
193
187
  []
194
188
  end
195
189
  end
196
- end
197
190
 
198
- # If we haven't generated any methods yet, generate them, then
199
- # see if we've created the method we're looking for.
200
- def method_missing(method, *args, &block) # :nodoc:
201
- self.class.define_attribute_methods
202
- if respond_to_without_attributes?(method)
203
- # make sure to invoke the correct attribute method, as we might have gotten here via a `super`
204
- # call in a overwritten attribute method
205
- if attribute_method = self.class.find_generated_attribute_method(method)
206
- # this is probably horribly slow, but should only happen at most once for a given AR class
207
- attribute_method.bind(self).call(*args, &block)
208
- else
209
- return super unless respond_to_missing?(method, true)
210
- send(method, *args, &block)
191
+ # Returns the column object for the named attribute.
192
+ # Returns nil if the named attribute does not exist.
193
+ #
194
+ # class Person < ActiveRecord::Base
195
+ # end
196
+ #
197
+ # person = Person.new
198
+ # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
199
+ # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
200
+ #
201
+ # person.column_for_attribute(:nothing)
202
+ # # => nil
203
+ def column_for_attribute(name)
204
+ column = columns_hash[name.to_s]
205
+ if column.nil?
206
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
207
+ `column_for_attribute` will return a null object for non-existent columns
208
+ in Rails 5.0. Use `has_attribute?` if you need to check for an
209
+ attribute's existence.
210
+ MESSAGE
211
211
  end
212
- else
213
- super
212
+ column
214
213
  end
215
214
  end
216
215
 
@@ -231,18 +230,14 @@ module ActiveRecord
231
230
  # person.respond_to('age?') # => true
232
231
  # person.respond_to(:nothing) # => false
233
232
  def respond_to?(name, include_private = false)
233
+ return false unless super
234
234
  name = name.to_s
235
- self.class.define_attribute_methods
236
- result = super
237
-
238
- # If the result is false the answer is false.
239
- return false unless result
240
235
 
241
236
  # If the result is true then check for the select case.
242
237
  # For queries selecting a subset of columns, return false for unselected columns.
243
238
  # We check defined?(@attributes) not to issue warnings if called on objects that
244
239
  # have been allocated but not yet initialized.
245
- if defined?(@attributes) && @attributes.any? && self.class.column_names.include?(name)
240
+ if defined?(@attributes) && self.class.column_names.include?(name)
246
241
  return has_attribute?(name)
247
242
  end
248
243
 
@@ -259,7 +254,7 @@ module ActiveRecord
259
254
  # person.has_attribute?('age') # => true
260
255
  # person.has_attribute?(:nothing) # => false
261
256
  def has_attribute?(attr_name)
262
- @attributes.has_key?(attr_name.to_s)
257
+ @attributes.key?(attr_name.to_s)
263
258
  end
264
259
 
265
260
  # Returns an array of names for the attributes available on this object.
@@ -283,20 +278,13 @@ module ActiveRecord
283
278
  # person.attributes
284
279
  # # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
285
280
  def attributes
286
- attribute_names.each_with_object({}) { |name, attrs|
287
- attrs[name] = read_attribute(name)
288
- }
289
- end
290
-
291
- # Placeholder so it can be overriden when needed by serialization
292
- def attributes_for_coder # :nodoc:
293
- attributes
281
+ @attributes.to_hash
294
282
  end
295
283
 
296
284
  # Returns an <tt>#inspect</tt>-like string for the value of the
297
- # attribute +attr_name+. String attributes are truncated upto 50
285
+ # attribute +attr_name+. String attributes are truncated up to 50
298
286
  # characters, Date and Time attributes are returned in the
299
- # <tt>:db</tt> format, Array attributes are truncated upto 10 values.
287
+ # <tt>:db</tt> format, Array attributes are truncated up to 10 values.
300
288
  # Other attributes return the value of <tt>#inspect</tt> without
301
289
  # modification.
302
290
  #
@@ -333,39 +321,24 @@ module ActiveRecord
333
321
  # class Task < ActiveRecord::Base
334
322
  # end
335
323
  #
336
- # person = Task.new(title: '', is_done: false)
337
- # person.attribute_present?(:title) # => false
338
- # person.attribute_present?(:is_done) # => true
339
- # person.name = 'Francesco'
340
- # person.is_done = true
341
- # person.attribute_present?(:title) # => true
342
- # person.attribute_present?(:is_done) # => true
324
+ # task = Task.new(title: '', is_done: false)
325
+ # task.attribute_present?(:title) # => false
326
+ # task.attribute_present?(:is_done) # => true
327
+ # task.title = 'Buy milk'
328
+ # task.is_done = true
329
+ # task.attribute_present?(:title) # => true
330
+ # task.attribute_present?(:is_done) # => true
343
331
  def attribute_present?(attribute)
344
332
  value = read_attribute(attribute)
345
333
  !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
346
334
  end
347
335
 
348
- # Returns the column object for the named attribute. Returns +nil+ if the
349
- # named attribute not exists.
350
- #
351
- # class Person < ActiveRecord::Base
352
- # end
353
- #
354
- # person = Person.new
355
- # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
356
- # # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
357
- #
358
- # person.column_for_attribute(:nothing)
359
- # # => nil
360
- def column_for_attribute(name)
361
- # FIXME: should this return a null object for columns that don't exist?
362
- self.class.columns_hash[name.to_s]
363
- end
364
-
365
336
  # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
366
337
  # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
367
338
  # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
368
339
  #
340
+ # Note: +:id+ is always present.
341
+ #
369
342
  # Alias for the <tt>read_attribute</tt> method.
370
343
  #
371
344
  # class Person < ActiveRecord::Base
@@ -399,13 +372,6 @@ module ActiveRecord
399
372
 
400
373
  protected
401
374
 
402
- def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
403
- attribute_names.each do |name|
404
- attributes[name] = clone_attribute_value(reader_method, name)
405
- end
406
- attributes
407
- end
408
-
409
375
  def clone_attribute_value(reader_method, attribute_name) # :nodoc:
410
376
  value = send(reader_method, attribute_name)
411
377
  value.duplicable? ? value.clone : value
@@ -423,7 +389,7 @@ module ActiveRecord
423
389
 
424
390
  def attribute_method?(attr_name) # :nodoc:
425
391
  # We check defined? because Syck calls respond_to? before actually calling initialize.
426
- defined?(@attributes) && @attributes.include?(attr_name)
392
+ defined?(@attributes) && @attributes.key?(attr_name)
427
393
  end
428
394
 
429
395
  private
@@ -442,16 +408,16 @@ module ActiveRecord
442
408
 
443
409
  # Filters the primary keys and readonly attributes from the attribute names.
444
410
  def attributes_for_update(attribute_names)
445
- attribute_names.select do |name|
446
- column_for_attribute(name) && !readonly_attribute?(name)
411
+ attribute_names.reject do |name|
412
+ readonly_attribute?(name)
447
413
  end
448
414
  end
449
415
 
450
416
  # Filters out the primary keys, from the attribute names, when the primary
451
417
  # key is to be generated (e.g. the id attribute has no value).
452
418
  def attributes_for_create(attribute_names)
453
- attribute_names.select do |name|
454
- column_for_attribute(name) && !(pk_attribute?(name) && id.nil?)
419
+ attribute_names.reject do |name|
420
+ pk_attribute?(name) && id.nil?
455
421
  end
456
422
  end
457
423
 
@@ -460,13 +426,10 @@ module ActiveRecord
460
426
  end
461
427
 
462
428
  def pk_attribute?(name)
463
- column_for_attribute(name).primary
429
+ name == self.class.primary_key
464
430
  end
465
431
 
466
432
  def typecasted_attribute_value(name)
467
- # FIXME: we need @attributes to be used consistently.
468
- # If the values stored in @attributes were already typecasted, this code
469
- # could be simplified
470
433
  read_attribute(name)
471
434
  end
472
435
  end
@@ -43,7 +43,7 @@ module ActiveRecord
43
43
  # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
44
44
  # task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
45
45
  def read_attribute_before_type_cast(attr_name)
46
- @attributes[attr_name.to_s]
46
+ @attributes[attr_name.to_s].value_before_type_cast
47
47
  end
48
48
 
49
49
  # Returns a hash of attributes before typecasting and deserialization.
@@ -57,7 +57,7 @@ module ActiveRecord
57
57
  # task.attributes_before_type_cast
58
58
  # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
59
59
  def attributes_before_type_cast
60
- @attributes
60
+ @attributes.values_before_type_cast
61
61
  end
62
62
 
63
63
  private