activerecord 4.1.8 → 4.2.11.3

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 (186) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1165 -1591
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +15 -8
  5. data/lib/active_record/association_relation.rb +13 -0
  6. data/lib/active_record/associations/alias_tracker.rb +3 -12
  7. data/lib/active_record/associations/association.rb +16 -4
  8. data/lib/active_record/associations/association_scope.rb +84 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  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/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -14
  14. data/lib/active_record/associations/builder/has_many.rb +1 -1
  15. data/lib/active_record/associations/builder/has_one.rb +2 -2
  16. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  17. data/lib/active_record/associations/collection_association.rb +87 -30
  18. data/lib/active_record/associations/collection_proxy.rb +33 -35
  19. data/lib/active_record/associations/foreign_association.rb +11 -0
  20. data/lib/active_record/associations/has_many_association.rb +83 -22
  21. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  22. data/lib/active_record/associations/has_one_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  24. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -12
  26. data/lib/active_record/associations/preloader/association.rb +14 -10
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +37 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +16 -12
  31. data/lib/active_record/associations.rb +158 -49
  32. data/lib/active_record/attribute.rb +163 -0
  33. data/lib/active_record/attribute_assignment.rb +20 -12
  34. data/lib/active_record/attribute_decorators.rb +66 -0
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  37. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  38. data/lib/active_record/attribute_methods/query.rb +1 -1
  39. data/lib/active_record/attribute_methods/read.rb +22 -59
  40. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -28
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +57 -95
  44. data/lib/active_record/attribute_set/builder.rb +106 -0
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attributes.rb +147 -0
  47. data/lib/active_record/autosave_association.rb +30 -12
  48. data/lib/active_record/base.rb +13 -24
  49. data/lib/active_record/callbacks.rb +6 -6
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +85 -53
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +139 -57
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +271 -74
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -60
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +295 -141
  62. data/lib/active_record/connection_adapters/column.rb +29 -240
  63. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +17 -33
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -145
  66. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  69. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -385
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +134 -43
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  101. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  103. data/lib/active_record/connection_handling.rb +1 -1
  104. data/lib/active_record/core.rb +163 -40
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +10 -12
  107. data/lib/active_record/errors.rb +53 -30
  108. data/lib/active_record/explain.rb +1 -1
  109. data/lib/active_record/explain_subscriber.rb +1 -1
  110. data/lib/active_record/fixtures.rb +62 -74
  111. data/lib/active_record/gem_version.rb +4 -4
  112. data/lib/active_record/inheritance.rb +35 -10
  113. data/lib/active_record/integration.rb +4 -4
  114. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  115. data/lib/active_record/locking/optimistic.rb +46 -26
  116. data/lib/active_record/migration/command_recorder.rb +19 -2
  117. data/lib/active_record/migration/join_table.rb +1 -1
  118. data/lib/active_record/migration.rb +79 -47
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +18 -8
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +48 -27
  123. data/lib/active_record/query_cache.rb +3 -3
  124. data/lib/active_record/querying.rb +10 -7
  125. data/lib/active_record/railtie.rb +19 -14
  126. data/lib/active_record/railties/databases.rake +55 -56
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +281 -117
  129. data/lib/active_record/relation/batches.rb +0 -1
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/delegation.rb +1 -1
  132. data/lib/active_record/relation/finder_methods.rb +71 -48
  133. data/lib/active_record/relation/merger.rb +39 -29
  134. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  135. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  136. data/lib/active_record/relation/predicate_builder.rb +42 -12
  137. data/lib/active_record/relation/query_methods.rb +130 -73
  138. data/lib/active_record/relation/spawn_methods.rb +10 -3
  139. data/lib/active_record/relation.rb +57 -25
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -8
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +54 -28
  155. data/lib/active_record/type/big_integer.rb +13 -0
  156. data/lib/active_record/type/binary.rb +50 -0
  157. data/lib/active_record/type/boolean.rb +31 -0
  158. data/lib/active_record/type/date.rb +50 -0
  159. data/lib/active_record/type/date_time.rb +54 -0
  160. data/lib/active_record/type/decimal.rb +64 -0
  161. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  162. data/lib/active_record/type/decorator.rb +14 -0
  163. data/lib/active_record/type/float.rb +19 -0
  164. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  165. data/lib/active_record/type/integer.rb +59 -0
  166. data/lib/active_record/type/mutable.rb +16 -0
  167. data/lib/active_record/type/numeric.rb +36 -0
  168. data/lib/active_record/type/serialized.rb +62 -0
  169. data/lib/active_record/type/string.rb +40 -0
  170. data/lib/active_record/type/text.rb +11 -0
  171. data/lib/active_record/type/time.rb +26 -0
  172. data/lib/active_record/type/time_value.rb +38 -0
  173. data/lib/active_record/type/type_map.rb +64 -0
  174. data/lib/active_record/type/unsigned_integer.rb +15 -0
  175. data/lib/active_record/type/value.rb +110 -0
  176. data/lib/active_record/type.rb +23 -0
  177. data/lib/active_record/validations/associated.rb +5 -3
  178. data/lib/active_record/validations/presence.rb +5 -3
  179. data/lib/active_record/validations/uniqueness.rb +24 -20
  180. data/lib/active_record/validations.rb +25 -19
  181. data/lib/active_record.rb +5 -0
  182. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  183. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  184. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  185. metadata +66 -11
  186. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -0,0 +1,106 @@
