activerecord 5.0.7 → 5.1.7

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 (219) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -2080
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record/aggregations.rb +244 -244
  8. data/lib/active_record/association_relation.rb +5 -5
  9. data/lib/active_record/associations/alias_tracker.rb +10 -11
  10. data/lib/active_record/associations/association.rb +23 -5
  11. data/lib/active_record/associations/association_scope.rb +95 -81
  12. data/lib/active_record/associations/belongs_to_association.rb +7 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +30 -16
  14. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  16. data/lib/active_record/associations/collection_association.rb +36 -205
  17. data/lib/active_record/associations/collection_proxy.rb +132 -63
  18. data/lib/active_record/associations/has_many_association.rb +10 -19
  19. data/lib/active_record/associations/has_many_through_association.rb +12 -4
  20. data/lib/active_record/associations/has_one_association.rb +24 -28
  21. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  22. data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
  23. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  24. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +121 -118
  26. data/lib/active_record/associations/preloader/association.rb +64 -64
  27. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  28. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  29. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  30. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  31. data/lib/active_record/associations/preloader/through_association.rb +41 -41
  32. data/lib/active_record/associations/preloader.rb +94 -94
  33. data/lib/active_record/associations/singular_association.rb +8 -25
  34. data/lib/active_record/associations/through_association.rb +2 -5
  35. data/lib/active_record/associations.rb +1591 -1562
  36. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute_assignment.rb +61 -61
  39. data/lib/active_record/attribute_decorators.rb +35 -13
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +229 -46
  42. data/lib/active_record/attribute_methods/primary_key.rb +74 -73
  43. data/lib/active_record/attribute_methods/read.rb +39 -35
  44. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  46. data/lib/active_record/attribute_methods/write.rb +30 -33
  47. data/lib/active_record/attribute_methods.rb +56 -65
  48. data/lib/active_record/attribute_mutation_tracker.rb +63 -11
  49. data/lib/active_record/attribute_set/builder.rb +27 -33
  50. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  51. data/lib/active_record/attribute_set.rb +9 -6
  52. data/lib/active_record/attributes.rb +22 -22
  53. data/lib/active_record/autosave_association.rb +18 -13
  54. data/lib/active_record/base.rb +24 -22
  55. data/lib/active_record/callbacks.rb +56 -14
  56. data/lib/active_record/coders/yaml_column.rb +9 -11
  57. data/lib/active_record/collection_cache_key.rb +3 -4
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
  70. data/lib/active_record/connection_adapters/column.rb +26 -4
  71. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  72. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  91. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
  92. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  99. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
  116. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
  117. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  118. data/lib/active_record/connection_handling.rb +14 -26
  119. data/lib/active_record/core.rb +109 -93
  120. data/lib/active_record/counter_cache.rb +60 -13
  121. data/lib/active_record/define_callbacks.rb +20 -0
  122. data/lib/active_record/dynamic_matchers.rb +80 -79
  123. data/lib/active_record/enum.rb +8 -6
  124. data/lib/active_record/errors.rb +64 -15
  125. data/lib/active_record/explain.rb +1 -2
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +7 -4
  128. data/lib/active_record/fixture_set/file.rb +11 -8
  129. data/lib/active_record/fixtures.rb +66 -53
  130. data/lib/active_record/gem_version.rb +1 -1
  131. data/lib/active_record/inheritance.rb +93 -79
  132. data/lib/active_record/integration.rb +7 -7
  133. data/lib/active_record/internal_metadata.rb +3 -16
  134. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  135. data/lib/active_record/locking/optimistic.rb +69 -74
  136. data/lib/active_record/locking/pessimistic.rb +10 -1
  137. data/lib/active_record/log_subscriber.rb +23 -28
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +100 -47
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/migration.rb +153 -155
  142. data/lib/active_record/model_schema.rb +94 -107
  143. data/lib/active_record/nested_attributes.rb +200 -199
  144. data/lib/active_record/null_relation.rb +11 -34
  145. data/lib/active_record/persistence.rb +65 -50
  146. data/lib/active_record/query_cache.rb +2 -6
  147. data/lib/active_record/querying.rb +3 -4
  148. data/lib/active_record/railtie.rb +16 -17
  149. data/lib/active_record/railties/controller_runtime.rb +6 -2
  150. data/lib/active_record/railties/databases.rake +105 -133
  151. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  152. data/lib/active_record/readonly_attributes.rb +2 -2
  153. data/lib/active_record/reflection.rb +154 -108
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  155. data/lib/active_record/relation/batches.rb +80 -51
  156. data/lib/active_record/relation/calculations.rb +169 -162
  157. data/lib/active_record/relation/delegation.rb +32 -31
  158. data/lib/active_record/relation/finder_methods.rb +197 -231
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  161. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  162. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  163. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  166. data/lib/active_record/relation/predicate_builder.rb +92 -89
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +255 -293
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +80 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/relation.rb +93 -119
  174. data/lib/active_record/result.rb +41 -32
  175. data/lib/active_record/runtime_registry.rb +3 -3
  176. data/lib/active_record/sanitization.rb +176 -192
  177. data/lib/active_record/schema.rb +3 -3
  178. data/lib/active_record/schema_dumper.rb +15 -38
  179. data/lib/active_record/schema_migration.rb +8 -4
  180. data/lib/active_record/scoping/default.rb +90 -90
  181. data/lib/active_record/scoping/named.rb +11 -11
  182. data/lib/active_record/scoping.rb +6 -6
  183. data/lib/active_record/secure_token.rb +2 -2
  184. data/lib/active_record/statement_cache.rb +13 -15
  185. data/lib/active_record/store.rb +31 -32
  186. data/lib/active_record/suppressor.rb +2 -1
  187. data/lib/active_record/table_metadata.rb +9 -5
  188. data/lib/active_record/tasks/database_tasks.rb +65 -55
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  192. data/lib/active_record/timestamp.rb +46 -25
  193. data/lib/active_record/touch_later.rb +1 -2
  194. data/lib/active_record/transactions.rb +97 -109
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +13 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/internal/abstract_json.rb +4 -0
  199. data/lib/active_record/type/serialized.rb +14 -8
  200. data/lib/active_record/type/text.rb +9 -0
  201. data/lib/active_record/type/time.rb +0 -1
  202. data/lib/active_record/type/type_map.rb +11 -15
  203. data/lib/active_record/type/unsigned_integer.rb +15 -0
  204. data/lib/active_record/type.rb +17 -13
  205. data/lib/active_record/type_caster/connection.rb +8 -6
  206. data/lib/active_record/type_caster/map.rb +3 -1
  207. data/lib/active_record/type_caster.rb +2 -2
  208. data/lib/active_record/validations/associated.rb +1 -1
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +8 -39
  211. data/lib/active_record/validations.rb +4 -4
  212. data/lib/active_record/version.rb +1 -1
  213. data/lib/active_record.rb +20 -20
  214. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  215. data/lib/rails/generators/active_record/migration.rb +1 -1
  216. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  217. data/lib/rails/generators/active_record.rb +4 -4
  218. metadata +24 -13
  219. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,4 +1,4 @@
