composite_primary_keys 3.1.11 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -8
- data/lib/composite_primary_keys.rb +53 -36
- data/lib/composite_primary_keys/associations/association.rb +23 -0
- data/lib/composite_primary_keys/associations/association_scope.rb +67 -0
- data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +31 -121
- data/lib/composite_primary_keys/associations/has_many_association.rb +27 -66
- data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +22 -0
- data/lib/composite_primary_keys/associations/join_dependency/join_part.rb +39 -0
- data/lib/composite_primary_keys/associations/preloader/association.rb +61 -0
- data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +13 -0
- data/lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb +46 -0
- data/lib/composite_primary_keys/attribute_methods/dirty.rb +30 -0
- data/lib/composite_primary_keys/attribute_methods/read.rb +88 -0
- data/lib/composite_primary_keys/attribute_methods/write.rb +33 -0
- data/lib/composite_primary_keys/base.rb +18 -70
- data/lib/composite_primary_keys/composite_predicates.rb +53 -0
- data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +6 -4
- data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +19 -41
- data/lib/composite_primary_keys/fixtures.rb +19 -6
- data/lib/composite_primary_keys/persistence.rb +32 -13
- data/lib/composite_primary_keys/relation.rb +23 -16
- data/lib/composite_primary_keys/relation/calculations.rb +48 -0
- data/lib/composite_primary_keys/relation/finder_methods.rb +117 -0
- data/lib/composite_primary_keys/relation/query_methods.rb +24 -0
- data/lib/composite_primary_keys/validations/uniqueness.rb +19 -23
- data/lib/composite_primary_keys/version.rb +5 -5
- data/test/connections/native_mysql/connection.rb +1 -1
- data/test/fixtures/articles.yml +1 -0
- data/test/fixtures/products.yml +2 -4
- data/test/fixtures/readings.yml +1 -0
- data/test/fixtures/suburbs.yml +1 -4
- data/test/fixtures/users.yml +1 -0
- data/test/test_associations.rb +61 -63
- data/test/test_attributes.rb +16 -21
- data/test/test_create.rb +3 -3
- data/test/test_delete.rb +87 -84
- data/test/{test_clone.rb → test_dup.rb} +8 -5
- data/test/test_exists.rb +22 -10
- data/test/test_habtm.rb +0 -74
- data/test/test_ids.rb +2 -1
- data/test/test_miscellaneous.rb +2 -2
- data/test/test_polymorphic.rb +1 -1
- data/test/test_suite.rb +1 -1
- data/test/test_update.rb +3 -3
- metadata +76 -75
- data/lib/composite_primary_keys/association_preload.rb +0 -158
- data/lib/composite_primary_keys/associations.rb +0 -155
- data/lib/composite_primary_keys/associations/association_proxy.rb +0 -33
- data/lib/composite_primary_keys/associations/has_one_association.rb +0 -27
- data/lib/composite_primary_keys/associations/through_association_scope.rb +0 -103
- data/lib/composite_primary_keys/attribute_methods.rb +0 -84
- data/lib/composite_primary_keys/calculations.rb +0 -31
- data/lib/composite_primary_keys/connection_adapters/ibm_db_adapter.rb +0 -21
- data/lib/composite_primary_keys/connection_adapters/oracle_adapter.rb +0 -15
- data/lib/composite_primary_keys/connection_adapters/oracle_enhanced_adapter.rb +0 -17
- data/lib/composite_primary_keys/connection_adapters/sqlite3_adapter.rb +0 -15
- data/lib/composite_primary_keys/finder_methods.rb +0 -123
- data/lib/composite_primary_keys/primary_key.rb +0 -19
- data/lib/composite_primary_keys/query_methods.rb +0 -24
- data/lib/composite_primary_keys/read.rb +0 -25
- data/lib/composite_primary_keys/reflection.rb +0 -37
- data/lib/composite_primary_keys/write.rb +0 -18
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class JoinDependency
|
4
|
+
class JoinAssociation
|
5
|
+
def build_constraint(reflection, table, key, foreign_table, foreign_key)
|
6
|
+
# CPK
|
7
|
+
# constraint = table[key].eq(foreign_table[foreign_key])
|
8
|
+
constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
|
9
|
+
|
10
|
+
if reflection.klass.finder_needs_type_condition?
|
11
|
+
constraint = table.create_and([
|
12
|
+
constraint,
|
13
|
+
reflection.klass.send(:type_condition, table)
|
14
|
+
])
|
15
|
+
end
|
16
|
+
|
17
|
+
constraint
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class JoinDependency
|
4
|
+
class JoinPart
|
5
|
+
def aliased_primary_key
|
6
|
+
# CPK
|
7
|
+
# "#{aliased_prefix}_r0"
|
8
|
+
|
9
|
+
active_record.composite? ?
|
10
|
+
primary_key.inject([]) {|aliased_keys, key| aliased_keys << "#{ aliased_prefix }_r#{aliased_keys.length}"} :
|
11
|
+
"#{ aliased_prefix }_r0"
|
12
|
+
end
|
13
|
+
|
14
|
+
def record_id(row)
|
15
|
+
# CPK
|
16
|
+
# row[aliased_primary_key]
|
17
|
+
active_record.composite? ?
|
18
|
+
aliased_primary_key.map {|key| row[key]}.to_composite_keys :
|
19
|
+
row[aliased_primary_key]
|
20
|
+
end
|
21
|
+
|
22
|
+
def column_names_with_alias
|
23
|
+
unless @column_names_with_alias
|
24
|
+
@column_names_with_alias = []
|
25
|
+
|
26
|
+
# CPK
|
27
|
+
#([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
|
28
|
+
keys = active_record.composite? ? primary_key.map(&:to_s) : [primary_key]
|
29
|
+
|
30
|
+
(keys + (column_names - keys)).each_with_index do |column_name, i|
|
31
|
+
@column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@column_names_with_alias
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class Association
|
5
|
+
def records_for(ids)
|
6
|
+
# CPK
|
7
|
+
# scoped.where(association_key.in(ids))
|
8
|
+
predicate = cpk_in_predicate(table, reflection.foreign_key, ids)
|
9
|
+
scoped.where(predicate)
|
10
|
+
end
|
11
|
+
|
12
|
+
def associated_records_by_owner
|
13
|
+
# CPK
|
14
|
+
# owner_keys = owners.map { |owner| owner[owner_key_name] }.compact.uniq
|
15
|
+
owner_keys = owners.map do |owner|
|
16
|
+
Array(owner_key_name).map do |owner_key|
|
17
|
+
owner[owner_key]
|
18
|
+
end
|
19
|
+
end.compact.uniq
|
20
|
+
|
21
|
+
if klass.nil? || owner_keys.empty?
|
22
|
+
records = []
|
23
|
+
else
|
24
|
+
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
|
25
|
+
# Make several smaller queries if necessary or make one query if the adapter supports it
|
26
|
+
sliced = owner_keys.each_slice(model.connection.in_clause_length || owner_keys.size)
|
27
|
+
records = sliced.map { |slice| records_for(slice) }.flatten
|
28
|
+
end
|
29
|
+
|
30
|
+
# Each record may have multiple owners, and vice-versa
|
31
|
+
records_by_owner = Hash[owners.map { |owner| [owner, []] }]
|
32
|
+
records.each do |record|
|
33
|
+
# CPK
|
34
|
+
# owner_key = record[association_key_name].to_s
|
35
|
+
owner_key = Array(association_key_name).map do |key_name|
|
36
|
+
record[key_name]
|
37
|
+
end.join(CompositePrimaryKeys::ID_SEP)
|
38
|
+
|
39
|
+
owners_by_key[owner_key].each do |owner|
|
40
|
+
records_by_owner[owner] << record
|
41
|
+
end
|
42
|
+
end
|
43
|
+
records_by_owner
|
44
|
+
end
|
45
|
+
|
46
|
+
def owners_by_key
|
47
|
+
@owners_by_key ||= owners.group_by do |owner|
|
48
|
+
# CPK
|
49
|
+
# key = owner[owner_key_name]
|
50
|
+
key = Array(owner_key_name).map do |key_name|
|
51
|
+
owner[key_name]
|
52
|
+
end
|
53
|
+
# CPK
|
54
|
+
# key && key.to_s
|
55
|
+
key && key.join(CompositePrimaryKeys::ID_SEP)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class HasAndBelongsToMany
|
5
|
+
def records_for(ids)
|
6
|
+
# CPK
|
7
|
+
#scope = super
|
8
|
+
predicate = cpk_in_predicate(join_table, reflection.foreign_key, ids)
|
9
|
+
scope = scoped.where(predicate)
|
10
|
+
|
11
|
+
klass.connection.select_all(scope.arel.to_sql, 'SQL', scope.bind_values)
|
12
|
+
end
|
13
|
+
|
14
|
+
def join
|
15
|
+
# CPK
|
16
|
+
#condition = table[reflection.association_primary_key].eq(
|
17
|
+
# join_table[reflection.association_foreign_key])
|
18
|
+
condition = cpk_join_predicate(table, reflection.association_primary_key,
|
19
|
+
join_table, reflection.association_foreign_key)
|
20
|
+
|
21
|
+
table.create_join(join_table, table.create_on(condition))
|
22
|
+
end
|
23
|
+
|
24
|
+
def association_key_alias(field)
|
25
|
+
"ar_association_key_name_#{field.to_s}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def join_select
|
29
|
+
# CPK
|
30
|
+
# association_key.as(Arel.sql(association_key_name))
|
31
|
+
Array(reflection.foreign_key).map do |key|
|
32
|
+
join_table[key].as(Arel.sql(association_key_alias(key)))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def association_key_name
|
37
|
+
# CPK
|
38
|
+
# 'ar_association_key_name'
|
39
|
+
Array(reflection.foreign_key).map do |key|
|
40
|
+
association_key_alias(key)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Dirty
|
4
|
+
def write_attribute(attr, value)
|
5
|
+
# CPK
|
6
|
+
# attr = attr.to_s
|
7
|
+
attr = attr.to_s unless self.composite?
|
8
|
+
|
9
|
+
# The attribute already has an unsaved change.
|
10
|
+
if attribute_changed?(attr)
|
11
|
+
old = @changed_attributes[attr]
|
12
|
+
@changed_attributes.delete(attr) unless field_changed?(attr, old, value)
|
13
|
+
else
|
14
|
+
old = clone_attribute_value(:read_attribute, attr)
|
15
|
+
# Save Time objects as TimeWithZone if time_zone_aware_attributes == true
|
16
|
+
old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
|
17
|
+
@changed_attributes[attr] = old if field_changed?(attr, old, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Carry on.
|
21
|
+
super(attr, value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ActiveRecord::Base.class_eval do
|
28
|
+
alias :[]= :write_attribute
|
29
|
+
public :[]=
|
30
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Read
|
4
|
+
module ClassMethods
|
5
|
+
def define_read_method(method_name, attr_name, column)
|
6
|
+
cast_code = column.type_cast_code('v')
|
7
|
+
# CPK - this is a really horrid hack, needed to get
|
8
|
+
# right class namespace :(
|
9
|
+
if cast_code.match(/^ActiveRecord/)
|
10
|
+
cast_code = "::#{cast_code}"
|
11
|
+
end
|
12
|
+
|
13
|
+
access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}"
|
14
|
+
|
15
|
+
# CPK
|
16
|
+
# unless attr_name.to_s == self.primary_key.to_s
|
17
|
+
# access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
|
18
|
+
# end
|
19
|
+
primary_keys = Array(self.primary_key)
|
20
|
+
|
21
|
+
unless primary_keys.include?(attr_name.to_s)
|
22
|
+
access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
|
23
|
+
end
|
24
|
+
|
25
|
+
if cache_attribute?(attr_name)
|
26
|
+
access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Where possible, generate the method by evalling a string, as this will result in
|
30
|
+
# faster accesses because it avoids the block eval and then string eval incurred
|
31
|
+
# by the second branch.
|
32
|
+
#
|
33
|
+
# The second, slower, branch is necessary to support instances where the database
|
34
|
+
# returns columns with extra stuff in (like 'my_column(omg)').
|
35
|
+
if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
|
36
|
+
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
|
37
|
+
def _#{method_name}
|
38
|
+
#{access_code}
|
39
|
+
end
|
40
|
+
|
41
|
+
alias #{method_name} _#{method_name}
|
42
|
+
STR
|
43
|
+
else
|
44
|
+
generated_attribute_methods.module_eval do
|
45
|
+
define_method("_#{method_name}") { eval(access_code) }
|
46
|
+
alias_method(method_name, "_#{method_name}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_attribute(attr_name)
|
53
|
+
# CPK
|
54
|
+
if attr_name.kind_of?(Array)
|
55
|
+
attr_name.map {|name| read_attribute(name)}.to_composite_keys
|
56
|
+
elsif respond_to? "_#{attr_name}"
|
57
|
+
send "_#{attr_name}" if @attributes.has_key?(attr_name.to_s)
|
58
|
+
else
|
59
|
+
_read_attribute attr_name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def _read_attribute(attr_name)
|
65
|
+
attr_name = attr_name.to_s
|
66
|
+
# CPK
|
67
|
+
# attr_name = self.class.primary_key if attr_name == 'id'
|
68
|
+
attr_name = self.class.primary_key if (attr_name == 'id' and !self.composite?)
|
69
|
+
value = @attributes[attr_name]
|
70
|
+
unless value.nil?
|
71
|
+
if column = column_for_attribute(attr_name)
|
72
|
+
if unserializable_attribute?(attr_name, column)
|
73
|
+
unserialize_attribute(attr_name)
|
74
|
+
else
|
75
|
+
column.type_cast(value)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
ActiveRecord::Base.class_eval do
|
87
|
+
alias :[] :read_attribute
|
88
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Write
|
4
|
+
def write_attribute(attr_name, value)
|
5
|
+
# CPK
|
6
|
+
if attr_name.kind_of?(Array)
|
7
|
+
unless value.length == attr_name.length
|
8
|
+
raise "Number of attr_names and values do not match"
|
9
|
+
end
|
10
|
+
[attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
|
11
|
+
value
|
12
|
+
else
|
13
|
+
attr_name = attr_name.to_s
|
14
|
+
# CPK
|
15
|
+
# attr_name = self.class.primary_key if attr_name == 'id'
|
16
|
+
attr_name = self.class.primary_key if (attr_name == 'id' and !self.composite?)
|
17
|
+
@attributes_cache.delete(attr_name)
|
18
|
+
if (column = column_for_attribute(attr_name)) && column.number?
|
19
|
+
@attributes[attr_name] = convert_number_column_value(value)
|
20
|
+
else
|
21
|
+
@attributes[attr_name] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#ActiveRecord::Base.class_eval do
|
30
|
+
# alias :[]= :write_attribute
|
31
|
+
# alias :raw_write_attribute :write_attribute
|
32
|
+
# public :[]=
|
33
|
+
#end
|
@@ -16,12 +16,11 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
|
18
18
|
cattr_accessor :primary_keys
|
19
|
-
self.primary_keys = keys.map { |k| k.to_sym }
|
19
|
+
self.primary_keys = keys.map { |k| k.to_sym }.to_composite_keys
|
20
20
|
|
21
21
|
class_eval <<-EOV
|
22
|
-
extend
|
22
|
+
extend CompositeClassMethods
|
23
23
|
include CompositeInstanceMethods
|
24
|
-
include CompositePrimaryKeys::ActiveRecord::AssociationPreload
|
25
24
|
EOV
|
26
25
|
end
|
27
26
|
|
@@ -34,38 +33,6 @@ module ActiveRecord
|
|
34
33
|
self.class.composite?
|
35
34
|
end
|
36
35
|
|
37
|
-
def [](attr_name)
|
38
|
-
# CPK
|
39
|
-
if attr_name.is_a?(String) and attr_name != attr_name.split(CompositePrimaryKeys::ID_SEP).first
|
40
|
-
attr_name = attr_name.split(CompositePrimaryKeys::ID_SEP)
|
41
|
-
end
|
42
|
-
|
43
|
-
# CPK
|
44
|
-
if attr_name.is_a?(Array)
|
45
|
-
values = attr_name.map {|name| read_attribute(name)}
|
46
|
-
CompositePrimaryKeys::CompositeKeys.new(values)
|
47
|
-
else
|
48
|
-
read_attribute(attr_name)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def []=(attr_name, value)
|
53
|
-
# CPK
|
54
|
-
if attr_name.is_a?(String) and attr_name != attr_name.split(CompositePrimaryKeys::ID_SEP).first
|
55
|
-
attr_name = attr_name.split(CompositePrimaryKeys::ID_SEP)
|
56
|
-
end
|
57
|
-
|
58
|
-
if attr_name.is_a? Array
|
59
|
-
unless value.length == attr_name.length
|
60
|
-
raise "Number of attr_names and values do not match"
|
61
|
-
end
|
62
|
-
[attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
|
63
|
-
value
|
64
|
-
else
|
65
|
-
write_attribute(attr_name, value)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
36
|
module CompositeClassMethods
|
70
37
|
def primary_key
|
71
38
|
primary_keys
|
@@ -84,20 +51,6 @@ module ActiveRecord
|
|
84
51
|
def ids_to_s(many_ids, id_sep = CompositePrimaryKeys::ID_SEP, list_sep = ',', left_bracket = '(', right_bracket = ')')
|
85
52
|
many_ids.map {|ids| "#{left_bracket}#{CompositePrimaryKeys::CompositeKeys.new(ids)}#{right_bracket}"}.join(list_sep)
|
86
53
|
end
|
87
|
-
|
88
|
-
def relation #:nodoc:
|
89
|
-
@relation ||= begin
|
90
|
-
result = Relation.new(self, arel_table)
|
91
|
-
# CPK
|
92
|
-
class << result
|
93
|
-
include CompositePrimaryKeys::ActiveRecord::FinderMethods::InstanceMethods
|
94
|
-
include CompositePrimaryKeys::ActiveRecord::Relation::InstanceMethods
|
95
|
-
end
|
96
|
-
result
|
97
|
-
end
|
98
|
-
|
99
|
-
finder_needs_type_condition? ? @relation.where(type_condition) : @relation
|
100
|
-
end
|
101
54
|
end
|
102
55
|
|
103
56
|
module CompositeInstanceMethods
|
@@ -143,35 +96,30 @@ module ActiveRecord
|
|
143
96
|
ids.is_a?(Array) ? super(comparison_object) && ids.all? {|id| id.present?} : super(comparison_object)
|
144
97
|
end
|
145
98
|
|
146
|
-
|
147
|
-
# as it copies the object's attributes only, not its associations. The extent of a "deep" clone is
|
148
|
-
# application specific and is therefore left to the application to implement according to its need.
|
149
|
-
def initialize_copy(other)
|
150
|
-
# Think the assertion which fails if the after_initialize callback goes at the end of the method is wrong. The
|
151
|
-
# deleted clone method called new which therefore called the after_initialize callback. It then went on to copy
|
152
|
-
# over the attributes. But if it's copying the attributes afterwards then it hasn't finished initializing right?
|
153
|
-
# For example in the test suite the topic model's after_initialize method sets the author_email_address to
|
154
|
-
# test@test.com. I would have thought this would mean that all cloned models would have an author email address
|
155
|
-
# of test@test.com. However the test_clone test method seems to test that this is not the case. As a result the
|
156
|
-
# after_initialize callback has to be run *before* the copying of the atrributes rather than afterwards in order
|
157
|
-
# for all tests to pass. This makes no sense to me.
|
158
|
-
callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
|
99
|
+
def initialize_dup(other)
|
159
100
|
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
|
160
101
|
# CPK
|
161
102
|
#cloned_attributes.delete(self.class.primary_key)
|
162
103
|
self.class.primary_key.each {|key| cloned_attributes.delete(key.to_s)}
|
163
104
|
|
164
105
|
@attributes = cloned_attributes
|
165
|
-
clear_aggregation_cache
|
166
|
-
@attributes_cache = {}
|
167
|
-
@new_record = true
|
168
|
-
ensure_proper_type
|
169
106
|
|
170
|
-
if
|
171
|
-
|
172
|
-
|
107
|
+
_run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks)
|
108
|
+
|
109
|
+
@changed_attributes = {}
|
110
|
+
attributes_from_column_definition.each do |attr, orig_value|
|
111
|
+
@changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
|
173
112
|
end
|
113
|
+
|
114
|
+
@aggregation_cache = {}
|
115
|
+
@association_cache = {}
|
116
|
+
@attributes_cache = {}
|
117
|
+
@new_record = true
|
118
|
+
|
119
|
+
ensure_proper_type
|
120
|
+
populate_with_current_scope_attributes
|
121
|
+
clear_timestamp_attributes
|
174
122
|
end
|
175
123
|
end
|
176
124
|
end
|
177
|
-
end
|
125
|
+
end
|