1
+ require 'active_record/attribute'
2
+
3
+ module ActiveRecord
4
+ class AttributeSet # :nodoc:
5
+ class Builder # :nodoc:
6
+ attr_reader :types, :always_initialized
7
+
8
+ def initialize(types, always_initialized = nil)
9
+ @types = types
10
+ @always_initialized = always_initialized
11
+ end
12
+
13
+ def build_from_database(values = {}, additional_types = {})
14
+ if always_initialized && !values.key?(always_initialized)
15
+ values[always_initialized] = nil
16
+ end
17
+
18
+ attributes = LazyAttributeHash.new(types, values, additional_types)
19
+ AttributeSet.new(attributes)
20
+ end
21
+ end
22
+ end
23
+
24
+ class LazyAttributeHash # :nodoc:
25
+ delegate :transform_values, to: :materialize
26
+
27
+ def initialize(types, values, additional_types)
28
+ @types = types
29
+ @values = values
30
+ @additional_types = additional_types
31
+ @materialized = false
32
+ @delegate_hash = {}
33
+ end
34
+
35
+ def key?(key)
36
+ delegate_hash.key?(key) || values.key?(key) || types.key?(key)
37
+ end
38
+
39
+ def [](key)
40
+ delegate_hash[key] || assign_default_value(key)
41
+ end
42
+
43
+ def []=(key, value)
44
+ if frozen?
45
+ raise RuntimeError, "Can't modify frozen hash"
46
+ end
47
+ delegate_hash[key] = value
48
+ end
49
+
50
+ def initialized_keys
51
+ delegate_hash.keys | values.keys
52
+ end
53
+
54
+ def initialize_dup(_)
55
+ @delegate_hash = delegate_hash.transform_values(&:dup)
56
+ super
57
+ end
58
+
59
+ def select
60
+ keys = types.keys | values.keys | delegate_hash.keys
61
+ keys.each_with_object({}) do |key, hash|
62
+ attribute = self[key]
63
+ if yield(key, attribute)
64
+ hash[key] = attribute
65
+ end
66
+ end
67
+ end
68
+
69
+ def ==(other)
70
+ if other.is_a?(LazyAttributeHash)
71
+ materialize == other.materialize
72
+ else
73
+ materialize == other
74
+ end
75
+ end
76
+
77
+ protected
78
+
79
+ attr_reader :types, :values, :additional_types, :delegate_hash
80
+
81
+ def materialize
82
+ unless @materialized
83
+ values.each_key { |key| self[key] }
84
+ types.each_key { |key| self[key] }
85
+ unless frozen?
86
+ @materialized = true
87
+ end
88
+ end
89
+ delegate_hash
90
+ end
91
+
92
+ private
93
+
94
+ def assign_default_value(name)
95
+ type = additional_types.fetch(name, types[name])
96
+ value_present = true
97
+ value = values.fetch(name) { value_present = false }
98
+
99
+ if value_present
100
+ delegate_hash[name] = Attribute.from_database(name, value, type)
101
+ elsif types.key?(name)
102
+ delegate_hash[name] = Attribute.uninitialized(name, type)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,81 @@
1
+ require 'active_record/attribute_set/builder'
2
+
3
+ module ActiveRecord
4
+ class AttributeSet # :nodoc:
5
+ def initialize(attributes)
6
+ @attributes = attributes
7
+ end
8
+
9
+ def [](name)
10
+ attributes[name] || Attribute.null(name)
11
+ end
12
+
13
+ def values_before_type_cast
14
+ attributes.transform_values(&:value_before_type_cast)
15
+ end
16
+
17
+ def to_hash
18
+ initialized_attributes.transform_values(&:value)
19
+ end
20
+ alias_method :to_h, :to_hash
21
+
22
+ def key?(name)
23
+ attributes.key?(name) && self[name].initialized?
24
+ end
25
+
26
+ def keys
27
+ attributes.initialized_keys
28
+ end
29
+
30
+ def fetch_value(name)
31
+ self[name].value { |n| yield n if block_given? }
32
+ end
33
+
34
+ def write_from_database(name, value)
35
+ attributes[name] = self[name].with_value_from_database(value)
36
+ end
37
+
38
+ def write_from_user(name, value)
39
+ attributes[name] = self[name].with_value_from_user(value)
40
+ end
41
+
42
+ def write_cast_value(name, value)
43
+ attributes[name] = self[name].with_cast_value(value)
44
+ end
45
+
46
+ def freeze
47
+ @attributes.freeze
48
+ super
49
+ end
50
+
51
+ def initialize_dup(_)
52
+ @attributes = attributes.dup
53
+ super
54
+ end
55
+
56
+ def initialize_clone(_)
57
+ @attributes = attributes.clone
58
+ super
59
+ end
60
+
61
+ def reset(key)
62
+ if key?(key)
63
+ write_from_database(key, nil)
64
+ end
65
+ end
66
+
67
+ def ==(other)
68
+ attributes == other.attributes
69
+ end
70
+
71
+ protected
72
+
73
+ attr_reader :attributes
74
+
75
+ private
76
+
77
+ def initialized_attributes
78
+ attributes.select { |_, attr| attr.initialized? }
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,147 @@
1
+ module ActiveRecord
2
+ module Attributes # :nodoc:
3
+ extend ActiveSupport::Concern
4
+
5
+ Type = ActiveRecord::Type
6
+
7
+ 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
14
+ end
15
+
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.
20
+ #
21
+ # +name+ The name of the methods to define attribute methods for, and the column which
22
+ # this will persist to.
23
+ #
24
+ # +cast_type+ A type object that contains information about how to type cast the value.
25
+ # See the examples section for more information.
26
+ #
27
+ # ==== Options
28
+ # The options hash accepts the following options:
29
+ #
30
+ # +default+ is the default value that the column should use on a new record.
31
+ #
32
+ # ==== Examples
33
+ #
34
+ # The type detected by Active Record can be overridden.
35
+ #
36
+ # # db/schema.rb
37
+ # create_table :store_listings, force: true do |t|
38
+ # t.decimal :price_in_cents
39
+ # end
40
+ #
41
+ # # app/models/store_listing.rb
42
+ # class StoreListing < ActiveRecord::Base
43
+ # end
44
+ #
45
+ # store_listing = StoreListing.new(price_in_cents: '10.1')
46
+ #
47
+ # # before
48
+ # store_listing.price_in_cents # => BigDecimal.new(10.1)
49
+ #
50
+ # class StoreListing < ActiveRecord::Base
51
+ # attribute :price_in_cents, Type::Integer.new
52
+ # end
53
+ #
54
+ # # after
55
+ # store_listing.price_in_cents # => 10
56
+ #
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.
62
+ #
63
+ # class MoneyType < ActiveRecord::Type::Integer
64
+ # def type_cast(value)
65
+ # if value.include?('$')
66
+ # price_in_dollars = value.gsub(/\$/, '').to_f
67
+ # price_in_dollars * 100
68
+ # else
69
+ # value.to_i
70
+ # end
71
+ # end
72
+ # end
73
+ #
74
+ # class StoreListing < ActiveRecord::Base
75
+ # attribute :price_in_cents, MoneyType.new
76
+ # end
77
+ #
78
+ # store_listing = StoreListing.new(price_in_cents: '$10.00')
79
+ # store_listing.price_in_cents # => 1000
80
+ def attribute(name, cast_type, options = {})
81
+ 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)
85
+
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))
94
+ end
95
+
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
103
+ end
104
+
105
+ def reset_column_information # :nodoc:
106
+ 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
119
+ end
120
+ end
121
+
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)
125
+ end
126
+
127
+ existing_columns + new_columns
128
+ end
129
+
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
141
+
142
+ def raw_default_values
143
+ super.merge(user_provided_defaults)
144
+ end
145
+ end
146
+ end
147
+ end
@@ -177,17 +177,15 @@ module ActiveRecord
177
177
  # before actually defining them.
