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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- 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.
|
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
|
111
|
-
# post.comments.
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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.
|
274
|
-
|
275
|
-
|
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
|
-
|
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
|
-
|
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? ||
|
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.
|
data/lib/active_record/base.rb
CHANGED
@@ -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
|
221
|
-
#
|
222
|
-
#
|
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
|
-
|
292
|
+
_run_destroy_callbacks { super }
|
293
293
|
end
|
294
294
|
|
295
295
|
def touch(*) #:nodoc:
|
296
|
-
|
296
|
+
_run_touch_callbacks { super }
|
297
297
|
end
|
298
298
|
|
299
299
|
private
|
300
300
|
|
301
301
|
def create_or_update #:nodoc:
|
302
|
-
|
302
|
+
_run_save_callbacks { super }
|
303
303
|
end
|
304
304
|
|
305
|
-
def
|
306
|
-
|
305
|
+
def _create_record #:nodoc:
|
306
|
+
_run_create_callbacks { super }
|
307
307
|
end
|
308
308
|
|
309
|
-
def
|
310
|
-
|
309
|
+
def _update_record(*) #:nodoc:
|
310
|
+
_run_update_callbacks { super }
|
311
311
|
end
|
312
312
|
end
|
313
313
|
end
|