composite_primary_keys 12.0.9 → 13.0.0

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +883 -877
  3. data/README.rdoc +181 -180
  4. data/lib/composite_primary_keys.rb +119 -117
  5. data/lib/composite_primary_keys/active_model/attribute_assignment.rb +19 -19
  6. data/lib/composite_primary_keys/associations/association_scope.rb +66 -68
  7. data/lib/composite_primary_keys/associations/join_dependency.rb +118 -103
  8. data/lib/composite_primary_keys/attribute_methods.rb +21 -9
  9. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +0 -2
  10. data/lib/composite_primary_keys/attribute_methods/read.rb +30 -30
  11. data/lib/composite_primary_keys/attribute_methods/write.rb +35 -35
  12. data/lib/composite_primary_keys/base.rb +141 -141
  13. data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +37 -37
  14. data/lib/composite_primary_keys/connection_adapters/sqlserver/database_statements.rb +44 -44
  15. data/lib/composite_primary_keys/core.rb +48 -48
  16. data/lib/composite_primary_keys/nested_attributes.rb +1 -1
  17. data/lib/composite_primary_keys/persistence.rb +82 -81
  18. data/lib/composite_primary_keys/reflection.rb +91 -29
  19. data/lib/composite_primary_keys/relation.rb +197 -193
  20. data/lib/composite_primary_keys/relation/batches.rb +15 -7
  21. data/lib/composite_primary_keys/relation/calculations.rb +104 -81
  22. data/lib/composite_primary_keys/relation/finder_methods.rb +235 -235
  23. data/lib/composite_primary_keys/relation/predicate_builder/association_query_value.rb +39 -20
  24. data/lib/composite_primary_keys/relation/query_methods.rb +42 -42
  25. data/lib/composite_primary_keys/relation/where_clause.rb +18 -23
  26. data/lib/composite_primary_keys/table_metadata.rb +11 -0
  27. data/lib/composite_primary_keys/version.rb +8 -8
  28. data/test/abstract_unit.rb +114 -114
  29. data/test/connections/databases.ci.yml +22 -22
  30. data/test/fixtures/db_definitions/db2-create-tables.sql +112 -112
  31. data/test/fixtures/db_definitions/db2-drop-tables.sql +16 -16
  32. data/test/fixtures/db_definitions/mysql.sql +180 -180
  33. data/test/fixtures/db_definitions/oracle.drop.sql +41 -41
  34. data/test/fixtures/db_definitions/oracle.sql +199 -199
  35. data/test/fixtures/db_definitions/postgresql.sql +182 -182
  36. data/test/fixtures/db_definitions/sqlite.sql +169 -169
  37. data/test/fixtures/db_definitions/sqlserver.sql +176 -176
  38. data/test/fixtures/department.rb +16 -16
  39. data/test/fixtures/departments.yml +19 -15
  40. data/test/fixtures/employees.yml +33 -28
  41. data/test/fixtures/restaurants_suburbs.yml +10 -10
  42. data/test/fixtures/streets.yml +16 -16
  43. data/test/fixtures/suburbs.yml +14 -14
  44. data/test/fixtures/user.rb +11 -11
  45. data/test/test_associations.rb +364 -358
  46. data/test/test_attributes.rb +75 -60
  47. data/test/test_calculations.rb +49 -42
  48. data/test/test_create.rb +218 -206
  49. data/test/test_delete.rb +182 -179
  50. data/test/test_exists.rb +39 -39
  51. data/test/test_find.rb +170 -164
  52. data/test/test_ids.rb +112 -112
  53. data/test/test_nested_attributes.rb +67 -67
  54. data/test/test_update.rb +96 -96
  55. metadata +12 -11