178
178
  def add_autosave_association_callbacks(reflection)
179
179
  save_method = :"autosave_associated_records_for_#{reflection.name}"
180
- validation_method = :"validate_associated_records_for_#{reflection.name}"
181
- collection = reflection.collection?
182
180
 
183
- if collection
181
+ if reflection.collection?
184
182
  before_save :before_save_collection_association
185
183
 
186
184
  define_non_cyclic_method(save_method) { save_collection_association(reflection) }
187
185
  # Doesn't use after_save as that would save associations added in after_create/after_update twice
188
186
  after_create save_method
189
187
  after_update save_method
190
- elsif reflection.macro == :has_one
188
+ elsif reflection.has_one?
191
189
  define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
192
190
  # Configures two callbacks instead of a single after_save so that
193
191
  # the model may rely on their execution order relative to its
@@ -204,8 +202,18 @@ module ActiveRecord
204
202
  before_save save_method
205
203
  end
206
204
 
205
+ define_autosave_validation_callbacks(reflection)
206
+ end
207
+
208
+ def define_autosave_validation_callbacks(reflection)
209
+ validation_method = :"validate_associated_records_for_#{reflection.name}"
207
210
  if reflection.validate? && !method_defined?(validation_method)
208
- method = (collection ? :validate_collection_association : :validate_single_association)
211
+ if reflection.collection?
212
+ method = :validate_collection_association
213
+ else
214
+ method = :validate_single_association
215
+ end
216
+
209
217
  define_non_cyclic_method(validation_method) { send(method, reflection) }
