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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1165 -1591
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +84 -43
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -14
- 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 +87 -30
- data/lib/active_record/associations/collection_proxy.rb +33 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -12
- data/lib/active_record/associations/preloader/association.rb +14 -10
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +37 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +16 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +20 -12
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -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 +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -28
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +57 -95
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +30 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +85 -53
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +139 -57
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +271 -74
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -60
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +295 -141
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +17 -33
- data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -145
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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 +19 -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 +109 -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 -385
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- 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 +134 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -40
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +10 -12
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +62 -74
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- 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 +79 -47
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +18 -8
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +48 -27
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +19 -14
- data/lib/active_record/railties/databases.rake +55 -56
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +281 -117
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +71 -48
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +42 -12
- data/lib/active_record/relation/query_methods.rb +130 -73
- data/lib/active_record/relation/spawn_methods.rb +10 -3
- data/lib/active_record/relation.rb +57 -25
- 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 +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- 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 +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -8
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +54 -28
- 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 +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -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 +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -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 +110 -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 +5 -3
- data/lib/active_record/validations/uniqueness.rb +24 -20
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +5 -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 +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- 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.
|
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
|
-
|
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
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
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
|
-
|
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? ||
|
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.
|
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
|
@@ -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
|
-
#
|
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
|
-
#
|
128
|
+
# super(minutes.to_i * 60)
|
127
129
|
# end
|
128
130
|
#
|
129
131
|
# def length
|
130
|
-
#
|
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
|
-
#
|
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
|
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
|
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.
|
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
|
-
|
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
305
|
def _create_record #:nodoc:
|
306
|
-
|
306
|
+
_run_create_callbacks { super }
|
307
307
|
end
|
308
308
|
|
309
309
|
def _update_record(*) #:nodoc:
|
310
|
-
|
310
|
+
_run_update_callbacks { super }
|
311
311
|
end
|
312
312
|
end
|
313
313
|
end
|