@@ -1,19 +1,19 @@
1
- module ActiveModel
2
- module AttributeAssignment
3
- def _assign_attribute(k, v)
4
- # CPK. This is super ugly, but if a table has a composite key where one of the fields is named :id we need
5
- # to handle it as a single value. Otherwise, we would call the id=(value) method which is expecting
6
- # and array of values.
7
- if k == 'id' && self.kind_of?(ActiveRecord::Base) && self.composite? && !self.column_for_attribute(k).null
8
- self._write_attribute(k, v)
9
- else
10
- setter = :"#{k}="
11
- if respond_to?(setter)
12
- public_send(setter, v)
13
- else
14
- raise UnknownAttributeError.new(self, k)
15
- end
16
- end
17
- end
18
- end
19
- end
1
+ module ActiveModel
2
+ module AttributeAssignment
3
+ def _assign_attribute(k, v)
4
+ # CPK. This is super ugly, but if a table has a composite key where one of the fields is named :id we need
5
+ # to handle it as a single value. Otherwise, we would call the id=(value) method which is expecting
6
+ # and array of values.
7
+ if k == 'id' && !v.kind_of?(Array) && self.kind_of?(ActiveRecord::Base) && self.composite? && !self.column_for_attribute(k).null
8
+ self._write_attribute(k, v)
9
+ else
10
+ setter = :"#{k}="
11
+ if respond_to?(setter)
12
+ public_send(setter, v)
13
+ else
14
+ raise UnknownAttributeError.new(self, k)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,69 +1,67 @@
1
- module ActiveRecord
2
- module Associations
3
- class AssociationScope
4
- def self.get_bind_values(owner, chain)
5
- binds = []
6
- last_reflection = chain.last
7
-
8
- # CPK
9
- # binds << last_reflection.join_id_for(owner)
10
- values = last_reflection.join_id_for(owner)
11
- binds += Array(values)
12
-
13
- if last_reflection.type
14
- binds << owner.class.polymorphic_name
15
- end
16
-
17
- chain.each_cons(2).each do |reflection, next_reflection|
18
- if reflection.type
19
- binds << next_reflection.klass.polymorphic_name
20
- end
21
- end
22
- binds
23
- end
24
-
25
- def last_chain_scope(scope, reflection, owner)
26
- join_keys = reflection.join_keys
27
- key = join_keys.key
28
- foreign_key = join_keys.foreign_key
29
-
30
- table = reflection.aliased_table
31
-
32
- # CPK
33
- # value = transform_value(owner[foreign_key])
34
- # scope = apply_scope(scope, table, key, value)
35
- Array(key).zip(Array(foreign_key)).each do |a_join_key, a_foreign_key|
36
- value = transform_value(owner[a_foreign_key])
37
- scope = apply_scope(scope, table, a_join_key, value)
38
- end
39
-
40
- if reflection.type
41
- polymorphic_type = transform_value(owner.class.polymorphic_name)
42
- scope = apply_scope(scope, table, reflection.type, polymorphic_type)
43
- end
44
-
45
- scope
46
- end
47
-
48
- def next_chain_scope(scope, reflection, next_reflection)
49
- join_keys = reflection.join_keys
50
- key = join_keys.key
51
- foreign_key = join_keys.foreign_key
52
-
53
- table = reflection.aliased_table
54
- foreign_table = next_reflection.aliased_table
55
-
56
- # CPK
57
- # constraint = table[key].eq(foreign_table[foreign_key])
58
- constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
59
-
60
- if reflection.type
61
- value = transform_value(next_reflection.klass.polymorphic_name)
62
- scope = apply_scope(scope, table, reflection.type, value)
63
- end
64
-
65
- scope.joins!(join(foreign_table, constraint))
66
- end
67
- end
68
- end
1
+ module ActiveRecord
2
+ module Associations
3
+ class AssociationScope
4
+ def self.get_bind_values(owner, chain)
5
+ binds = []
6
+ last_reflection = chain.last
7
+
8
+ # CPK
9
+ # binds << last_reflection.join_id_for(owner)
10
+ values = last_reflection.join_id_for(owner)
11
+ binds += Array(values)
12
+
13
+ if last_reflection.type
14
+ binds << owner.class.polymorphic_name
15
+ end
16
+
17
+ chain.each_cons(2).each do |reflection, next_reflection|
18
+ if reflection.type
19
+ binds << next_reflection.klass.polymorphic_name
20
+ end
21
+ end
22
+ binds
23
+ end
24
+
25
+ def last_chain_scope(scope, reflection, owner)
26
+ key = reflection.join_primary_key
27
+ foreign_key = reflection.join_foreign_key
28
+
29
+ table = reflection.aliased_table
30
+
31
+ # CPK
32
+ # value = transform_value(owner[foreign_key])
33
+ # scope = apply_scope(scope, table, key, value)
34
+ Array(key).zip(Array(foreign_key)).each do |a_join_key, a_foreign_key|
35
+ value = transform_value(owner[a_foreign_key])
36
+ scope = apply_scope(scope, table, a_join_key, value)
37
+ end
38
+
39
+ if reflection.type
40
+ polymorphic_type = transform_value(owner.class.polymorphic_name)
41
+ scope = apply_scope(scope, table, reflection.type, polymorphic_type)
42
+ end
43
+
44
+ scope
45
+ end
46
+
47
+ def next_chain_scope(scope, reflection, next_reflection)
48
+ key = reflection.join_primary_key
49
+ foreign_key = reflection.join_foreign_key
50
+
51
+ table = reflection.aliased_table
52
+ foreign_table = next_reflection.aliased_table
53
+
54
+ # CPK
55
+ # constraint = table[key].eq(foreign_table[foreign_key])
56
+ constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
57
+
58
+ if reflection.type
59
+ value = transform_value(next_reflection.klass.polymorphic_name)
60
+ scope = apply_scope(scope, table, reflection.type, value)
61
+ end
62
+
63
+ scope.joins!(join(foreign_table, constraint))
64
+ end
65
+ end
66
+ end
69
67
  end