210
218
  validate validation_method
211
219
  end
@@ -272,11 +280,18 @@ module ActiveRecord
272
280
  # go through nested autosave associations that are loaded in memory (without loading
273
281
  # any new ones), and return true if is changed for autosave
274
282
  def nested_records_changed_for_autosave?
275
- self.class._reflections.values.any? do |reflection|
276
- if reflection.options[:autosave]
277
- association = association_instance_get(reflection.name)
278
- association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
283
+ @_nested_records_changed_for_autosave_already_called ||= false
284
+ return false if @_nested_records_changed_for_autosave_already_called
285
+ begin
286
+ @_nested_records_changed_for_autosave_already_called = true
287
+ self.class._reflections.values.any? do |reflection|
288
+ if reflection.options[:autosave]
289
+ association = association_instance_get(reflection.name)
290
+ association && Array.wrap(association.target).any?(&:changed_for_autosave?)
291
+ end
279
292
  end
293
+ ensure
294
+ @_nested_records_changed_for_autosave_already_called = false
280
295
  end
281
296
  end
282
297
 
@@ -303,9 +318,10 @@ module ActiveRecord
303
318
  # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
304
319
  # enabled records if they're marked_for_destruction? or destroyed.
305
320
  def association_valid?(reflection, record)
306
- return true if record.destroyed? || record.marked_for_destruction?
321
+ return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
307
322
 
308
- unless valid = record.valid?
323
+ validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
324
+ unless valid = record.valid?(validation_context)
309
325
  if reflection.options[:autosave]
310
326
  record.errors.each do |attribute, message|
311
327
  attribute = "#{reflection.name}.#{attribute}"
@@ -404,7 +420,9 @@ module ActiveRecord
404
420
 
405
421
  # If the record is new or it has changed, returns true.
406
422
  def record_changed?(reflection, record, key)
