composite_primary_keys 14.0.8 → 14.0.10

Sign up to get free protection for your applications and to get access to all the features.
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