1
- require 'active_record/attribute'
1
+ require "active_record/attribute"
2
2
 
3
3
  module ActiveRecord
4
4
  class Attribute # :nodoc:
@@ -20,9 +20,11 @@ module ActiveRecord
20
20
  self.class.new(name, user_provided_value, type, original_attribute)
21
21
  end
22
22
 
23
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
24
+ # Workaround for Ruby 2.2 "private attribute?" warning.
23
25
  protected
24
26
 
25
- attr_reader :user_provided_value
27
+ attr_reader :user_provided_value
26
28
  end
27
29
  end
28
30
  end
@@ -77,7 +77,11 @@ module ActiveRecord
77
77
  end
78
78
 
79
79
  def with_type(type)
80
- self.class.new(name, value_before_type_cast, type, original_attribute)
80
+ if changed_in_place?
81
+ with_value_from_user(value).with_type(type)
82
+ else
83
+ self.class.new(name, value_before_type_cast, type, original_attribute)
84
+ end
81
85
  end
82
86
 
83
87
  def type_cast(*)
@@ -108,106 +112,129 @@ module ActiveRecord
108
112
  [self.class, name, value_before_type_cast, type].hash
109
113
  end
110
114
 
111
- protected
112
-
113
- attr_reader :original_attribute
114
- alias_method :assigned?, :original_attribute
115
-
116
- def initialize_dup(other)
117
- if defined?(@value) && @value.duplicable?
118
- @value = @value.dup
119
- end
115
+ def init_with(coder)
116
+ @name = coder["name"]
117
+ @value_before_type_cast = coder["value_before_type_cast"]
118
+ @type = coder["type"]
119
+ @original_attribute = coder["original_attribute"]
120
+ @value = coder["value"] if coder.map.key?("value")
120
121
  end
121
122
 
