activerecord 4.1.0 → 4.2.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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +776 -1330
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/association_relation.rb +4 -0
  6. data/lib/active_record/associations/alias_tracker.rb +14 -13
  7. data/lib/active_record/associations/association.rb +2 -2
  8. data/lib/active_record/associations/association_scope.rb +83 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
  13. data/lib/active_record/associations/builder/has_many.rb +1 -1
  14. data/lib/active_record/associations/builder/has_one.rb +2 -2
  15. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  16. data/lib/active_record/associations/collection_association.rb +66 -29
  17. data/lib/active_record/associations/collection_proxy.rb +22 -26
  18. data/lib/active_record/associations/has_many_association.rb +65 -18
  19. data/lib/active_record/associations/has_many_through_association.rb +55 -27
  20. data/lib/active_record/associations/has_one_association.rb +0 -1
  21. data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
  22. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  23. data/lib/active_record/associations/join_dependency.rb +20 -12
  24. data/lib/active_record/associations/preloader/association.rb +34 -11
  25. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  26. data/lib/active_record/associations/preloader.rb +49 -59
  27. data/lib/active_record/associations/singular_association.rb +25 -4
  28. data/lib/active_record/associations/through_association.rb +23 -14
  29. data/lib/active_record/associations.rb +171 -42
  30. data/lib/active_record/attribute.rb +149 -0
  31. data/lib/active_record/attribute_assignment.rb +18 -10
  32. data/lib/active_record/attribute_decorators.rb +66 -0
  33. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  34. data/lib/active_record/attribute_methods/dirty.rb +98 -44
  35. data/lib/active_record/attribute_methods/primary_key.rb +14 -8
  36. data/lib/active_record/attribute_methods/query.rb +1 -1
  37. data/lib/active_record/attribute_methods/read.rb +22 -59
  38. data/lib/active_record/attribute_methods/serialization.rb +37 -147
  39. data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
  40. data/lib/active_record/attribute_methods/write.rb +14 -21
  41. data/lib/active_record/attribute_methods.rb +67 -94
  42. data/lib/active_record/attribute_set/builder.rb +86 -0
  43. data/lib/active_record/attribute_set.rb +77 -0
  44. data/lib/active_record/attributes.rb +139 -0
  45. data/lib/active_record/autosave_association.rb +45 -38
  46. data/lib/active_record/base.rb +10 -20
  47. data/lib/active_record/callbacks.rb +7 -7
  48. data/lib/active_record/coders/json.rb +13 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
  60. data/lib/active_record/connection_adapters/column.rb +28 -239
  61. data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
  62. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
  63. data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
  64. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  65. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  66. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
  67. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  96. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
  97. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  98. data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
  99. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  100. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
  101. data/lib/active_record/connection_handling.rb +3 -3
  102. data/lib/active_record/core.rb +143 -32
  103. data/lib/active_record/counter_cache.rb +60 -7
  104. data/lib/active_record/enum.rb +10 -11
  105. data/lib/active_record/errors.rb +49 -27
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixtures.rb +56 -70
  108. data/lib/active_record/gem_version.rb +2 -2
  109. data/lib/active_record/inheritance.rb +35 -10
  110. data/lib/active_record/integration.rb +4 -4
  111. data/lib/active_record/locking/optimistic.rb +35 -17
  112. data/lib/active_record/log_subscriber.rb +1 -1
  113. data/lib/active_record/migration/command_recorder.rb +19 -2
  114. data/lib/active_record/migration/join_table.rb +1 -1
  115. data/lib/active_record/migration.rb +52 -49
  116. data/lib/active_record/model_schema.rb +49 -57
  117. data/lib/active_record/nested_attributes.rb +7 -7
  118. data/lib/active_record/null_relation.rb +19 -5
  119. data/lib/active_record/persistence.rb +50 -31
  120. data/lib/active_record/query_cache.rb +3 -3
  121. data/lib/active_record/querying.rb +10 -7
  122. data/lib/active_record/railtie.rb +14 -11
  123. data/lib/active_record/railties/databases.rake +56 -54
  124. data/lib/active_record/readonly_attributes.rb +0 -1
  125. data/lib/active_record/reflection.rb +286 -102
  126. data/lib/active_record/relation/batches.rb +0 -1
  127. data/lib/active_record/relation/calculations.rb +39 -31
  128. data/lib/active_record/relation/delegation.rb +2 -2
  129. data/lib/active_record/relation/finder_methods.rb +80 -36
  130. data/lib/active_record/relation/merger.rb +25 -30
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
  132. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  133. data/lib/active_record/relation/predicate_builder.rb +11 -10
  134. data/lib/active_record/relation/query_methods.rb +141 -55
  135. data/lib/active_record/relation/spawn_methods.rb +3 -0
  136. data/lib/active_record/relation.rb +69 -30
  137. data/lib/active_record/result.rb +18 -7
  138. data/lib/active_record/sanitization.rb +12 -2
  139. data/lib/active_record/schema.rb +0 -1
  140. data/lib/active_record/schema_dumper.rb +58 -26
  141. data/lib/active_record/schema_migration.rb +11 -0
  142. data/lib/active_record/scoping/default.rb +8 -7
  143. data/lib/active_record/scoping/named.rb +4 -0
  144. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  145. data/lib/active_record/statement_cache.rb +95 -10
  146. data/lib/active_record/store.rb +19 -10
  147. data/lib/active_record/tasks/database_tasks.rb +73 -7
  148. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
  149. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  151. data/lib/active_record/timestamp.rb +11 -9
  152. data/lib/active_record/transactions.rb +37 -21
  153. data/lib/active_record/type/big_integer.rb +13 -0
  154. data/lib/active_record/type/binary.rb +50 -0
  155. data/lib/active_record/type/boolean.rb +30 -0
  156. data/lib/active_record/type/date.rb +46 -0
  157. data/lib/active_record/type/date_time.rb +43 -0
  158. data/lib/active_record/type/decimal.rb +40 -0
  159. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  160. data/lib/active_record/type/decorator.rb +14 -0
  161. data/lib/active_record/type/float.rb +19 -0
  162. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  163. data/lib/active_record/type/integer.rb +55 -0
  164. data/lib/active_record/type/mutable.rb +16 -0
  165. data/lib/active_record/type/numeric.rb +36 -0
  166. data/lib/active_record/type/serialized.rb +56 -0
  167. data/lib/active_record/type/string.rb +36 -0
  168. data/lib/active_record/type/text.rb +11 -0
  169. data/lib/active_record/type/time.rb +26 -0
  170. data/lib/active_record/type/time_value.rb +38 -0
  171. data/lib/active_record/type/type_map.rb +64 -0
  172. data/lib/active_record/type/unsigned_integer.rb +15 -0
  173. data/lib/active_record/type/value.rb +101 -0
  174. data/lib/active_record/type.rb +23 -0
  175. data/lib/active_record/validations/associated.rb +5 -3
  176. data/lib/active_record/validations/presence.rb +6 -4
  177. data/lib/active_record/validations/uniqueness.rb +11 -17
  178. data/lib/active_record/validations.rb +25 -19
  179. data/lib/active_record.rb +3 -0
  180. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  181. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
  182. data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +65 -10
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -0,0 +1,86 @@
1
+ module ActiveRecord
2
+ class AttributeSet # :nodoc:
3
+ class Builder # :nodoc:
4
+ attr_reader :types, :always_initialized
5
+
6
+ def initialize(types, always_initialized = nil)
7
+ @types = types
8
+ @always_initialized = always_initialized
9
+ end
10
+
11
+ def build_from_database(values = {}, additional_types = {})
12
+ if always_initialized && !values.key?(always_initialized)
13
+ values[always_initialized] = nil
14
+ end
15
+
16
+ attributes = LazyAttributeHash.new(types, values, additional_types)
17
+ AttributeSet.new(attributes)
18
+ end
19
+ end
20
+ end
21
+
22
+ class LazyAttributeHash # :nodoc:
23
+ delegate :select, :transform_values, to: :materialize
24
+
25
+ def initialize(types, values, additional_types)
26
+ @types = types
27
+ @values = values
28
+ @additional_types = additional_types
29
+ @materialized = false
30
+ @delegate_hash = {}
31
+ end
32
+
33
+ def key?(key)
34
+ delegate_hash.key?(key) || values.key?(key) || types.key?(key)
35
+ end
36
+
37
+ def [](key)
38
+ delegate_hash[key] || assign_default_value(key)
39
+ end
40
+
41
+ def []=(key, value)
42
+ if frozen?
43
+ raise RuntimeError, "Can't modify frozen hash"
44
+ end
45
+ delegate_hash[key] = value
46
+ end
47
+
48
+ def initialized_keys
49
+ delegate_hash.keys | values.keys
50
+ end
51
+
52
+ def initialize_dup(_)
53
+ @delegate_hash = delegate_hash.transform_values(&:dup)
54
+ super
55
+ end
56
+
57
+ protected
58
+
59
+ attr_reader :types, :values, :additional_types, :delegate_hash
60
+
61
+ private
62
+
63
+ def assign_default_value(name)
64
+ type = additional_types.fetch(name, types[name])
65
+ value_present = true
66
+ value = values.fetch(name) { value_present = false }
67
+
68
+ if value_present
69
+ delegate_hash[name] = Attribute.from_database(name, value, type)
70
+ elsif types.key?(name)
71
+ delegate_hash[name] = Attribute.uninitialized(name, type)
72
+ end
73
+ end
74
+
75
+ def materialize
76
+ unless @materialized
77
+ values.each_key { |key| self[key] }
78
+ types.each_key { |key| self[key] }
79
+ unless frozen?
80
+ @materialized = true
81
+ end
82
+ end
83
+ delegate_hash
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,77 @@
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
+ protected
68
+
69
+ attr_reader :attributes
70
+
71
+ private
72
+
73
+ def initialized_attributes
74
+ attributes.select { |_, attr| attr.initialized? }
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,139 @@
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
+ end
13
+
14
+ module ClassMethods # :nodoc:
15
+ # Defines or overrides a attribute on this model. This allows customization of
16
+ # Active Record's type casting behavior, as well as adding support for user defined
17
+ # types.
18
+ #
19
+ # +name+ The name of the methods to define attribute methods for, and the column which
20
+ # this will persist to.
21
+ #
22
+ # +cast_type+ A type object that contains information about how to type cast the value.
23
+ # See the examples section for more information.
24
+ #
25
+ # ==== Options
26
+ # The options hash accepts the following options:
27
+ #
28
+ # +default+ is the default value that the column should use on a new record.
29
+ #
30
+ # ==== Examples
31
+ #
32
+ # The type detected by Active Record can be overridden.
33
+ #
34
+ # # db/schema.rb
35
+ # create_table :store_listings, force: true do |t|
36
+ # t.decimal :price_in_cents
37
+ # end
38
+ #
39
+ # # app/models/store_listing.rb
40
+ # class StoreListing < ActiveRecord::Base
41
+ # end
42
+ #
43
+ # store_listing = StoreListing.new(price_in_cents: '10.1')
44
+ #
45
+ # # before
46
+ # store_listing.price_in_cents # => BigDecimal.new(10.1)
47
+ #
48
+ # class StoreListing < ActiveRecord::Base
49
+ # attribute :price_in_cents, Type::Integer.new
50
+ # end
51
+ #
52
+ # # after
53
+ # store_listing.price_in_cents # => 10
54
+ #
55
+ # Users may also define their own custom types, as long as they respond to the methods
56
+ # defined on the value type. The `type_cast` method on your type object will be called
57
+ # with values both from the database, and from your controllers. See
58
+ # `ActiveRecord::Attributes::Type::Value` for the expected API. It is recommended that your
59
+ # type objects inherit from an existing type, or the base value type.
60
+ #
61
+ # class MoneyType < ActiveRecord::Type::Integer
62
+ # def type_cast(value)
63
+ # if value.include?('$')
64
+ # price_in_dollars = value.gsub(/\$/, '').to_f
65
+ # price_in_dollars * 100
66
+ # else
67
+ # value.to_i
68
+ # end
69
+ # end
70
+ # end
71
+ #
72
+ # class StoreListing < ActiveRecord::Base
73
+ # attribute :price_in_cents, MoneyType.new
74
+ # end
75
+ #
76
+ # store_listing = StoreListing.new(price_in_cents: '$10.00')
77
+ # store_listing.price_in_cents # => 1000
78
+ def attribute(name, cast_type, options = {})
79
+ name = name.to_s
80
+ clear_caches_calculated_from_columns
81
+ # Assign a new hash to ensure that subclasses do not share a hash
82
+ self.user_provided_columns = user_provided_columns.merge(name => cast_type)
83
+
84
+ if options.key?(:default)
85
+ self.user_provided_defaults = user_provided_defaults.merge(name => options[:default])
86
+ end
87
+ end
88
+
89
+ # Returns an array of column objects for the table associated with this class.
90
+ def columns
91
+ @columns ||= add_user_provided_columns(connection.schema_cache.columns(table_name))
92
+ end
93
+
94
+ # Returns a hash of column objects for the table associated with this class.
95
+ def columns_hash
96
+ @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
97
+ end
98
+
99
+ def reset_column_information # :nodoc:
100
+ super
101
+ clear_caches_calculated_from_columns
102
+ end
103
+
104
+ private
105
+
106
+ def add_user_provided_columns(schema_columns)
107
+ existing_columns = schema_columns.map do |column|
108
+ new_type = user_provided_columns[column.name]
109
+ if new_type
110
+ column.with_type(new_type)
111
+ else
112
+ column
113
+ end
114
+ end
115
+
116
+ existing_column_names = existing_columns.map(&:name)
117
+ new_columns = user_provided_columns.except(*existing_column_names).map do |(name, type)|
118
+ connection.new_column(name, nil, type)
119
+ end
120
+
121
+ existing_columns + new_columns
122
+ end
123
+
124
+ def clear_caches_calculated_from_columns
125
+ @attributes_builder = nil
126
+ @column_names = nil
127
+ @column_types = nil
128
+ @columns = nil
129
+ @columns_hash = nil
130
+ @content_columns = nil
131
+ @default_attributes = nil
132
+ end
133
+
134
+ def raw_default_values
135
+ super.merge(user_provided_defaults)
136
+ end
137
+ end
138
+ end
139
+ end
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
  #