@@ -1,103 +1,118 @@
1
- module ActiveRecord
2
- module Associations
3
- class JoinDependency
4
- class Aliases # :nodoc:
5
- def column_alias(node, column)
6
- # CPK
7
- #@alias_cache[node][column]
8
- if column.kind_of?(Array)
9
- column.map do |a_column|
10
- @alias_cache[node][a_column]
11
- end
12
- else
13
- @alias_cache[node][column]
14
- end
15
- end
16
- end
17
-
18
- def instantiate(result_set, &block)
19
- primary_key = aliases.column_alias(join_root, join_root.primary_key)
20
-
21
- seen = Hash.new { |i, object_id|
22
- i[object_id] = Hash.new { |j, child_class|
23
- j[child_class] = {}
24
- }
25
- }
26
-
27
- model_cache = Hash.new { |h, klass| h[klass] = {} }
28
- parents = model_cache[join_root]
29
- column_aliases = aliases.column_aliases join_root
30
-
31
- message_bus = ActiveSupport::Notifications.instrumenter
32
-
33
- payload = {
34
- record_count: result_set.length,
35
- class_name: join_root.base_klass.name
36
- }
37
-
38
- message_bus.instrument("instantiation.active_record", payload) do
39
- result_set.each { |row_hash|
40
- # CPK
41
- # parent_key = primary_key ? row_hash[primary_key] : row_hash
42
- # CPK
43
- parent_key = if primary_key.kind_of?(Array)
44
- primary_key.map {|key| row_hash[key]}
45
- else
46
- primary_key ? row_hash[primary_key] : row_hash
47
- end
48
-
49
- parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
50
- construct(parent, join_root, row_hash, seen, model_cache)
51
- }
52
- end
53
-
54
- parents.values
55
- end
56
-
57
- def construct(ar_parent, parent, row, seen, model_cache)
58
- return if ar_parent.nil?
59
-
60
- parent.children.each do |node|
61
- if node.reflection.collection?
62
- other = ar_parent.association(node.reflection.name)
63
- other.loaded!
64
- elsif ar_parent.association_cached?(node.reflection.name)
65
- model = ar_parent.association(node.reflection.name).target
66
- construct(model, node, row, seen, model_cache)
67
- next
68
- end
69
-
70
- key = aliases.column_alias(node, node.primary_key)
71
-
72
- # CPK
73
- if key.is_a?(Array)
74
- id = Array(key).map do |column_alias|
75
- row[column_alias]
76
- end
77
- # At least the first value in the key has to be set. Should we require all values to be set?
78
- id = nil if id.first.nil?
79
- else # original
80
- id = row[key]
81
- end
82
-
83
- if id.nil? # duplicating this so it is clear what remained unchanged from the original
84
- nil_association = ar_parent.association(node.reflection.name)
85
- nil_association.loaded!
86
- next
87
- end
88
-
89
- model = seen[ar_parent.object_id][node][id]
90
-
91
- if model
92
- construct(model, node, row, seen, model_cache)
93
- else
94
- model = construct_model(ar_parent, node, row, model_cache, id)
95
-
96
- seen[ar_parent.object_id][node][id] = model
97
- construct(model, node, row, seen, model_cache)
98
- end
99
- end
100
- end
101
- end
102
- end
103
- end
1
+ module ActiveRecord
2
+ module Associations
3
+ class JoinDependency
4
+ class Aliases # :nodoc:
5
+ def column_alias(node, column)
6
+ # CPK
7
+ #@alias_cache[node][column]
8
+ if column.kind_of?(Array)
9
+ column.map do |a_column|
10
+ @alias_cache[node][a_column]
11
+ end
12
+ else
13
+ @alias_cache[node][column]
14
+ end
15
+ end
16
+ end
17
+
18
+ def instantiate(result_set, strict_loading_value, &block)
19
+ primary_key = aliases.column_alias(join_root, join_root.primary_key)
20
+
21
+ seen = Hash.new { |i, parent|
22
+ i[parent] = Hash.new { |j, child_class|
23
+ j[child_class] = {}
24
+ }
25
+ }.compare_by_identity
26
+
27
+ model_cache = Hash.new { |h, klass| h[klass] = {} }
28
+ parents = model_cache[join_root]
29
+
30
+ column_aliases = aliases.column_aliases(join_root)
31
+ column_names = []
32
+
33
+ result_set.columns.each do |name|
34
+ column_names << name unless /\At\d+_r\d+\z/.match?(name)
35
+ end
36
+
37
+ if column_names.empty?
38
+ column_types = {}
39
+ else
40
+ column_types = result_set.column_types
41
+ unless column_types.empty?
42
+ attribute_types = join_root.attribute_types
43
+ column_types = column_types.slice(*column_names).delete_if { |k, _| attribute_types.key?(k) }
44
+ end
45
+ column_aliases += column_names.map! { |name| Aliases::Column.new(name, name) }
46
+ end
47
+
48
+ message_bus = ActiveSupport::Notifications.instrumenter
49
+
50
+ payload = {
51
+ record_count: result_set.length,
52
+ class_name: join_root.base_klass.name
53
+ }
54
+
55
+ message_bus.instrument("instantiation.active_record", payload) do
56
+ result_set.each { |row_hash|
57
+ # CPK
58
+ # parent_key = primary_key ? row_hash[primary_key] : row_hash
59
+ parent_key = if primary_key.kind_of?(Array)
60
+ primary_key.map {|key| row_hash[key]}
61
+ else
62
+ primary_key ? row_hash[primary_key] : row_hash
63
+ end
64
+
65
+ parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, column_types, &block)
66
+ construct(parent, join_root, row_hash, seen, model_cache, strict_loading_value)
67
+ }
68
+ end
69
+
70
+ parents.values
71
+ end
72
+
73
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
74
+ return if ar_parent.nil?
75
+
76
+ parent.children.each do |node|
77
+ if node.reflection.collection?
78
+ other = ar_parent.association(node.reflection.name)
79
+ other.loaded!
80
+ elsif ar_parent.association_cached?(node.reflection.name)
81
+ model = ar_parent.association(node.reflection.name).target
82
+ construct(model, node, row, seen, model_cache, strict_loading_value)
83
+ next
84
+ end
85
+
86
+ key = aliases.column_alias(node, node.primary_key)
87
+ # CPK
88
+ if key.is_a?(Array)
89
+ id = Array(key).map do |column_alias|
90
+ row[column_alias]
91
+ end
92
+ # At least the first value in the key has to be set. Should we require all values to be set?
93
+ id = nil if id.first.nil?
94
+ else # original
95
+ id = row[key]
96
+ end
97
+
98
+ if id.nil?
99
+ nil_association = ar_parent.association(node.reflection.name)
100
+ nil_association.loaded!
101
+ next
102
+ end
103
+
104
+ model = seen[ar_parent][node][id]
105
+
106
+ if model
107
+ construct(model, node, row, seen, model_cache, strict_loading_value)
108
+ else
109
+ model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
110
+
111
+ seen[ar_parent][node][id] = model
112
+ construct(model, node, row, seen, model_cache, strict_loading_value)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -1,9 +1,21 @@
1
- module ActiveRecord
2
- module AttributeMethods
3
- def has_attribute?(attr_name)
4
- # CPK
5
- # @attributes.key?(attr_name.to_s)
6
- Array(attr_name).all? {|single_attr| @attributes.key?(single_attr.to_s) }
7
- end
8
- end
9
- end
1
+ module ActiveRecord
2
+ module AttributeMethods
3
+ def has_attribute?(attr_name)
4
+ # CPK
5
+ # attr_name = attr_name.to_s
6
+ # attr_name = self.class.attribute_aliases[attr_name] || attr_name
7
+ # @attributes.key?(attr_name)
8
+ Array(attr_name).all? do |attr|
9
+ attr = attr.to_s
10
+ attr = self.class.attribute_aliases[attr] || attr
11
+ @attributes.key?(attr)
12
+ end
13
+ end
14
+
15
+ def _has_attribute?(attr_name)
16
+ # CPK
17
+ # @attributes.key?(attr_name)
18
+ Array(attr_name).all? { |attr| @attributes.key?(attr) }
19
+ end
20
+ end
21
+ end
@@ -16,7 +16,6 @@ module ActiveRecord
16
16
 