122
- def changed_from_assignment?
123
- assigned? && type.changed?(original_value, value, value_before_type_cast)
123
+ def encode_with(coder)
124
+ coder["name"] = name
125
+ coder["value_before_type_cast"] = value_before_type_cast unless value_before_type_cast.nil?
126
+ coder["type"] = type if type
127
+ coder["original_attribute"] = original_attribute if original_attribute
128
+ coder["value"] = value if defined?(@value)
124
129
  end
125
130
 
126
- def original_value_for_database
127
- if assigned?
128
- original_attribute.original_value_for_database
129
- else
130
- _original_value_for_database
131
- end
132
- end
131
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
132
+ # Workaround for Ruby 2.2 "private attribute?" warning.
133
+ protected
133
134
 
134
- def _original_value_for_database
135
- type.serialize(original_value)
136
- end
135
+ attr_reader :original_attribute
136
+ alias_method :assigned?, :original_attribute
137
137
 
138
- class FromDatabase < Attribute # :nodoc:
139
- def type_cast(value)
140
- type.deserialize(value)
138
+ def original_value_for_database
139
+ if assigned?
140
+ original_attribute.original_value_for_database
141
+ else
142
+ _original_value_for_database
143
+ end
141
144
  end
142
145
 
143
- def _original_value_for_database
144
- value_before_type_cast
146
+ private
147
+ def initialize_dup(other)
148
+ if defined?(@value) && @value.duplicable?
149
+ @value = @value.dup
150
+ end
145
151
  end
146
- end
147
152
 
148
- class FromUser < Attribute # :nodoc:
149
- def type_cast(value)
150
- type.cast(value)
153
+ def changed_from_assignment?
154
+ assigned? && type.changed?(original_value, value, value_before_type_cast)
151
155
  end
152
156
 
153
- def came_from_user?
154
- true
157
+ def _original_value_for_database
158
+ type.serialize(original_value)
155
159
  end
156
- end
157
160
 
158
- class WithCastValue < Attribute # :nodoc:
159
- def type_cast(value)
160
- value
161
- end
161
+ class FromDatabase < Attribute # :nodoc:
162
+ def type_cast(value)
163
+ type.deserialize(value)
164
+ end
162
165
 
163
- def changed_in_place?
164
- false
166
+ def _original_value_for_database
167
+ value_before_type_cast
168
+ end
165
169
  end
166
- end
167
170
 
168
- class Null < Attribute # :nodoc:
169
- def initialize(name)
170
- super(name, nil, Type::Value.new)
171
- end
171
+ class FromUser < Attribute # :nodoc:
172
+ def type_cast(value)
173
+ type.cast(value)
174
+ end
172
175
 
173
- def type_cast(*)
174
- nil
176
+ def came_from_user?
177
+ true
178
+ end
175
179
  end
176
180
 
177
- def with_type(type)
178
- self.class.with_cast_value(name, nil, type)
179
- end
181
+ class WithCastValue < Attribute # :nodoc:
182
+ def type_cast(value)
183
+ value
184
+ end
180
185
 
181
- def with_value_from_database(value)
182
- raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
186
+ def changed_in_place?
187
+ false
188
+ end
183
189
  end
184
- alias_method :with_value_from_user, :with_value_from_database
185
- end
186
190
 
187
- class Uninitialized < Attribute # :nodoc:
188
- UNINITIALIZED_ORIGINAL_VALUE = Object.new
191
+ class Null < Attribute # :nodoc:
192
+ def initialize(name)
193
+ super(name, nil, Type.default_value)
194
+ end
189
195
 
190
- def initialize(name, type)
191
- super(name, nil, type)
192
- end
196
+ def type_cast(*)
197
+ nil
198
+ end
193
199
 
194
- def value
195
- if block_given?
196
- yield name
200
+ def with_type(type)
201
+ self.class.with_cast_value(name, nil, type)
197
202
  end
198
- end
199
203
 
200
- def original_value
201
- UNINITIALIZED_ORIGINAL_VALUE
204
+ def with_value_from_database(value)
205
+ raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
206
+ end
207
+ alias_method :with_value_from_user, :with_value_from_database
202
208
  end
203
209
 
204
- def value_for_database
205
- end
210
+ class Uninitialized < Attribute # :nodoc:
211
+ UNINITIALIZED_ORIGINAL_VALUE = Object.new
206
212
 