407
- record.new_record? || record[reflection.foreign_key] != key || record.attribute_changed?(reflection.foreign_key)
423
+ record.new_record? ||
424
+ (record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
425
+ record.attribute_changed?(reflection.foreign_key)
408
426
  end
409
427
 
410
428
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
@@ -9,16 +9,19 @@ require 'active_support/core_ext/class/delegating_attributes'
9
9
  require 'active_support/core_ext/array/extract_options'
10
10
  require 'active_support/core_ext/hash/deep_merge'
11
11
  require 'active_support/core_ext/hash/slice'
12
+ require 'active_support/core_ext/hash/transform_values'
12
13
  require 'active_support/core_ext/string/behavior'
13
14
  require 'active_support/core_ext/kernel/singleton_class'
14
15
  require 'active_support/core_ext/module/introspection'
15
16
  require 'active_support/core_ext/object/duplicable'
16
17
  require 'active_support/core_ext/class/subclasses'
17
18
  require 'arel'
19
+ require 'active_record/attribute_decorators'
18
20
  require 'active_record/errors'
19
21
  require 'active_record/log_subscriber'
20
22
  require 'active_record/explain_subscriber'
21
23
  require 'active_record/relation/delegation'
24
+ require 'active_record/attributes'
22
25
 
23
26
  module ActiveRecord #:nodoc:
24
27
  # = Active Record
@@ -116,28 +119,28 @@ module ActiveRecord #:nodoc:
116
119
  # All column values are automatically available through basic accessors on the Active Record
117
120
  # object, but sometimes you want to specialize this behavior. This can be done by overwriting
118
121
  # the default accessors (using the same name as the attribute) and calling
119
- # <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually
120
- # change things.
122
+ # +super+ to actually change things.
121
123
  #
122
124
  # class Song < ActiveRecord::Base
123
125
  # # Uses an integer of seconds to hold the length of the song
124
126
  #
125
127
  # def length=(minutes)
126
- # write_attribute(:length, minutes.to_i * 60)
128
+ # super(minutes.to_i * 60)
127
129
  # end
128
130
  #
129
131
  # def length
130
- # read_attribute(:length) / 60
132
+ # super / 60
131
133
  # end
132
134
  # end
133
135
  #
134
136
  # You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
135
- # instead of <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
137
+ # or <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
136
138
  #
137
139
  # == Attribute query methods
138
140
  #
139
141
  # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
140
142
  # Query methods allow you to test whether an attribute value is present.
143
+ # For numeric values, present is defined as non-zero.
141
144
  #
142
145
  # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
143
146
  # to determine whether the user has a name:
@@ -217,25 +220,9 @@ module ActiveRecord #:nodoc:
217
220
  #
218
221
  # == Single table inheritance
219
222
  #
220
- # Active Record allows inheritance by storing the name of the class in a column that by
221
- # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
222
- # This means that an inheritance looking like this:
223
- #
224
- # class Company < ActiveRecord::Base; end
225
- # class Firm < Company; end
226
- # class Client < Company; end
227
- # class PriorityClient < Client; end
228
- #
229
- # When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
230
- # the companies table with type = "Firm". You can then fetch this row again using
231
- # <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
232
- #
233
- # If you don't have a type column defined in your table, single-table inheritance won't
234
- # be triggered. In that case, it'll work just like normal subclasses with no special magic
235
- # for differentiating between them or reloading the right type with find.
236
- #
237
- # Note, all the attributes for all the cases are kept in the same table. Read more:
238
- # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
223
+ # Active Record allows inheritance by storing the name of the class in a
224
+ # column that is named "type" by default. See ActiveRecord::Inheritance for
225
+ # more details.
239
226
  #
240
227
  # == Connection to multiple databases in different models
241
228
  #
@@ -306,6 +293,8 @@ module ActiveRecord #:nodoc:
306
293
  include Integration
307
294
  include Validations
308
295
  include CounterCache
296
+ include Attributes
297
+ include AttributeDecorators
309
298
  include Locking::Optimistic
310
299
  include Locking::Pessimistic
311
300
  include AttributeMethods
@@ -199,7 +199,7 @@ module ActiveRecord
199
199
  # == Canceling callbacks
200
200
  #
201
201
  # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
202
- # cancelled. If an <tt>after_*</tt> callback returns +false+, all the later callbacks are cancelled.
202
+ # cancelled.
203
203
  # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
204
204
  # methods on the model, which are called last.
205
205
  #
@@ -289,25 +289,25 @@ module ActiveRecord
289
289
  end
290
290
 
291
291
  def destroy #:nodoc:
292
- run_callbacks(:destroy) { super }
292
+ _run_destroy_callbacks { super }
293
293
  end
294
294
 
295
295
  def touch(*) #:nodoc:
296
- run_callbacks(:touch) { super }
296
+ _run_touch_callbacks { super }
297
297
  end
298
298
 
299
299
  private
300
300
 
301
301
  def create_or_update #:nodoc:
302
- run_callbacks(:save) { super }
302
+ _run_save_callbacks { super }
303
303
  end
304
304
 
305
305
  def _create_record #:nodoc:
306
- run_callbacks(:create) { super }
306
+ _run_create_callbacks { super }
307
307
  end
308
308
 
309
309
  def _update_record(*) #:nodoc:
310
- run_callbacks(:update) { super }
310
+ _run_update_callbacks { super }
311
311
  end
312
312
  end
313
313
  end