activerecord 4.1.16 → 4.2.0.beta1
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 +634 -2185
- data/README.rdoc +15 -10
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- 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/has_and_belongs_to_many.rb +2 -11
- 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 +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/attribute.rb +131 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- 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 -20
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -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 +76 -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 +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -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/quoting.rb +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- data/lib/active_record/migration.rb +22 -32
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -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/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -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 +51 -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 +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record/validations/uniqueness.rb +9 -23
- 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 +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,18 +1,27 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module AttributeMethods
|
3
3
|
module TimeZoneConversion
|
4
|
-
class
|
5
|
-
def
|
6
|
-
|
4
|
+
class TimeZoneConverter < SimpleDelegator # :nodoc:
|
5
|
+
def type_cast_from_database(value)
|
6
|
+
convert_time_to_time_zone(super)
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
value
|
11
|
-
|
9
|
+
def type_cast_from_user(value)
|
10
|
+
if value.is_a?(Array)
|
11
|
+
value.map { |v| type_cast_from_user(v) }
|
12
|
+
elsif value.respond_to?(:in_time_zone)
|
13
|
+
value.in_time_zone
|
14
|
+
end
|
12
15
|
end
|
13
16
|
|
14
|
-
def
|
15
|
-
|
17
|
+
def convert_time_to_time_zone(value)
|
18
|
+
if value.is_a?(Array)
|
19
|
+
value.map { |v| convert_time_to_time_zone(v) }
|
20
|
+
elsif value.acts_like?(:time)
|
21
|
+
value.in_time_zone
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end
|
16
25
|
end
|
17
26
|
end
|
18
27
|
|
@@ -27,43 +36,26 @@ module ActiveRecord
|
|
27
36
|
end
|
28
37
|
|
29
38
|
module ClassMethods
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
EOV
|
44
|
-
generated_attribute_methods.module_eval(method_body, __FILE__, line)
|
45
|
-
else
|
46
|
-
super
|
39
|
+
private
|
40
|
+
|
41
|
+
def inherited(subclass)
|
42
|
+
# We need to apply this decorator here, rather than on module inclusion. The closure
|
43
|
+
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
44
|
+
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
|
45
|
+
# `skip_time_zone_conversion_for_attributes` would not be picked up.
|
46
|
+
subclass.class_eval do
|
47
|
+
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
|
48
|
+
decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
|
49
|
+
TimeZoneConverter.new(type)
|
50
|
+
end
|
47
51
|
end
|
52
|
+
super
|
48
53
|
end
|
49
54
|
|
50
|
-
|
51
|
-
def create_time_zone_conversion_attribute?(name, column)
|
55
|
+
def create_time_zone_conversion_attribute?(name, cast_type)
|
52
56
|
time_zone_aware_attributes &&
|
53
57
|
!self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
|
54
|
-
(:datetime ==
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def convert_value_to_time_zone(attr_name, value)
|
61
|
-
if value.is_a?(Array)
|
62
|
-
value.map { |v| convert_value_to_time_zone(attr_name, v) }
|
63
|
-
elsif value.respond_to?(:in_time_zone)
|
64
|
-
value.in_time_zone || self.class.columns_hash[attr_name].type_cast(value)
|
65
|
-
else
|
66
|
-
nil
|
58
|
+
(:datetime == cast_type.type)
|
67
59
|
end
|
68
60
|
end
|
69
61
|
end
|
@@ -26,8 +26,6 @@ module ActiveRecord
|
|
26
26
|
protected
|
27
27
|
|
28
28
|
if Module.methods_transplantable?
|
29
|
-
# See define_method_attribute in read.rb for an explanation of
|
30
|
-
# this code.
|
31
29
|
def define_method_attribute=(name)
|
32
30
|
method = WriterMethodCache[name]
|
33
31
|
generated_attribute_methods.module_eval {
|
@@ -55,11 +53,11 @@ module ActiveRecord
|
|
55
53
|
# specified +value+. Empty strings for fixnum and float columns are
|
56
54
|
# turned into +nil+.
|
57
55
|
def write_attribute(attr_name, value)
|
58
|
-
write_attribute_with_type_cast(attr_name, value,
|
56
|
+
write_attribute_with_type_cast(attr_name, value, true)
|
59
57
|
end
|
60
58
|
|
61
59
|
def raw_write_attribute(attr_name, value)
|
62
|
-
write_attribute_with_type_cast(attr_name, value,
|
60
|
+
write_attribute_with_type_cast(attr_name, value, false)
|
63
61
|
end
|
64
62
|
|
65
63
|
private
|
@@ -68,30 +66,17 @@ module ActiveRecord
|
|
68
66
|
write_attribute(attribute_name, value)
|
69
67
|
end
|
70
68
|
|
71
|
-
def
|
72
|
-
return value unless column
|
73
|
-
|
74
|
-
column.type_cast_for_write value
|
75
|
-
end
|
76
|
-
alias_method :raw_type_cast_attribute_for_write, :type_cast_attribute_for_write
|
77
|
-
|
78
|
-
def write_attribute_with_type_cast(attr_name, value, type_cast_method)
|
69
|
+
def write_attribute_with_type_cast(attr_name, value, should_type_cast)
|
79
70
|
attr_name = attr_name.to_s
|
80
71
|
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
|
81
|
-
@attributes_cache.delete(attr_name)
|
82
|
-
column = column_for_attribute(attr_name)
|
83
72
|
|
84
|
-
|
85
|
-
|
86
|
-
if column && column.binary?
|
87
|
-
@attributes_cache[attr_name] = value
|
88
|
-
end
|
89
|
-
|
90
|
-
if column || @attributes.has_key?(attr_name)
|
91
|
-
@attributes[attr_name] = send(type_cast_method, column, value)
|
73
|
+
if should_type_cast
|
74
|
+
@attributes.write_from_user(attr_name, value)
|
92
75
|
else
|
93
|
-
|
76
|
+
@attributes.write_from_database(attr_name, value)
|
94
77
|
end
|
78
|
+
|
79
|
+
value
|
95
80
|
end
|
96
81
|
end
|
97
82
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'active_record/attribute_set/builder'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class AttributeSet # :nodoc:
|
5
|
+
delegate :keys, to: :initialized_attributes
|
6
|
+
|
7
|
+
def initialize(attributes)
|
8
|
+
@attributes = attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](name)
|
12
|
+
attributes[name] || Attribute.null(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def values_before_type_cast
|
16
|
+
attributes.transform_values(&:value_before_type_cast)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
initialized_attributes.transform_values(&:value)
|
21
|
+
end
|
22
|
+
alias_method :to_h, :to_hash
|
23
|
+
|
24
|
+
def key?(name)
|
25
|
+
attributes.key?(name) && self[name].initialized?
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_value(name, &block)
|
29
|
+
self[name].value(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_from_database(name, value)
|
33
|
+
attributes[name] = self[name].with_value_from_database(value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_from_user(name, value)
|
37
|
+
attributes[name] = self[name].with_value_from_user(value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def freeze
|
41
|
+
@attributes.freeze
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize_dup(_)
|
46
|
+
@attributes = attributes.transform_values(&:dup)
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize_clone(_)
|
51
|
+
@attributes = attributes.clone
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
def reset(key)
|
56
|
+
if key?(key)
|
57
|
+
write_from_database(key, nil)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def ensure_initialized(key)
|
62
|
+
unless self[key].initialized?
|
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,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class AttributeSet # :nodoc:
|
3
|
+
class Builder # :nodoc:
|
4
|
+
attr_reader :types
|
5
|
+
|
6
|
+
def initialize(types)
|
7
|
+
@types = types
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_from_database(values = {}, additional_types = {})
|
11
|
+
attributes = build_attributes_from_values(values, additional_types)
|
12
|
+
add_uninitialized_attributes(attributes)
|
13
|
+
AttributeSet.new(attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_attributes_from_values(values, additional_types)
|
19
|
+
values.each_with_object({}) do |(name, value), hash|
|
20
|
+
type = additional_types.fetch(name, types[name])
|
21
|
+
hash[name] = Attribute.from_database(name, value, type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_uninitialized_attributes(attributes)
|
26
|
+
types.except(*attributes.keys).each do |name, type|
|
27
|
+
attributes[name] = Attribute.uninitialized(name, type)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,122 @@
|
|
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
|
+
self.user_provided_columns = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods # :nodoc:
|
13
|
+
# Defines or overrides a attribute on this model. This allows customization of
|
14
|
+
# Active Record's type casting behavior, as well as adding support for user defined
|
15
|
+
# types.
|
16
|
+
#
|
17
|
+
# +name+ The name of the methods to define attribute methods for, and the column which
|
18
|
+
# this will persist to.
|
19
|
+
#
|
20
|
+
# +cast_type+ A type object that contains information about how to type cast the value.
|
21
|
+
# See the examples section for more information.
|
22
|
+
#
|
23
|
+
# ==== Options
|
24
|
+
# The options hash accepts the following options:
|
25
|
+
#
|
26
|
+
# +default+ is the default value that the column should use on a new record.
|
27
|
+
#
|
28
|
+
# ==== Examples
|
29
|
+
#
|
30
|
+
# The type detected by Active Record can be overridden.
|
31
|
+
#
|
32
|
+
# # db/schema.rb
|
33
|
+
# create_table :store_listings, force: true do |t|
|
34
|
+
# t.decimal :price_in_cents
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# # app/models/store_listing.rb
|
38
|
+
# class StoreListing < ActiveRecord::Base
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# store_listing = StoreListing.new(price_in_cents: '10.1')
|
42
|
+
#
|
43
|
+
# # before
|
44
|
+
# store_listing.price_in_cents # => BigDecimal.new(10.1)
|
45
|
+
#
|
46
|
+
# class StoreListing < ActiveRecord::Base
|
47
|
+
# attribute :price_in_cents, Type::Integer.new
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# # after
|
51
|
+
# store_listing.price_in_cents # => 10
|
52
|
+
#
|
53
|
+
# Users may also define their own custom types, as long as they respond to the methods
|
54
|
+
# defined on the value type. The `type_cast` method on your type object will be called
|
55
|
+
# with values both from the database, and from your controllers. See
|
56
|
+
# `ActiveRecord::Attributes::Type::Value` for the expected API. It is recommended that your
|
57
|
+
# type objects inherit from an existing type, or the base value type.
|
58
|
+
#
|
59
|
+
# class MoneyType < ActiveRecord::Type::Integer
|
60
|
+
# def type_cast(value)
|
61
|
+
# if value.include?('$')
|
62
|
+
# price_in_dollars = value.gsub(/\$/, '').to_f
|
63
|
+
# price_in_dollars * 100
|
64
|
+
# else
|
65
|
+
# value.to_i
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# class StoreListing < ActiveRecord::Base
|
71
|
+
# attribute :price_in_cents, MoneyType.new
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# store_listing = StoreListing.new(price_in_cents: '$10.00')
|
75
|
+
# store_listing.price_in_cents # => 1000
|
76
|
+
def attribute(name, cast_type, options = {})
|
77
|
+
name = name.to_s
|
78
|
+
clear_caches_calculated_from_columns
|
79
|
+
# Assign a new hash to ensure that subclasses do not share a hash
|
80
|
+
self.user_provided_columns = user_provided_columns.merge(name => connection.new_column(name, options[:default], cast_type))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns an array of column objects for the table associated with this class.
|
84
|
+
def columns
|
85
|
+
@columns ||= add_user_provided_columns(connection.schema_cache.columns(table_name))
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a hash of column objects for the table associated with this class.
|
89
|
+
def columns_hash
|
90
|
+
@columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
|
91
|
+
end
|
92
|
+
|
93
|
+
def reset_column_information # :nodoc:
|
94
|
+
super
|
95
|
+
clear_caches_calculated_from_columns
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def add_user_provided_columns(schema_columns)
|
101
|
+
existing_columns = schema_columns.map do |column|
|
102
|
+
user_provided_columns[column.name] || column
|
103
|
+
end
|
104
|
+
|
105
|
+
existing_column_names = existing_columns.map(&:name)
|
106
|
+
new_columns = user_provided_columns.except(*existing_column_names).values
|
107
|
+
|
108
|
+
existing_columns + new_columns
|
109
|
+
end
|
110
|
+
|
111
|
+
def clear_caches_calculated_from_columns
|
112
|
+
@attributes_builder = nil
|
113
|
+
@column_names = nil
|
114
|
+
@column_types = nil
|
115
|
+
@columns = nil
|
116
|
+
@columns_hash = nil
|
117
|
+
@content_columns = nil
|
118
|
+
@default_attributes = nil
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -177,15 +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?
|
180
182
|
|
181
|
-
if
|
183
|
+
if collection
|
182
184
|
before_save :before_save_collection_association
|
183
185
|
|
184
186
|
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
185
|
-
|
186
|
-
|
187
|
-
after_update save_method
|
188
|
-
elsif reflection.macro == :has_one
|
187
|
+
after_save save_method
|
188
|
+
elsif reflection.has_one?
|
189
189
|
define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
|
190
190
|
# Configures two callbacks instead of a single after_save so that
|
191
191
|
# the model may rely on their execution order relative to its
|
@@ -202,22 +202,9 @@ module ActiveRecord
|
|
202
202
|
before_save save_method
|
203
203
|
end
|
204
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}"
|
210
205
|
if reflection.validate? && !method_defined?(validation_method)
|
211
|
-
|
212
|
-
|
213
|
-
else
|
214
|
-
method = :validate_single_association
|
215
|
-
end
|
216
|
-
|
217
|
-
define_non_cyclic_method(validation_method) do
|
218
|
-
send(method, reflection)
|
219
|
-
true
|
220
|
-
end
|
206
|
+
method = (collection ? :validate_collection_association : :validate_single_association)
|
207
|
+
define_non_cyclic_method(validation_method) { send(method, reflection) }
|
221
208
|
validate validation_method
|
222
209
|
end
|
223
210
|
end
|
@@ -316,7 +303,8 @@ module ActiveRecord
|
|
316
303
|
def association_valid?(reflection, record)
|
317
304
|
return true if record.destroyed? || record.marked_for_destruction?
|
318
305
|
|
319
|
-
|
306
|
+
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
|
307
|
+
unless valid = record.valid?(validation_context)
|
320
308
|
if reflection.options[:autosave]
|
321
309
|
record.errors.each do |attribute, message|
|
322
310
|
attribute = "#{reflection.name}.#{attribute}"
|
@@ -350,6 +338,7 @@ module ActiveRecord
|
|
350
338
|
autosave = reflection.options[:autosave]
|
351
339
|
|
352
340
|
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
|
341
|
+
|
353
342
|
if autosave
|
354
343
|
records_to_destroy = records.select(&:marked_for_destruction?)
|
355
344
|
records_to_destroy.each { |record| association.destroy(record) }
|
@@ -373,6 +362,7 @@ module ActiveRecord
|
|
373
362
|
|
374
363
|
raise ActiveRecord::Rollback unless saved
|
375
364
|
end
|
365
|
+
@new_record_before_save = false
|
376
366
|
end
|
377
367
|
|
378
368
|
# reconstruct the scope now that we know the owner's id
|