207
- def initialized?
208
- false
213
+ def initialize(name, type)
214
+ super(name, nil, type)
215
+ end
216
+
217
+ def value
218
+ if block_given?
219
+ yield name
220
+ end
221
+ end
222
+
223
+ def original_value
224
+ UNINITIALIZED_ORIGINAL_VALUE
225
+ end
226
+
227
+ def value_for_database
228
+ end
229
+
230
+ def initialized?
231
+ false
232
+ end
233
+
234
+ def with_type(type)
235
+ self.class.new(name, type)
236
+ end
209
237
  end
210
- end
211
- private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue
238
+ private_constant :FromDatabase, :FromUser, :Null, :Uninitialized, :WithCastValue
212
239
  end
213
240
  end
@@ -1,4 +1,4 @@
1
- require 'active_model/forbidden_attributes_protection'
1
+ require "active_model/forbidden_attributes_protection"
2
2
 
3
3
  module ActiveRecord
4
4
  module AttributeAssignment
@@ -12,80 +12,80 @@ module ActiveRecord
12
12
 
13
13
  private
14
14
 
15
- def _assign_attributes(attributes) # :nodoc:
16
- multi_parameter_attributes = {}
17
- nested_parameter_attributes = {}
15
+ def _assign_attributes(attributes)
16
+ multi_parameter_attributes = {}
17
+ nested_parameter_attributes = {}
18
18
 
19
- attributes.each do |k, v|
20
- if k.include?("(")
21
- multi_parameter_attributes[k] = attributes.delete(k)
22
- elsif v.is_a?(Hash)
23
- nested_parameter_attributes[k] = attributes.delete(k)
19
+ attributes.each do |k, v|
20
+ if k.include?("(")
21
+ multi_parameter_attributes[k] = attributes.delete(k)
22
+ elsif v.is_a?(Hash)
23
+ nested_parameter_attributes[k] = attributes.delete(k)
24
+ end
24
25
  end
25
- end
26
- super(attributes)
26
+ super(attributes)
27
27
 
28
- assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
29
- assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
30
- end
28
+ assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
29
+ assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
30
+ end
31
31
 
32
- # Assign any deferred nested attributes after the base attributes have been set.
33
- def assign_nested_parameter_attributes(pairs)
34
- pairs.each { |k, v| _assign_attribute(k, v) }
35
- end
32
+ # Assign any deferred nested attributes after the base attributes have been set.
33
+ def assign_nested_parameter_attributes(pairs)
34
+ pairs.each { |k, v| _assign_attribute(k, v) }
35
+ end
36
36
 
37
- # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
38
- # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
39
- # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
40
- # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
41
- # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
42
- # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
43
- def assign_multiparameter_attributes(pairs)
44
- execute_callstack_for_multiparameter_attributes(
45
- extract_callstack_for_multiparameter_attributes(pairs)
46
- )
47
- end
37
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
38
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
39
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
40
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
41
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
42
+ # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
43
+ def assign_multiparameter_attributes(pairs)
44
+ execute_callstack_for_multiparameter_attributes(
45
+ extract_callstack_for_multiparameter_attributes(pairs)
46
+ )
47
+ end
48
48
 
49
- def execute_callstack_for_multiparameter_attributes(callstack)
50
- errors = []
51
- callstack.each do |name, values_with_empty_parameters|
52
- begin
53
- if values_with_empty_parameters.each_value.all?(&:nil?)
54
- values = nil
55
- else
56
- values = values_with_empty_parameters
49
+ def execute_callstack_for_multiparameter_attributes(callstack)
50
+ errors = []
51
+ callstack.each do |name, values_with_empty_parameters|
52
+ begin
53
+ if values_with_empty_parameters.each_value.all?(&:nil?)
54
+ values = nil
55
+ else
56
+ values = values_with_empty_parameters
57
+ end
58
+ send("#{name}=", values)
59
+ rescue => ex
60
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
57
61
  end
58
- send("#{name}=", values)
59
- rescue => ex
60
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
62
+ end
63
+ unless errors.empty?
64
+ error_descriptions = errors.map(&:message).join(",")
65
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
61
66
  end
62
67
  end
63
- unless errors.empty?
64
- error_descriptions = errors.map(&:message).join(",")
65
- raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
66
- end
67
- end
68
68
 
69
- def extract_callstack_for_multiparameter_attributes(pairs)
70
- attributes = {}
69
+ def extract_callstack_for_multiparameter_attributes(pairs)
70
+ attributes = {}
71
71
 
72
- pairs.each do |(multiparameter_name, value)|
73
- attribute_name = multiparameter_name.split("(").first
74
- attributes[attribute_name] ||= {}
72
+ pairs.each do |(multiparameter_name, value)|
73
+ attribute_name = multiparameter_name.split("(").first
74
+ attributes[attribute_name] ||= {}
75
75
 
