composite_primary_keys 14.0.8 → 14.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.rdoc +13 -0
- data/README.rdoc +182 -182
- data/Rakefile +37 -37
- data/lib/composite_primary_keys/associations/collection_association.rb +38 -38
- data/lib/composite_primary_keys/associations/preloader/association.rb +52 -52
- data/lib/composite_primary_keys/autosave_association.rb +60 -60
- data/lib/composite_primary_keys/composite_arrays.rb +88 -88
- data/lib/composite_primary_keys/composite_predicates.rb +121 -121
- data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +36 -36
- data/lib/composite_primary_keys/core.rb +71 -48
- data/lib/composite_primary_keys/nested_attributes.rb +2 -2
- data/lib/composite_primary_keys/persistence.rb +96 -96
- data/lib/composite_primary_keys/reflection.rb +93 -91
- data/lib/composite_primary_keys/relation/calculations.rb +110 -110
- data/lib/composite_primary_keys/relation/query_methods.rb +40 -40
- data/lib/composite_primary_keys/relation.rb +199 -199
- data/lib/composite_primary_keys/validations/uniqueness.rb +40 -40
- data/lib/composite_primary_keys/version.rb +1 -1
- data/lib/composite_primary_keys.rb +117 -117
- data/scripts/console.rb +48 -48
- data/tasks/databases/trilogy.rake +23 -23
- data/test/abstract_unit.rb +124 -124
- data/test/connections/databases.ci.yml +32 -32
- data/test/fixtures/admin.rb +4 -4
- data/test/fixtures/db_definitions/db2-create-tables.sql +146 -146
- data/test/fixtures/db_definitions/db2-drop-tables.sql +23 -23
- data/test/fixtures/db_definitions/mysql.sql +203 -203
- data/test/fixtures/db_definitions/oracle.drop.sql +45 -45
- data/test/fixtures/db_definitions/oracle.sql +220 -220
- data/test/fixtures/db_definitions/postgresql.sql +205 -205
- data/test/fixtures/db_definitions/sqlite.sql +190 -190
- data/test/fixtures/db_definitions/sqlserver.sql +199 -199
- data/test/fixtures/department.rb +20 -20
- data/test/fixtures/moderator.rb +4 -4
- data/test/fixtures/room.rb +14 -14
- data/test/fixtures/room_assignment.rb +18 -18
- data/test/fixtures/staff_room.rb +6 -6
- data/test/fixtures/staff_room_key.rb +6 -6
- data/test/fixtures/user.rb +14 -14
- data/test/test_associations.rb +403 -403
- data/test/test_composite_arrays.rb +44 -44
- data/test/test_equal.rb +55 -26
- data/test/test_has_one_through.rb +30 -30
- data/test/test_hash.rb +73 -0
- data/test/test_nested_attributes.rb +90 -67
- metadata +7 -8
@@ -1,49 +1,72 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Core
|
3
|
-
def initialize_dup(other) # :nodoc:
|
4
|
-
@attributes = @attributes.deep_dup
|
5
|
-
# CPK
|
6
|
-
#@attributes.reset(@primary_key)
|
7
|
-
Array(self.class.primary_key).each {|key| @attributes.reset(key)}
|
8
|
-
|
9
|
-
_run_initialize_callbacks
|
10
|
-
|
11
|
-
@new_record = true
|
12
|
-
@destroyed = false
|
13
|
-
@_start_transaction_state = nil
|
14
|
-
@transaction_state = nil
|
15
|
-
|
16
|
-
super
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
id
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
1
|
+
module ActiveRecord
|
2
|
+
module Core
|
3
|
+
def initialize_dup(other) # :nodoc:
|
4
|
+
@attributes = @attributes.deep_dup
|
5
|
+
# CPK
|
6
|
+
#@attributes.reset(@primary_key)
|
7
|
+
Array(self.class.primary_key).each {|key| @attributes.reset(key)}
|
8
|
+
|
9
|
+
_run_initialize_callbacks
|
10
|
+
|
11
|
+
@new_record = true
|
12
|
+
@destroyed = false
|
13
|
+
@_start_transaction_state = nil
|
14
|
+
@transaction_state = nil
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def primary_key_values_present?
|
20
|
+
if self.composite?
|
21
|
+
id.all? {|key| !key.nil?}
|
22
|
+
else
|
23
|
+
!id.nil?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(comparison_object)
|
28
|
+
super ||
|
29
|
+
comparison_object.instance_of?(self.class) &&
|
30
|
+
primary_key_values_present? &&
|
31
|
+
comparison_object.id == id
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash
|
35
|
+
if primary_key_values_present?
|
36
|
+
self.class.hash ^ id.hash
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def find(*ids) # :nodoc:
|
44
|
+
# We don't have cache keys for this stuff yet
|
45
|
+
return super unless ids.length == 1
|
46
|
+
return super if block_given? ||
|
47
|
+
primary_key.nil? ||
|
48
|
+
scope_attributes? ||
|
49
|
+
columns_hash.key?(inheritance_column) && !base_class?
|
50
|
+
|
51
|
+
# CPK
|
52
|
+
return super if self.composite?
|
53
|
+
|
54
|
+
id = ids.first
|
55
|
+
|
56
|
+
return super if StatementCache.unsupported_value?(id)
|
57
|
+
|
58
|
+
key = primary_key
|
59
|
+
|
60
|
+
statement = cached_find_by_statement(key) { |params|
|
61
|
+
where(key => params.bind).limit(1)
|
62
|
+
}
|
63
|
+
|
64
|
+
record = statement.execute([id], connection)&.first
|
65
|
+
unless record
|
66
|
+
raise ::ActiveRecord::RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
|
67
|
+
end
|
68
|
+
record
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
49
72
|
end
|
@@ -61,12 +61,12 @@ module ActiveRecord
|
|
61
61
|
unless reject_new_record?(association_name, attributes)
|
62
62
|
association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
|
63
63
|
end
|
64
|
-
elsif existing_record =
|
64
|
+
elsif existing_record = cpk_detect_record(attributes['id'], existing_records)
|
65
65
|
unless call_reject_if(association_name, attributes)
|
66
66
|
# Make sure we are operating on the actual object which is in the association's
|
67
67
|
# proxy_target array (either by finding it, or adding it if not found)
|
68
68
|
# Take into account that the proxy_target may have changed due to callbacks
|
69
|
-
target_record =
|
69
|
+
target_record = cpk_detect_record(attributes['id'], association.target)
|
70
70
|
if target_record
|
71
71
|
existing_record = target_record
|
72
72
|
else
|
@@ -1,96 +1,96 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Persistence
|
3
|
-
module ClassMethods
|
4
|
-
def delete(id_or_array)
|
5
|
-
# CPK
|
6
|
-
if self.composite?
|
7
|
-
id_or_array = if id_or_array.is_a?(CompositePrimaryKeys::CompositeKeys)
|
8
|
-
[id_or_array]
|
9
|
-
else
|
10
|
-
Array(id_or_array)
|
11
|
-
end
|
12
|
-
|
13
|
-
# Delete should return the number of deleted records
|
14
|
-
id_or_array.map do |id|
|
15
|
-
# Is the passed in id actually a record?
|
16
|
-
id = id.kind_of?(::ActiveRecord::Base) ? id.id : id
|
17
|
-
delete_by(cpk_id_predicate(self.arel_table, self.primary_key, id))
|
18
|
-
end.sum
|
19
|
-
else
|
20
|
-
delete_by(primary_key => id_or_array)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def _update_record(values, constraints) # :nodoc:
|
25
|
-
# CPK
|
26
|
-
if self.composite? && constraints[primary_key]
|
27
|
-
primary_key_values = constraints.delete(primary_key)
|
28
|
-
primary_key.each_with_index do |key, i|
|
29
|
-
constraints[key] = primary_key_values[i]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
34
|
-
|
35
|
-
default_constraint = build_default_constraint
|
36
|
-
constraints << default_constraint if default_constraint
|
37
|
-
|
38
|
-
if current_scope = self.global_current_scope
|
39
|
-
constraints << current_scope.where_clause.ast
|
40
|
-
end
|
41
|
-
|
42
|
-
um = Arel::UpdateManager.new(arel_table)
|
43
|
-
um.set(values.transform_keys { |name| arel_table[name] })
|
44
|
-
um.wheres = constraints
|
45
|
-
|
46
|
-
connection.update(um, "#{self} Update")
|
47
|
-
end
|
48
|
-
|
49
|
-
def _delete_record(constraints) # :nodoc:
|
50
|
-
# CPK
|
51
|
-
if self.composite? && constraints[primary_key]
|
52
|
-
primary_key_values = constraints.delete(primary_key)
|
53
|
-
primary_key.each_with_index do |key, i|
|
54
|
-
constraints[key] = primary_key_values[i]
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
59
|
-
|
60
|
-
default_constraint = build_default_constraint
|
61
|
-
constraints << default_constraint if default_constraint
|
62
|
-
|
63
|
-
if current_scope = self.global_current_scope
|
64
|
-
constraints << current_scope.where_clause.ast
|
65
|
-
end
|
66
|
-
|
67
|
-
dm = Arel::DeleteManager.new(arel_table)
|
68
|
-
dm.wheres = constraints
|
69
|
-
|
70
|
-
connection.delete(dm, "#{self} Destroy")
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def _create_record(attribute_names = self.attribute_names)
|
75
|
-
attribute_names = attributes_for_create(attribute_names)
|
76
|
-
|
77
|
-
new_id = self.class._insert_record(
|
78
|
-
attributes_with_values(attribute_names)
|
79
|
-
)
|
80
|
-
|
81
|
-
# CPK
|
82
|
-
if self.composite?
|
83
|
-
self.id = self.id.zip(Array(new_id)).map {|id1, id2| id2.nil? ? id1 : id2}
|
84
|
-
else
|
85
|
-
self.id ||= new_id if self.class.primary_key
|
86
|
-
end
|
87
|
-
|
88
|
-
@new_record = false
|
89
|
-
@previously_new_record = true
|
90
|
-
|
91
|
-
yield(self) if block_given?
|
92
|
-
|
93
|
-
id
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module Persistence
|
3
|
+
module ClassMethods
|
4
|
+
def delete(id_or_array)
|
5
|
+
# CPK
|
6
|
+
if self.composite?
|
7
|
+
id_or_array = if id_or_array.is_a?(CompositePrimaryKeys::CompositeKeys)
|
8
|
+
[id_or_array]
|
9
|
+
else
|
10
|
+
Array(id_or_array)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Delete should return the number of deleted records
|
14
|
+
id_or_array.map do |id|
|
15
|
+
# Is the passed in id actually a record?
|
16
|
+
id = id.kind_of?(::ActiveRecord::Base) ? id.id : id
|
17
|
+
delete_by(cpk_id_predicate(self.arel_table, self.primary_key, id))
|
18
|
+
end.sum
|
19
|
+
else
|
20
|
+
delete_by(primary_key => id_or_array)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def _update_record(values, constraints) # :nodoc:
|
25
|
+
# CPK
|
26
|
+
if self.composite? && constraints[primary_key]
|
27
|
+
primary_key_values = constraints.delete(primary_key)
|
28
|
+
primary_key.each_with_index do |key, i|
|
29
|
+
constraints[key] = primary_key_values[i]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
34
|
+
|
35
|
+
default_constraint = build_default_constraint
|
36
|
+
constraints << default_constraint if default_constraint
|
37
|
+
|
38
|
+
if current_scope = self.global_current_scope
|
39
|
+
constraints << current_scope.where_clause.ast
|
40
|
+
end
|
41
|
+
|
42
|
+
um = Arel::UpdateManager.new(arel_table)
|
43
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
44
|
+
um.wheres = constraints
|
45
|
+
|
46
|
+
connection.update(um, "#{self} Update")
|
47
|
+
end
|
48
|
+
|
49
|
+
def _delete_record(constraints) # :nodoc:
|
50
|
+
# CPK
|
51
|
+
if self.composite? && constraints[primary_key]
|
52
|
+
primary_key_values = constraints.delete(primary_key)
|
53
|
+
primary_key.each_with_index do |key, i|
|
54
|
+
constraints[key] = primary_key_values[i]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
59
|
+
|
60
|
+
default_constraint = build_default_constraint
|
61
|
+
constraints << default_constraint if default_constraint
|
62
|
+
|
63
|
+
if current_scope = self.global_current_scope
|
64
|
+
constraints << current_scope.where_clause.ast
|
65
|
+
end
|
66
|
+
|
67
|
+
dm = Arel::DeleteManager.new(arel_table)
|
68
|
+
dm.wheres = constraints
|
69
|
+
|
70
|
+
connection.delete(dm, "#{self} Destroy")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def _create_record(attribute_names = self.attribute_names)
|
75
|
+
attribute_names = attributes_for_create(attribute_names)
|
76
|
+
|
77
|
+
new_id = self.class._insert_record(
|
78
|
+
attributes_with_values(attribute_names)
|
79
|
+
)
|
80
|
+
|
81
|
+
# CPK
|
82
|
+
if self.composite?
|
83
|
+
self.id = self.id.zip(Array(new_id)).map {|id1, id2| id2.nil? ? id1 : id2}
|
84
|
+
else
|
85
|
+
self.id ||= new_id if self.class.primary_key
|
86
|
+
end
|
87
|
+
|
88
|
+
@new_record = false
|
89
|
+
@previously_new_record = true
|
90
|
+
|
91
|
+
yield(self) if block_given?
|
92
|
+
|
93
|
+
id
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -1,91 +1,93 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Reflection
|
3
|
-
class AbstractReflection
|
4
|
-
def join_scope(table, foreign_table, foreign_klass)
|
5
|
-
predicate_builder = predicate_builder(table)
|
6
|
-
scope_chain_items = join_scopes(table, predicate_builder)
|
7
|
-
klass_scope = klass_join_scope(table, predicate_builder)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
when
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
if
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module Reflection
|
3
|
+
class AbstractReflection
|
4
|
+
def join_scope(table, foreign_table, foreign_klass)
|
5
|
+
predicate_builder = predicate_builder(table)
|
6
|
+
scope_chain_items = join_scopes(table, predicate_builder)
|
7
|
+
klass_scope = klass_join_scope(table, predicate_builder)
|
8
|
+
|
9
|
+
if type
|
10
|
+
klass_scope.where!(type => foreign_klass.polymorphic_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
scope_chain_items.inject(klass_scope, &:merge!)
|
14
|
+
|
15
|
+
key = join_primary_key
|
16
|
+
foreign_key = join_foreign_key
|
17
|
+
|
18
|
+
# CPK
|
19
|
+
#klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
|
20
|
+
constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
|
21
|
+
klass_scope.where!(constraint)
|
22
|
+
|
23
|
+
if klass.finder_needs_type_condition?
|
24
|
+
klass_scope.where!(klass.send(:type_condition, table))
|
25
|
+
end
|
26
|
+
|
27
|
+
klass_scope
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class AssociationReflection < MacroReflection
|
32
|
+
def foreign_key
|
33
|
+
# CPK
|
34
|
+
# @foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
|
35
|
+
@foreign_key ||= extract_keys(options[:foreign_key]) || derive_foreign_key
|
36
|
+
end
|
37
|
+
|
38
|
+
def association_foreign_key
|
39
|
+
# CPK
|
40
|
+
# @association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
|
41
|
+
@association_foreign_key ||= extract_keys(options[:association_foreign_key]) || class_name.foreign_key
|
42
|
+
end
|
43
|
+
|
44
|
+
def active_record_primary_key
|
45
|
+
# CPK (Rails freezes the string returned in the expression that calculates PK here. But Rails uses the `-` method which is not available on Array for CPK, so we calculate it in one line and freeze it on the next)
|
46
|
+
# @active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
|
47
|
+
@active_record_primary_key ||= begin
|
48
|
+
pk = options[:primary_key] || primary_key(active_record)
|
49
|
+
pk.freeze
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def extract_keys(keys)
|
56
|
+
case keys
|
57
|
+
when Array
|
58
|
+
keys.map { |k| k.to_s }
|
59
|
+
when NilClass
|
60
|
+
nil
|
61
|
+
else
|
62
|
+
keys.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class BelongsToReflection < AssociationReflection
|
68
|
+
def association_primary_key(klass = nil)
|
69
|
+
if primary_key = options[:primary_key]
|
70
|
+
# CPK
|
71
|
+
# @association_primary_key ||= -primary_key.to_s
|
72
|
+
@association_primary_key ||= primary_key.freeze
|
73
|
+
else
|
74
|
+
primary_key(klass || self.klass)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ThroughReflection < AbstractReflection #:nodoc:
|
80
|
+
def association_primary_key(klass = nil)
|
81
|
+
# Get the "actual" source reflection if the immediate source reflection has a
|
82
|
+
# source reflection itself
|
83
|
+
if primary_key = actual_source_reflection.options[:primary_key]
|
84
|
+
# CPK
|
85
|
+
# @association_primary_key ||= -primary_key.to_s
|
86
|
+
@association_primary_key ||= primary_key.freeze
|
87
|
+
else
|
88
|
+
primary_key(klass || self.klass)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|