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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +13 -0
  3. data/README.rdoc +182 -182
  4. data/Rakefile +37 -37
  5. data/lib/composite_primary_keys/associations/collection_association.rb +38 -38
  6. data/lib/composite_primary_keys/associations/preloader/association.rb +52 -52
  7. data/lib/composite_primary_keys/autosave_association.rb +60 -60
  8. data/lib/composite_primary_keys/composite_arrays.rb +88 -88
  9. data/lib/composite_primary_keys/composite_predicates.rb +121 -121
  10. data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +36 -36
  11. data/lib/composite_primary_keys/core.rb +71 -48
  12. data/lib/composite_primary_keys/nested_attributes.rb +2 -2
  13. data/lib/composite_primary_keys/persistence.rb +96 -96
  14. data/lib/composite_primary_keys/reflection.rb +93 -91
  15. data/lib/composite_primary_keys/relation/calculations.rb +110 -110
  16. data/lib/composite_primary_keys/relation/query_methods.rb +40 -40
  17. data/lib/composite_primary_keys/relation.rb +199 -199
  18. data/lib/composite_primary_keys/validations/uniqueness.rb +40 -40
  19. data/lib/composite_primary_keys/version.rb +1 -1
  20. data/lib/composite_primary_keys.rb +117 -117
  21. data/scripts/console.rb +48 -48
  22. data/tasks/databases/trilogy.rake +23 -23
  23. data/test/abstract_unit.rb +124 -124
  24. data/test/connections/databases.ci.yml +32 -32
  25. data/test/fixtures/admin.rb +4 -4
  26. data/test/fixtures/db_definitions/db2-create-tables.sql +146 -146
  27. data/test/fixtures/db_definitions/db2-drop-tables.sql +23 -23
  28. data/test/fixtures/db_definitions/mysql.sql +203 -203
  29. data/test/fixtures/db_definitions/oracle.drop.sql +45 -45
  30. data/test/fixtures/db_definitions/oracle.sql +220 -220
  31. data/test/fixtures/db_definitions/postgresql.sql +205 -205
  32. data/test/fixtures/db_definitions/sqlite.sql +190 -190
  33. data/test/fixtures/db_definitions/sqlserver.sql +199 -199
  34. data/test/fixtures/department.rb +20 -20
  35. data/test/fixtures/moderator.rb +4 -4
  36. data/test/fixtures/room.rb +14 -14
  37. data/test/fixtures/room_assignment.rb +18 -18
  38. data/test/fixtures/staff_room.rb +6 -6
  39. data/test/fixtures/staff_room_key.rb +6 -6
  40. data/test/fixtures/user.rb +14 -14
  41. data/test/test_associations.rb +403 -403
  42. data/test/test_composite_arrays.rb +44 -44
  43. data/test/test_equal.rb +55 -26
  44. data/test/test_has_one_through.rb +30 -30
  45. data/test/test_hash.rb +73 -0
  46. data/test/test_nested_attributes.rb +90 -67
  47. 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
- module ClassMethods
20
- def find(*ids) # :nodoc:
21
- # We don't have cache keys for this stuff yet
22
- return super unless ids.length == 1
23
- return super if block_given? ||
24
- primary_key.nil? ||
25
- scope_attributes? ||
26
- columns_hash.key?(inheritance_column) && !base_class?
27
-
28
- # CPK
29
- return super if self.composite?
30
-
31
- id = ids.first
32
-
33
- return super if StatementCache.unsupported_value?(id)
34
-
35
- key = primary_key
36
-
37
- statement = cached_find_by_statement(key) { |params|
38
- where(key => params.bind).limit(1)
39
- }
40
-
41
- record = statement.execute([id], connection)&.first
42
- unless record
43
- raise ::ActiveRecord::RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
44
- end
45
- record
46
- end
47
- end
48
- end
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 = existing_records.detect { |record| record.id.to_s == attributes["id"].to_s }
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 = association.target.detect { |record| record.id.to_s == attributes["id"].to_s }
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
- key = join_primary_key
10
- foreign_key = join_foreign_key
11
-
12
- # CPK
13
- #klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
14
- constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
15
- klass_scope.where!(constraint)
16
-
17
- if type
18
- klass_scope.where!(type => foreign_klass.polymorphic_name)
19
- end
20
-
21
- if klass.finder_needs_type_condition?
22
- klass_scope.where!(klass.send(:type_condition, table))
23
- end
24
-
25
- scope_chain_items.inject(klass_scope, &:merge!)
26
- end
27
- end
28
-
29
- class AssociationReflection < MacroReflection
30
- def foreign_key
31
- # CPK
32
- # @foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
33
- @foreign_key ||= extract_keys(options[:foreign_key]) || derive_foreign_key
34
- end
35
-
36
- def association_foreign_key
37
- # CPK
38
- # @association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
39
- @association_foreign_key ||= extract_keys(options[:association_foreign_key]) || class_name.foreign_key
40
- end
41
-
42
- def active_record_primary_key
43
- # 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)
44
- # @active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
45
- @active_record_primary_key ||= begin
46
- pk = options[:primary_key] || primary_key(active_record)
47
- pk.freeze
48
- end
49
- end
50
-
51
- private
52
-
53
- def extract_keys(keys)
54
- case keys
55
- when Array
56
- keys.map { |k| k.to_s }
57
- when NilClass
58
- nil
59
- else
60
- keys.to_s
61
- end
62
- end
63
- end
64
-
65
- class BelongsToReflection < AssociationReflection
66
- def association_primary_key(klass = nil)
67
- if primary_key = options[:primary_key]
68
- # CPK
69
- # @association_primary_key ||= -primary_key.to_s
70
- @association_primary_key ||= primary_key.freeze
71
- else
72
- primary_key(klass || self.klass)
73
- end
74
- end
75
- end
76
-
77
- class ThroughReflection < AbstractReflection #:nodoc:
78
- def association_primary_key(klass = nil)
79
- # Get the "actual" source reflection if the immediate source reflection has a
80
- # source reflection itself
81
- if primary_key = actual_source_reflection.options[:primary_key]
82
- # CPK
83
- # @association_primary_key ||= -primary_key.to_s
84
- @association_primary_key ||= primary_key.freeze
85
- else
86
- primary_key(klass || self.klass)
87
- end
88
- end
89
- end
90
- end
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