76
- parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
77
- attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
78
- end
76
+ parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
77
+ attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
78
+ end
79
79
 
80
- attributes
81
- end
80
+ attributes
81
+ end
82
82
 
83
- def type_cast_attribute_value(multiparameter_name, value)
84
- multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
85
- end
83
+ def type_cast_attribute_value(multiparameter_name, value)
84
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
85
+ end
86
86
 
87
- def find_parameter_position(multiparameter_name)
88
- multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
89
- end
87
+ def find_parameter_position(multiparameter_name)
88
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
89
+ end
90
90
  end
91
91
  end
@@ -8,12 +8,34 @@ module ActiveRecord
8
8
  end
9
9
 
10
10
  module ClassMethods # :nodoc:
11
+ # This method is an internal API used to create class macros such as
12
+ # +serialize+, and features like time zone aware attributes.
13
+ #
14
+ # Used to wrap the type of an attribute in a new type.
15
+ # When the schema for a model is loaded, attributes with the same name as
16
+ # +column_name+ will have their type yielded to the given block. The
17
+ # return value of that block will be used instead.
18
+ #
19
+ # Subsequent calls where +column_name+ and +decorator_name+ are the same
20
+ # will override the previous decorator, not decorate twice. This can be
21
+ # used to create idempotent class macros like +serialize+
11
22
  def decorate_attribute_type(column_name, decorator_name, &block)
12
23
  matcher = ->(name, _) { name == column_name.to_s }
13
24
  key = "_#{column_name}_#{decorator_name}"
14
25
  decorate_matching_attribute_types(matcher, key, &block)
15
26
  end
16
27
 
28
+ # This method is an internal API used to create higher level features like
29
+ # time zone aware attributes.
30
+ #
31
+ # When the schema for a model is loaded, +matcher+ will be called for each
32
+ # attribute with its name and type. If the matcher returns a truthy value,
33
+ # the type will then be yielded to the given block, and the return value
34
+ # of that block will replace the type.
35
+ #
36
+ # Subsequent calls to this method with the same value for +decorator_name+
37
+ # will replace the previous decorator, not decorate twice. This can be
38
+ # used to ensure that class macros are idempotent.
17
39
  def decorate_matching_attribute_types(matcher, decorator_name, &block)
18
40
  reload_schema_from_cache
19
41
  decorator_name = decorator_name.to_s
@@ -24,13 +46,13 @@ module ActiveRecord
24
46
 
25
47
  private
26
48
 
27
- def load_schema!
28
- super
29
- attribute_types.each do |name, type|
30
- decorated_type = attribute_type_decorations.apply(name, type)
31
- define_attribute(name, decorated_type)
49
+ def load_schema!
50
+ super
51
+ attribute_types.each do |name, type|
52
+ decorated_type = attribute_type_decorations.apply(name, type)
53
+ define_attribute(name, decorated_type)
54
+ end
32
55
  end
33
- end
34
56
  end
35
57
 
36
58
  class TypeDecorator # :nodoc:
@@ -53,15 +75,15 @@ module ActiveRecord
53
75
 
54
76
  private
55
77
 
56
- def decorators_for(name, type)
57
- matching(name, type).map(&:last)
58
- end
78
+ def decorators_for(name, type)
79
+ matching(name, type).map(&:last)
80
+ end
59
81
 
60
- def matching(name, type)
61
- @decorations.values.select do |(matcher, _)|
62
- matcher.call(name, type)
82
+ def matching(name, type)
83
+ @decorations.values.select do |(matcher, _)|
84
+ matcher.call(name, type)
85
+ end
63
86
  end
64
- end
65
87
  end
66
88
  end
67
89
  end
@@ -63,14 +63,14 @@ module ActiveRecord
63
63
 
64
64
  private
65
65
 
66
- # Handle *_before_type_cast for method_missing.
67
- def attribute_before_type_cast(attribute_name)
68
- read_attribute_before_type_cast(attribute_name)
69
- end
66
+ # Handle *_before_type_cast for method_missing.
67
+ def attribute_before_type_cast(attribute_name)
68
+ read_attribute_before_type_cast(attribute_name)
69
+ end
70
70
 
71
- def attribute_came_from_user?(attribute_name)
72
- @attributes[attribute_name].came_from_user?
73
- end
71
+ def attribute_came_from_user?(attribute_name)
72
+ @attributes[attribute_name].came_from_user?
73
+ end
74
74
  end
75
75
  end
76
76
  end