17
17
  # Returns the primary key previous value.
18
18
  def id_was
19
- sync_with_transaction_state
20
19
  # CPK
21
20
  # attribute_was(self.class.primary_key)
22
21
  if self.composite?
@@ -29,7 +28,6 @@ module ActiveRecord
29
28
  end
30
29
 
31
30
  def id_in_database
32
- sync_with_transaction_state
33
31
  # CPK
34
32
  # attribute_in_database(self.class.primary_key)
35
33
  if self.composite?
@@ -1,30 +1,30 @@
1
- module ActiveRecord
2
- module AttributeMethods
3
- module Read
4
- def read_attribute(attr_name, &block)
5
- # CPK
6
- # name = attr_name.to_s
7
- name = attr_name
8
- if self.class.attribute_alias?(name)
9
- name = self.class.attribute_alias(name)
10
- end
11
-
12
- primary_key = self.class.primary_key
13
- # CPK
14
- # name = primary_key if name == "id" && primary_key
15
- name = primary_key if name == "id" && primary_key && !composite?
16
- sync_with_transaction_state if name == primary_key
17
- _read_attribute(name, &block)
18
- end
19
-
20
- def _read_attribute(attr_name, &block) # :nodoc
21
- # CPK
22
- if attr_name.kind_of?(Array)
23
- attr_name.map {|name| @attributes.fetch_value(name.to_s, &block)}
24
- else
25
- @attributes.fetch_value(attr_name.to_s, &block)
26
- end
27
- end
28
- end
29
- end
30
- end
1
+ module ActiveRecord
2
+ module AttributeMethods
3
+ module Read
4
+ def read_attribute(attr_name, &block)
5
+ # CPK
6
+ # name = attr_name.to_s
7
+ name = attr_name
8
+ if self.class.attribute_alias?(name)
9
+ name = self.class.attribute_alias(name)
10
+ end
11
+
12
+ primary_key = self.class.primary_key
13
+ # CPK
14
+ # name = primary_key if name == "id" && primary_key
15
+ name = primary_key if name == "id" && primary_key && !composite?
16
+
17
+ _read_attribute(name, &block)
18
+ end
19
+
20
+ def _read_attribute(attr_name, &block) # :nodoc
21
+ # CPK
22
+ if attr_name.kind_of?(Array)
23
+ attr_name.map {|name| @attributes.fetch_value(name.to_s, &block)}
24
+ else
25
+ @attributes.fetch_value(attr_name.to_s, &block)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end