activerecord 4.2.11.1 → 5.0.0

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 (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1282 -1195
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record.rb +8 -4
  8. data/lib/active_record/aggregations.rb +35 -24
  9. data/lib/active_record/association_relation.rb +3 -3
  10. data/lib/active_record/associations.rb +317 -209
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +11 -9
  13. data/lib/active_record/associations/association_scope.rb +73 -102
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  22. data/lib/active_record/associations/collection_association.rb +49 -41
  23. data/lib/active_record/associations/collection_proxy.rb +67 -27
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +20 -71
  26. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +29 -19
  29. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  30. data/lib/active_record/associations/preloader.rb +14 -4
  31. data/lib/active_record/associations/preloader/association.rb +46 -52
  32. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  33. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  35. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  36. data/lib/active_record/associations/singular_association.rb +7 -1
  37. data/lib/active_record/associations/through_association.rb +11 -3
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  40. data/lib/active_record/attribute_assignment.rb +19 -140
  41. data/lib/active_record/attribute_decorators.rb +6 -5
  42. data/lib/active_record/attribute_methods.rb +76 -47
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  44. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  45. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  46. data/lib/active_record/attribute_methods/query.rb +2 -2
  47. data/lib/active_record/attribute_methods/read.rb +31 -59
  48. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  50. data/lib/active_record/attribute_methods/write.rb +13 -37
  51. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attribute_set/builder.rb +6 -4
  54. data/lib/active_record/attributes.rb +199 -81
  55. data/lib/active_record/autosave_association.rb +49 -16
  56. data/lib/active_record/base.rb +32 -23
  57. data/lib/active_record/callbacks.rb +39 -43
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +20 -8
  60. data/lib/active_record/collection_cache_key.rb +40 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  62. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  63. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  64. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  65. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  66. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  67. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  69. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  70. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
  71. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  72. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  73. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  74. data/lib/active_record/connection_adapters/column.rb +28 -43
  75. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  76. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  77. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  78. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  79. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  80. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  84. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  85. data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  88. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
  91. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +37 -14
  121. data/lib/active_record/core.rb +89 -107
  122. data/lib/active_record/counter_cache.rb +13 -24
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +113 -76
  125. data/lib/active_record/errors.rb +87 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +76 -40
  130. data/lib/active_record/gem_version.rb +4 -4
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +15 -15
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration.rb +363 -133
  140. data/lib/active_record/migration/command_recorder.rb +59 -18
  141. data/lib/active_record/migration/compatibility.rb +126 -0
  142. data/lib/active_record/model_schema.rb +129 -41
  143. data/lib/active_record/nested_attributes.rb +58 -29
  144. data/lib/active_record/null_relation.rb +16 -8
  145. data/lib/active_record/persistence.rb +121 -80
  146. data/lib/active_record/query_cache.rb +15 -18
  147. data/lib/active_record/querying.rb +10 -9
  148. data/lib/active_record/railtie.rb +23 -16
  149. data/lib/active_record/railties/controller_runtime.rb +1 -1
  150. data/lib/active_record/railties/databases.rake +69 -46
  151. data/lib/active_record/readonly_attributes.rb +1 -1
  152. data/lib/active_record/reflection.rb +282 -115
  153. data/lib/active_record/relation.rb +176 -116
  154. data/lib/active_record/relation/batches.rb +139 -34
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  156. data/lib/active_record/relation/calculations.rb +79 -108
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +163 -81
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +16 -42
  161. data/lib/active_record/relation/predicate_builder.rb +120 -107
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  163. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  164. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  166. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  167. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  168. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  169. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +308 -244
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +4 -7
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/result.rb +4 -3
  177. data/lib/active_record/runtime_registry.rb +1 -1
  178. data/lib/active_record/sanitization.rb +95 -66
  179. data/lib/active_record/schema.rb +26 -22
  180. data/lib/active_record/schema_dumper.rb +62 -38
  181. data/lib/active_record/schema_migration.rb +11 -14
  182. data/lib/active_record/scoping.rb +32 -15
  183. data/lib/active_record/scoping/default.rb +23 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/secure_token.rb +38 -0
  186. data/lib/active_record/serialization.rb +2 -4
  187. data/lib/active_record/statement_cache.rb +16 -14
  188. data/lib/active_record/store.rb +8 -3
  189. data/lib/active_record/suppressor.rb +58 -0
  190. data/lib/active_record/table_metadata.rb +68 -0
  191. data/lib/active_record/tasks/database_tasks.rb +57 -43
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  194. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  195. data/lib/active_record/timestamp.rb +20 -9
  196. data/lib/active_record/touch_later.rb +58 -0
  197. data/lib/active_record/transactions.rb +138 -56
  198. data/lib/active_record/type.rb +66 -17
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -45
  201. data/lib/active_record/type/date_time.rb +2 -49
  202. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  203. data/lib/active_record/type/internal/timezone.rb +15 -0
  204. data/lib/active_record/type/serialized.rb +15 -14
  205. data/lib/active_record/type/time.rb +10 -16
  206. data/lib/active_record/type/type_map.rb +4 -4
  207. data/lib/active_record/type_caster.rb +7 -0
  208. data/lib/active_record/type_caster/connection.rb +29 -0
  209. data/lib/active_record/type_caster/map.rb +19 -0
  210. data/lib/active_record/validations.rb +33 -32
  211. data/lib/active_record/validations/absence.rb +23 -0
  212. data/lib/active_record/validations/associated.rb +10 -3
  213. data/lib/active_record/validations/length.rb +24 -0
  214. data/lib/active_record/validations/presence.rb +11 -12
  215. data/lib/active_record/validations/uniqueness.rb +30 -29
  216. data/lib/rails/generators/active_record/migration.rb +7 -0
  217. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  218. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  219. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +59 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -10,6 +10,10 @@ module ActiveRecord
10
10
  attributes[name] || Attribute.null(name)
11
11
  end
12
12
 
13
+ def []=(name, value)
14
+ attributes[name] = value
15
+ end
16
+
13
17
  def values_before_type_cast
14
18
  attributes.transform_values(&:value_before_type_cast)
15
19
  end
@@ -24,11 +28,19 @@ module ActiveRecord
24
28
  end
25
29
 
26
30
  def keys
27
- attributes.initialized_keys
31
+ attributes.each_key.select { |name| self[name].initialized? }
28
32
  end
29
33
 
30
- def fetch_value(name)
31
- self[name].value { |n| yield n if block_given? }
34
+ if defined?(JRUBY_VERSION)
35
+ # This form is significantly faster on JRuby, and this is one of our biggest hotspots.
36
+ # https://github.com/jruby/jruby/pull/2562
37
+ def fetch_value(name, &block)
38
+ self[name].value(&block)
39
+ end
40
+ else
41
+ def fetch_value(name)
42
+ self[name].value { |n| yield n if block_given? }
43
+ end
32
44
  end
33
45
 
34
46
  def write_from_database(name, value)
@@ -48,6 +60,12 @@ module ActiveRecord
48
60
  super
49
61
  end
50
62
 
63
+ def deep_dup
64
+ dup.tap do |copy|
65
+ copy.instance_variable_set(:@attributes, attributes.deep_dup)
66
+ end
67
+ end
68
+
51
69
  def initialize_dup(_)
52
70
  @attributes = attributes.dup
53
71
  super
@@ -64,6 +82,15 @@ module ActiveRecord
64
82
  end
65
83
  end
66
84
 
85
+ def accessed
86
+ attributes.select { |_, attr| attr.has_been_read? }.keys
87
+ end
88
+
89
+ def map(&block)
90
+ new_attributes = attributes.transform_values(&block)
91
+ AttributeSet.new(new_attributes)
92
+ end
93
+
67
94
  def ==(other)
68
95
  attributes == other.attributes
69
96
  end
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  end
23
23
 
24
24
  class LazyAttributeHash # :nodoc:
25
- delegate :transform_values, to: :materialize
25
+ delegate :transform_values, :each_key, to: :materialize
26
26
 
27
27
  def initialize(types, values, additional_types)
28
28
  @types = types
@@ -47,12 +47,14 @@ module ActiveRecord
47
47
  delegate_hash[key] = value
48
48
  end
49
49
 
50
- def initialized_keys
51
- delegate_hash.keys | values.keys
50
+ def deep_dup
51
+ dup.tap do |copy|
52
+ copy.instance_variable_set(:@delegate_hash, delegate_hash.transform_values(&:dup))
53
+ end
52
54
  end
53
55
 
54
56
  def initialize_dup(_)
55
- @delegate_hash = delegate_hash.transform_values(&:dup)
57
+ @delegate_hash = Hash[delegate_hash]
56
58
  super
57
59
  end
58
60
 
@@ -1,33 +1,44 @@
1
+ require 'active_record/attribute/user_provided_default'
2
+
1
3
  module ActiveRecord
2
- module Attributes # :nodoc:
4
+ # See ActiveRecord::Attributes::ClassMethods for documentation
5
+ module Attributes
3
6
  extend ActiveSupport::Concern
4
7
 
5
- Type = ActiveRecord::Type
6
-
7
8
  included do
8
- class_attribute :user_provided_columns, instance_accessor: false # :internal:
9
- class_attribute :user_provided_defaults, instance_accessor: false # :internal:
10
- self.user_provided_columns = {}
11
- self.user_provided_defaults = {}
12
-
13
- delegate :persistable_attribute_names, to: :class
9
+ class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false # :internal:
10
+ self.attributes_to_define_after_schema_loads = {}
14
11
  end
15
12
 
16
- module ClassMethods # :nodoc:
17
- # Defines or overrides a attribute on this model. This allows customization of
18
- # Active Record's type casting behavior, as well as adding support for user defined
19
- # types.
13
+ module ClassMethods
14
+ # Defines an attribute with a type on this model. It will override the
15
+ # type of existing attributes if needed. This allows control over how
16
+ # values are converted to and from SQL when assigned to a model. It also
17
+ # changes the behavior of values passed to
18
+ # {ActiveRecord::Base.where}[rdoc-ref:QueryMethods#where]. This will let you use
19
+ # your domain objects across much of Active Record, without having to
20
+ # rely on implementation details or monkey patching.
20
21
  #
21
- # +name+ The name of the methods to define attribute methods for, and the column which
22
- # this will persist to.
22
+ # +name+ The name of the methods to define attribute methods for, and the
23
+ # column which this will persist to.
23
24
  #
24
- # +cast_type+ A type object that contains information about how to type cast the value.
25
- # See the examples section for more information.
25
+ # +cast_type+ A symbol such as +:string+ or +:integer+, or a type object
26
+ # to be used for this attribute. See the examples below for more
27
+ # information about providing custom type objects.
26
28
  #
27
29
  # ==== Options
28
- # The options hash accepts the following options:
29
30
  #
30
- # +default+ is the default value that the column should use on a new record.
31
+ # The following options are accepted:
32
+ #
33
+ # +default+ The default value to use when no value is provided. If this option
34
+ # is not passed, the previous default value (if any) will be used.
35
+ # Otherwise, the default will be +nil+.
36
+ #
37
+ # +array+ (PG only) specifies that the type should be an array (see the
38
+ # examples below).
39
+ #
40
+ # +range+ (PG only) specifies that the type should be a range (see the
41
+ # examples below).
31
42
  #
32
43
  # ==== Examples
33
44
  #
@@ -48,99 +59,206 @@ module ActiveRecord
48
59
  # store_listing.price_in_cents # => BigDecimal.new(10.1)
49
60
  #
50
61
  # class StoreListing < ActiveRecord::Base
51
- # attribute :price_in_cents, Type::Integer.new
62
+ # attribute :price_in_cents, :integer
52
63
  # end
53
64
  #
54
65
  # # after
55
66
  # store_listing.price_in_cents # => 10
56
67
  #
57
- # Users may also define their own custom types, as long as they respond to the methods
58
- # defined on the value type. The `type_cast` method on your type object will be called
59
- # with values both from the database, and from your controllers. See
60
- # `ActiveRecord::Attributes::Type::Value` for the expected API. It is recommended that your
61
- # type objects inherit from an existing type, or the base value type.
68
+ # A default can also be provided.
69
+ #
70
+ # # db/schema.rb
71
+ # create_table :store_listings, force: true do |t|
72
+ # t.string :my_string, default: "original default"
73
+ # end
74
+ #
75
+ # StoreListing.new.my_string # => "original default"
76
+ #
77
+ # # app/models/store_listing.rb
78
+ # class StoreListing < ActiveRecord::Base
79
+ # attribute :my_string, :string, default: "new default"
80
+ # end
81
+ #
82
+ # StoreListing.new.my_string # => "new default"
83
+ #
84
+ # class Product < ActiveRecord::Base
85
+ # attribute :my_default_proc, :datetime, default: -> { Time.now }
86
+ # end
87
+ #
88
+ # Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
89
+ # sleep 1
90
+ # Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
91
+ #
92
+ # \Attributes do not need to be backed by a database column.
93
+ #
94
+ # # app/models/my_model.rb
95
+ # class MyModel < ActiveRecord::Base
96
+ # attribute :my_string, :string
97
+ # attribute :my_int_array, :integer, array: true
98
+ # attribute :my_float_range, :float, range: true
99
+ # end
100
+ #
101
+ # model = MyModel.new(
102
+ # my_string: "string",
103
+ # my_int_array: ["1", "2", "3"],
104
+ # my_float_range: "[1,3.5]",
105
+ # )
106
+ # model.attributes
107
+ # # =>
108
+ # {
109
+ # my_string: "string",
110
+ # my_int_array: [1, 2, 3],
111
+ # my_float_range: 1.0..3.5
112
+ # }
113
+ #
114
+ # ==== Creating Custom Types
115
+ #
116
+ # Users may also define their own custom types, as long as they respond
117
+ # to the methods defined on the value type. The method +deserialize+ or
118
+ # +cast+ will be called on your type object, with raw input from the
119
+ # database or from your controllers. See ActiveRecord::Type::Value for the
120
+ # expected API. It is recommended that your type objects inherit from an
121
+ # existing type, or from ActiveRecord::Type::Value
62
122
  #
63
123
  # class MoneyType < ActiveRecord::Type::Integer
64
- # def type_cast(value)
65
- # if value.include?('$')
124
+ # def cast(value)
125
+ # if !value.kind_of?(Numeric) && value.include?('$')
66
126
  # price_in_dollars = value.gsub(/\$/, '').to_f
67
- # price_in_dollars * 100
127
+ # super(price_in_dollars * 100)
68
128
  # else
69
- # value.to_i
129
+ # super
70
130
  # end
71
131
  # end
72
132
  # end
73
133
  #
134
+ # # config/initializers/types.rb
135
+ # ActiveRecord::Type.register(:money, MoneyType)
136
+ #
137
+ # # app/models/store_listing.rb
74
138
  # class StoreListing < ActiveRecord::Base
75
- # attribute :price_in_cents, MoneyType.new
139
+ # attribute :price_in_cents, :money
76
140
  # end
77
141
  #
78
142
  # store_listing = StoreListing.new(price_in_cents: '$10.00')
79
143
  # store_listing.price_in_cents # => 1000
80
- def attribute(name, cast_type, options = {})
144
+ #
145
+ # For more details on creating custom types, see the documentation for
146
+ # ActiveRecord::Type::Value. For more details on registering your types
147
+ # to be referenced by a symbol, see ActiveRecord::Type.register. You can
148
+ # also pass a type object directly, in place of a symbol.
149
+ #
150
+ # ==== \Querying
151
+ #
152
+ # When {ActiveRecord::Base.where}[rdoc-ref:QueryMethods#where] is called, it will
153
+ # use the type defined by the model class to convert the value to SQL,
154
+ # calling +serialize+ on your type object. For example:
155
+ #
156
+ # class Money < Struct.new(:amount, :currency)
157
+ # end
158
+ #
159
+ # class MoneyType < Type::Value
160
+ # def initialize(currency_converter:)
161
+ # @currency_converter = currency_converter
162
+ # end
163
+ #
164
+ # # value will be the result of +deserialize+ or
165
+ # # +cast+. Assumed to be an instance of +Money+ in
166
+ # # this case.
167
+ # def serialize(value)
168
+ # value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
169
+ # value_in_bitcoins.amount
170
+ # end
171
+ # end
172
+ #
173
+ # # config/initializers/types.rb
174
+ # ActiveRecord::Type.register(:money, MoneyType)
175
+ #
176
+ # # app/models/product.rb
177
+ # class Product < ActiveRecord::Base
178
+ # currency_converter = ConversionRatesFromTheInternet.new
179
+ # attribute :price_in_bitcoins, :money, currency_converter: currency_converter
180
+ # end
181
+ #
182
+ # Product.where(price_in_bitcoins: Money.new(5, "USD"))
183
+ # # => SELECT * FROM products WHERE price_in_bitcoins = 0.02230
184
+ #
185
+ # Product.where(price_in_bitcoins: Money.new(5, "GBP"))
186
+ # # => SELECT * FROM products WHERE price_in_bitcoins = 0.03412
187
+ #
188
+ # ==== Dirty Tracking
189
+ #
190
+ # The type of an attribute is given the opportunity to change how dirty
191
+ # tracking is performed. The methods +changed?+ and +changed_in_place?+
192
+ # will be called from ActiveModel::Dirty. See the documentation for those
193
+ # methods in ActiveRecord::Type::Value for more details.
194
+ def attribute(name, cast_type, **options)
81
195
  name = name.to_s
82
- clear_caches_calculated_from_columns
83
- # Assign a new hash to ensure that subclasses do not share a hash
84
- self.user_provided_columns = user_provided_columns.merge(name => cast_type)
196
+ reload_schema_from_cache
85
197
 
86
- if options.key?(:default)
87
- self.user_provided_defaults = user_provided_defaults.merge(name => options[:default])
88
- end
89
- end
90
-
91
- # Returns an array of column objects for the table associated with this class.
92
- def columns
93
- @columns ||= add_user_provided_columns(connection.schema_cache.columns(table_name))
198
+ self.attributes_to_define_after_schema_loads =
199
+ attributes_to_define_after_schema_loads.merge(
200
+ name => [cast_type, options]
201
+ )
94
202
  end
95
203
 
96
- # Returns a hash of column objects for the table associated with this class.
97
- def columns_hash
98
- @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
99
- end
100
-
101
- def persistable_attribute_names # :nodoc:
102
- @persistable_attribute_names ||= connection.schema_cache.columns_hash(table_name).keys
204
+ # This is the low level API which sits beneath +attribute+. It only
205
+ # accepts type objects, and will do its work immediately instead of
206
+ # waiting for the schema to load. Automatic schema detection and
207
+ # ClassMethods#attribute both call this under the hood. While this method
208
+ # is provided so it can be used by plugin authors, application code
209
+ # should probably use ClassMethods#attribute.
210
+ #
211
+ # +name+ The name of the attribute being defined. Expected to be a +String+.
212
+ #
213
+ # +cast_type+ The type object to use for this attribute.
214
+ #
215
+ # +default+ The default value to use when no value is provided. If this option
216
+ # is not passed, the previous default value (if any) will be used.
217
+ # Otherwise, the default will be +nil+. A proc can also be passed, and
218
+ # will be called once each time a new value is needed.
219
+ #
220
+ # +user_provided_default+ Whether the default value should be cast using
221
+ # +cast+ or +deserialize+.
222
+ def define_attribute(
223
+ name,
224
+ cast_type,
225
+ default: NO_DEFAULT_PROVIDED,
226
+ user_provided_default: true
227
+ )
228
+ attribute_types[name] = cast_type
229
+ define_default_attribute(name, default, cast_type, from_user: user_provided_default)
103
230
  end
104
231
 
105
- def reset_column_information # :nodoc:
232
+ def load_schema! # :nodoc:
106
233
  super
107
- clear_caches_calculated_from_columns
108
- end
109
-
110
- private
111
-
112
- def add_user_provided_columns(schema_columns)
113
- existing_columns = schema_columns.map do |column|
114
- new_type = user_provided_columns[column.name]
115
- if new_type
116
- column.with_type(new_type)
117
- else
118
- column
234
+ attributes_to_define_after_schema_loads.each do |name, (type, options)|
235
+ if type.is_a?(Symbol)
236
+ type = ActiveRecord::Type.lookup(type, **options.except(:default))
119
237
  end
120
- end
121
238
 
122
- existing_column_names = existing_columns.map(&:name)
123
- new_columns = user_provided_columns.except(*existing_column_names).map do |(name, type)|
124
- connection.new_column(name, nil, type)
239
+ define_attribute(name, type, **options.slice(:default))
125
240
  end
126
-
127
- existing_columns + new_columns
128
241
  end
129
242
 
130
- def clear_caches_calculated_from_columns
131
- @attributes_builder = nil
132
- @column_names = nil
133
- @column_types = nil
134
- @columns = nil
135
- @columns_hash = nil
136
- @content_columns = nil
137
- @default_attributes = nil
138
- @persistable_attribute_names = nil
139
- @attribute_names = nil
140
- end
243
+ private
244
+
245
+ NO_DEFAULT_PROVIDED = Object.new # :nodoc:
246
+ private_constant :NO_DEFAULT_PROVIDED
141
247
 
142
- def raw_default_values
143
- super.merge(user_provided_defaults)
248
+ def define_default_attribute(name, value, type, from_user:)
249
+ if value == NO_DEFAULT_PROVIDED
250
+ default_attribute = _default_attributes[name].with_type(type)
251
+ elsif from_user
252
+ default_attribute = Attribute::UserProvidedDefault.new(
253
+ name,
254
+ value,
255
+ type,
256
+ _default_attributes[name],
257
+ )
258
+ else
259
+ default_attribute = Attribute.from_database(name, value, type)
260
+ end
261
+ _default_attributes[name] = default_attribute
144
262
  end
145
263
  end
146
264
  end
@@ -1,10 +1,10 @@
1
1
  module ActiveRecord
2
2
  # = Active Record Autosave Association
3
3
  #
4
- # +AutosaveAssociation+ is a module that takes care of automatically saving
4
+ # AutosaveAssociation is a module that takes care of automatically saving
5
5
  # associated records when their parent is saved. In addition to saving, it
6
6
  # also destroys any associated records that were marked for destruction.
7
- # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
7
+ # (See #mark_for_destruction and #marked_for_destruction?).
8
8
  #
9
9
  # Saving of the parent, its associations, and the destruction of marked
10
10
  # associations, all happen inside a transaction. This should never leave the
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  #
23
23
  # == Validation
24
24
  #
25
- # Children records are validated unless <tt>:validate</tt> is +false+.
25
+ # Child records are validated unless <tt>:validate</tt> is +false+.
26
26
  #
27
27
  # == Callbacks
28
28
  #
@@ -125,7 +125,6 @@ module ActiveRecord
125
125
  # Now it _is_ removed from the database:
126
126
  #
127
127
  # Comment.find_by(id: id).nil? # => true
128
-
129
128
  module AutosaveAssociation
130
129
  extend ActiveSupport::Concern
131
130
 
@@ -141,9 +140,11 @@ module ActiveRecord
141
140
 
142
141
  included do
143
142
  Associations::Builder::Association.extensions << AssociationBuilderExtension
143
+ mattr_accessor :index_nested_attribute_errors, instance_writer: false
144
+ self.index_nested_attribute_errors = false
144
145
  end
145
146
 
146
- module ClassMethods
147
+ module ClassMethods # :nodoc:
147
148
  private
148
149
 
149
150
  def define_non_cyclic_method(name, &block)
@@ -198,7 +199,7 @@ module ActiveRecord
198
199
  after_create save_method
199
200
  after_update save_method
200
201
  else
201
- define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
202
+ define_non_cyclic_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
202
203
  before_save save_method
203
204
  end
204
205
 
@@ -214,8 +215,15 @@ module ActiveRecord
214
215
  method = :validate_single_association
215
216
  end
216
217
 
217
- define_non_cyclic_method(validation_method) { send(method, reflection) }
218
+ define_non_cyclic_method(validation_method) do
219
+ send(method, reflection)
220
+ # TODO: remove the following line as soon as the return value of
221
+ # callbacks is ignored, that is, returning `false` does not
222
+ # display a deprecation warning or halts the callback chain.
223
+ true
224
+ end
218
225
  validate validation_method
226
+ after_validation :_ensure_no_duplicate_errors
219
227
  end
220
228
  end
221
229
  end
@@ -227,7 +235,7 @@ module ActiveRecord
227
235
  super
228
236
  end
229
237
 
230
- # Marks this record to be destroyed as part of the parents save transaction.
238
+ # Marks this record to be destroyed as part of the parent's save transaction.
231
239
  # This does _not_ actually destroy the record instantly, rather child record will be destroyed
232
240
  # when <tt>parent.save</tt> is called.
233
241
  #
@@ -236,7 +244,7 @@ module ActiveRecord
236
244
  @marked_for_destruction = true
237
245
  end
238
246
 
239
- # Returns whether or not this record will be destroyed as part of the parents save transaction.
247
+ # Returns whether or not this record will be destroyed as part of the parent's save transaction.
240
248
  #
241
249
  # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
242
250
  def marked_for_destruction?
@@ -271,9 +279,9 @@ module ActiveRecord
271
279
  if new_record
272
280
  association && association.target
273
281
  elsif autosave
274
- association.target.find_all { |record| record.changed_for_autosave? }
282
+ association.target.find_all(&:changed_for_autosave?)
275
283
  else
276
- association.target.find_all { |record| record.new_record? }
284
+ association.target.find_all(&:new_record?)
277
285
  end
278
286
  end
279
287
 
@@ -309,7 +317,7 @@ module ActiveRecord
309
317
  def validate_collection_association(reflection)
310
318
  if association = association_instance_get(reflection.name)
311
319
  if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
312
- records.each { |record| association_valid?(reflection, record) }
320
+ records.each_with_index { |record, index| association_valid?(reflection, record, index) }
313
321
  end
314
322
  end
315
323
  end
@@ -317,17 +325,36 @@ module ActiveRecord
317
325
  # Returns whether or not the association is valid and applies any errors to
318
326
  # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
319
327
  # enabled records if they're marked_for_destruction? or destroyed.
320
- def association_valid?(reflection, record)
328
+ def association_valid?(reflection, record, index=nil)
321
329
  return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
322
330
 
323
331
  validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
324
332
  unless valid = record.valid?(validation_context)
325
333
  if reflection.options[:autosave]
334
+ indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
335
+
326
336
  record.errors.each do |attribute, message|
327
- attribute = "#{reflection.name}.#{attribute}"
337
+ if indexed_attribute
338
+ attribute = "#{reflection.name}[#{index}].#{attribute}"
339
+ else
340
+ attribute = "#{reflection.name}.#{attribute}"
341
+ end
328
342
  errors[attribute] << message
329
343
  errors[attribute].uniq!
330
344
  end
345
+
346
+ record.errors.details.each_key do |attribute|
347
+ if indexed_attribute
348
+ reflection_attribute = "#{reflection.name}[#{index}].#{attribute}"
349
+ else
350
+ reflection_attribute = "#{reflection.name}.#{attribute}"
351
+ end
352
+
353
+ record.errors.details[attribute].each do |error|
354
+ errors.details[reflection_attribute] << error
355
+ errors.details[reflection_attribute].uniq!
356
+ end
357
+ end
331
358
  else
332
359
  errors.add(reflection.name)
333
360
  end
@@ -346,7 +373,7 @@ module ActiveRecord
346
373
  # <tt>:autosave</tt> is enabled on the association.
347
374
  #
348
375
  # In addition, it destroys all children that were marked for destruction
349
- # with mark_for_destruction.
376
+ # with #mark_for_destruction.
350
377
  #
351
378
  # This all happens inside a transaction, _if_ the Transactions module is included into
352
379
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
@@ -389,7 +416,7 @@ module ActiveRecord
389
416
  # on the association.
390
417
  #
391
418
  # In addition, it will destroy the association if it was marked for
392
- # destruction with mark_for_destruction.
419
+ # destruction with #mark_for_destruction.
393
420
  #
394
421
  # This all happens inside a transaction, _if_ the Transactions module is included into
395
422
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
@@ -450,5 +477,11 @@ module ActiveRecord
450
477
  end
451
478
  end
452
479
  end
480
+
481
+ def _ensure_no_duplicate_errors
482
+ errors.messages.each_key do |attribute|
483
+ errors[attribute].uniq!
484
+ end
485
+ end
453
486
  end
454
487
  end