36
36
  # === One-to-one Example
37
37
  #
38
- # class Post
38
+ # class Post < ActiveRecord::Base
39
39
  # has_one :author, autosave: true
40
40
  # end
41
41
  #
@@ -76,7 +76,7 @@ module ActiveRecord
76
76
  #
77
77
  # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
78
78
  #
79
- # class Post
79
+ # class Post < ActiveRecord::Base
80
80
  # has_many :comments # :autosave option is not declared
81
81
  # end
82
82
  #
@@ -95,20 +95,23 @@ module ActiveRecord
95
95
  # When <tt>:autosave</tt> is true all children are saved, no matter whether they
96
96
  # are new records or not:
97
97
  #
98
- # class Post
98
+ # class Post < ActiveRecord::Base
99
99
  # has_many :comments, autosave: true
100
100
  # end
101
101
  #
102
102
  # post = Post.create(title: 'ruby rocks')
103
103
  # post.comments.create(body: 'hello world')
104
104
  # post.comments[0].body = 'hi everyone'
105
- # post.save # => saves both post and comment, with 'hi everyone' as body
105
+ # post.comments.build(body: "good morning.")
106
+ # post.title += "!"
107
+ # post.save # => saves both post and comments.
106
108
  #
107
109
  # Destroying one of the associated models as part of the parent's save action
