composite_primary_keys 12.0.5 → 13.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +883 -862
  3. data/README.rdoc +181 -180
  4. data/lib/composite_primary_keys.rb +119 -118
  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 -22
  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 +16 -8
  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 -19
  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 -180
  49. data/test/test_delete.rb +182 -179
  50. data/test/test_exists.rb +39 -39
  51. data/test/test_find.rb +170 -157
  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 +5 -5
  56. data/lib/composite_primary_keys/connection_adapters/mysql/database_statements.rb +0 -24
@@ -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