108
110
  # is as simple as marking it for destruction:
109
111
  #
110
- # post.comments.last.mark_for_destruction
111
- # post.comments.last.marked_for_destruction? # => true
112
+ # post.comments # => [#<Comment id: 1, ...>, #<Comment id: 2, ...]>
113
+ # post.comments[1].mark_for_destruction
114
+ # post.comments[1].marked_for_destruction? # => true
112
115
  # post.comments.length # => 2
113
116
  #
114
117
  # Note that the model is _not_ yet removed from the database:
@@ -144,6 +147,7 @@ module ActiveRecord
144
147
  private
145
148
 
146
149
  def define_non_cyclic_method(name, &block)
150
+ return if method_defined?(name)
147
151
  define_method(name) do |*args|
148
152
  result = true; @_already_called ||= {}
149
153
  # Loop prevention for validation of associations
@@ -176,30 +180,28 @@ module ActiveRecord
176
180
  validation_method = :"validate_associated_records_for_#{reflection.name}"
177
181
  collection = reflection.collection?
178
182
 
179
- unless method_defined?(save_method)
180
- if collection
181
- before_save :before_save_collection_association
182
-
183
- define_non_cyclic_method(save_method) { save_collection_association(reflection) }
184
- # Doesn't use after_save as that would save associations added in after_create/after_update twice
185
- after_create save_method
186
- after_update save_method
187
- elsif reflection.macro == :has_one
188
- define_method(save_method) { save_has_one_association(reflection) }
189
- # Configures two callbacks instead of a single after_save so that
190
- # the model may rely on their execution order relative to its
191
- # own callbacks.
192
- #
193
- # For example, given that after_creates run before after_saves, if
194
- # we configured instead an after_save there would be no way to fire
195
- # a custom after_create callback after the child association gets
196
- # created.
197
- after_create save_method
198
- after_update save_method
199
- else
200
- define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
201
- before_save save_method
202
- end
183
+ if collection
184
+ before_save :before_save_collection_association
185
+
186
+ define_non_cyclic_method(save_method) { save_collection_association(reflection) }
187
+ # Doesn't use after_save as that would save associations added in after_create/after_update twice
188
+ after_create save_method
189
+ after_update save_method
190
+ elsif reflection.has_one?
191
+ define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
192
+ # Configures two callbacks instead of a single after_save so that
193
+ # the model may rely on their execution order relative to its
194
+ # own callbacks.
195
+ #
196
+ # For example, given that after_creates run before after_saves, if
197
+ # we configured instead an after_save there would be no way to fire
198
+ # a custom after_create callback after the child association gets
199
+ # created.
200
+ after_create save_method
201
+ after_update save_method
202
+ else
203
+ define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
204
+ before_save save_method
203
205
  end
204
206
 
205
207
  if reflection.validate? && !method_defined?(validation_method)
@@ -270,9 +272,11 @@ module ActiveRecord
270
272
  # go through nested autosave associations that are loaded in memory (without loading
271
273
  # any new ones), and return true if is changed for autosave
272
274
  def nested_records_changed_for_autosave?
273
- self.class.reflect_on_all_autosave_associations.any? do |reflection|
274
- association = association_instance_get(reflection.name)
275
- association && Array.wrap(association.target).any? { |a| a.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? }
279
+ end
276
280
  end
277
281
  end
278
282
 
@@ -301,7 +305,8 @@ module ActiveRecord
301
305
  def association_valid?(reflection, record)
302
306
  return true if record.destroyed? || record.marked_for_destruction?
303
307
 
304
- unless valid = record.valid?
308
+ validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
309
+ unless valid = record.valid?(validation_context)
305
310
  if reflection.options[:autosave]
306
311
  record.errors.each do |attribute, message|
307
312
  attribute = "#{reflection.name}.#{attribute}"
@@ -335,7 +340,6 @@ module ActiveRecord
335
340
  autosave = reflection.options[:autosave]
336
341
 
337
342
  if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
338
-
339
343
  if autosave
340
344
  records_to_destroy = records.select(&:marked_for_destruction?)
341
345
  records_to_destroy.each { |record| association.destroy(record) }
@@ -377,15 +381,16 @@ module ActiveRecord
377
381
  def save_has_one_association(reflection)
378
382
  association = association_instance_get(reflection.name)
379
383
  record = association && association.load_target
384
+
380
385
  if record && !record.destroyed?
381
386
  autosave = reflection.options[:autosave]
382
387
 
383
388
  if autosave && record.marked_for_destruction?
384
389
  record.destroy
385
- else
390
+ elsif autosave != false
386
391
  key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
387
- if autosave != false && (autosave || new_record? || record_changed?(reflection, record, key))
388
392
 
393
+ if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
389
394
  unless reflection.through_reflection
390
395
  record[reflection.foreign_key] = key
391
396
  end
@@ -400,7 +405,9 @@ module ActiveRecord
400
405
 
401
406
  # If the record is new or it has changed, returns true.
402
407
  def record_changed?(reflection, record, key)
403
- record.new_record? || record[reflection.foreign_key] != key || record.attribute_changed?(reflection.foreign_key)
408
+ record.new_record? ||
409
+ (record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
410
+ record.attribute_changed?(reflection.foreign_key)
404
411
  end
405
412
 
406
413
  # 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
@@ -138,6 +141,7 @@ module ActiveRecord #:nodoc:
138
141
  #
139
142
  # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
140
143
  # Query methods allow you to test whether an attribute value is present.
144
+ # For numeric values, present is defined as non-zero.
141
145
  #
142
146
  # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
143
147
  # to determine whether the user has a name:
@@ -217,25 +221,9 @@ module ActiveRecord #:nodoc:
217
221
  #
218
222
  # == Single table inheritance
219
223
  #
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
224
+ # Active Record allows inheritance by storing the name of the class in a
225
+ # column that is named "type" by default. See ActiveRecord::Inheritance for
226
+ # more details.
239
227
  #
240
228
  # == Connection to multiple databases in different models
241
229
  #
@@ -296,7 +284,6 @@ module ActiveRecord #:nodoc:
296
284
 
297
285
  include Core
298
286
  include Persistence
299
- include NoTouching
300
287
  include ReadonlyAttributes
301
288
  include ModelSchema
302
289
  include Inheritance
@@ -307,6 +294,8 @@ module ActiveRecord #:nodoc:
307
294
  include Integration
308
295
  include Validations
309
296
  include CounterCache
297
+ include Attributes
298
+ include AttributeDecorators
310
299
  include Locking::Optimistic
311
300
  include Locking::Pessimistic
312
301
  include AttributeMethods
@@ -318,6 +307,7 @@ module ActiveRecord #:nodoc:
318
307
  include NestedAttributes
319
308
  include Aggregations
320
309
  include Transactions
310
+ include NoTouching
321
311
  include Reflection
322
312
  include Serialization
323
313
  include Store
@@ -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
- def create_record #:nodoc:
306
- run_callbacks(:create) { super }
305
+ def _create_record #:nodoc:
306
+ _run_create_callbacks { super }
307
307
  end
308
308
 
309
- def update_record(*) #:nodoc:
310
- run_callbacks(:update) { super }
309
+ def _update_record(*) #:nodoc:
310
+ _run_update_callbacks { super }
311
311
  end
312
312
  end
313
313
  end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module Coders # :nodoc:
3
+ class JSON # :nodoc:
4
+ def self.dump(obj)
5
+ ActiveSupport::JSON.encode(obj)
6
+ end
7
+
8
+ def self.load(json)
9
+ ActiveSupport::JSON.decode(json) unless json.nil?
10
+ end
11
+ end
12
+ end
13
+ end