composite_primary_keys 12.0.10 → 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.
- checksums.yaml +4 -4
- data/History.rdoc +4 -1
- data/README.rdoc +1 -0
- data/lib/composite_primary_keys.rb +3 -1
- data/lib/composite_primary_keys/associations/association_scope.rb +4 -6
- data/lib/composite_primary_keys/associations/join_dependency.rb +34 -19
- data/lib/composite_primary_keys/attribute_methods.rb +14 -2
- data/lib/composite_primary_keys/attribute_methods/primary_key.rb +0 -2
- data/lib/composite_primary_keys/attribute_methods/read.rb +1 -1
- data/lib/composite_primary_keys/attribute_methods/write.rb +1 -1
- data/lib/composite_primary_keys/nested_attributes.rb +1 -1
- data/lib/composite_primary_keys/reflection.rb +64 -2
- data/lib/composite_primary_keys/relation.rb +13 -9
- data/lib/composite_primary_keys/relation/batches.rb +15 -7
- data/lib/composite_primary_keys/relation/calculations.rb +46 -23
- data/lib/composite_primary_keys/relation/finder_methods.rb +7 -7
- data/lib/composite_primary_keys/relation/predicate_builder/association_query_value.rb +23 -4
- data/lib/composite_primary_keys/relation/query_methods.rb +2 -2
- data/lib/composite_primary_keys/relation/where_clause.rb +6 -11
- data/lib/composite_primary_keys/table_metadata.rb +11 -0
- data/lib/composite_primary_keys/version.rb +2 -2
- data/test/fixtures/departments.yml +4 -0
- data/test/fixtures/employees.yml +6 -1
- data/test/test_associations.rb +6 -0
- data/test/test_attributes.rb +19 -4
- data/test/test_calculations.rb +9 -2
- data/test/test_delete.rb +2 -2
- data/test/test_find.rb +8 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa8acb9d77d6a2de529283cbaa0cbea67daec08813942c8bfa83f39b0bdf299f
|
4
|
+
data.tar.gz: deb46f06053d8a07b19903a46b3d30e3ae82699d265f339c5463e810900416b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d31a495089eafc1cced865e0cd55e6e7a09698b124f30c7d18a754aa396c611f85abcfcf32af55e5c23534432d0f74c7eee5f1d674574c0a0b0c937d61fb16fd
|
7
|
+
data.tar.gz: ab7776729d53de9d12c08bb0035c256ef1d4cc00dab2b7be029a4c610c6cd36b6e1397160ebde56d7acaecc29817b1456e83b482a6a7f33c0a5ac15c6ba715e2
|
data/History.rdoc
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
== 13.0.0
|
2
|
+
* Update to ActiveRecord 6.1 (Javier Julio, Charlie Savage, Sammy Larbi)
|
3
|
+
|
1
4
|
== 12.0.10 (2021-05-09)
|
2
5
|
* Support using the #id method on records with primary keys that also have an :id attribute.
|
3
6
|
|
@@ -514,7 +517,7 @@ by replacing quoted identifiers with all-caps equivalents on Oracle (Rhett Sutph
|
|
514
517
|
* Update Oracle tests (Rhett Sutphin)
|
515
518
|
|
516
519
|
== 4.1.1 2011-08-31
|
517
|
-
* Support for AR 3.1.1
|
520
|
+
* Support for AR 3.1.1
|
518
521
|
* Make polymorphic belongs_to work in rails 3.1.1 (Tom Hughes)
|
519
522
|
* Eliminate relative paths from the test suite (Rhett Sutphin)
|
520
523
|
* Minor improvements to the CPK test runner w/o relative path changes (Rhett Sutphin)
|
data/README.rdoc
CHANGED
@@ -20,6 +20,7 @@ Every major version of ActiveRecord has included numerous internal changes. As
|
|
20
20
|
CPK has to be rewritten for each version of ActiveRecord. To help keep
|
21
21
|
things straight, here is the mapping:
|
22
22
|
|
23
|
+
Version 13.x is designed to work with ActiveRecord 6.1.x
|
23
24
|
Version 12.x is designed to work with ActiveRecord 6.0.x
|
24
25
|
Version 11.x is designed to work with ActiveRecord 5.2.x
|
25
26
|
Version 10.x is designed to work with ActiveRecord 5.1.x
|
@@ -23,7 +23,7 @@
|
|
23
23
|
|
24
24
|
unless defined?(ActiveRecord)
|
25
25
|
require 'rubygems'
|
26
|
-
gem 'activerecord', '~>6.
|
26
|
+
gem 'activerecord', '~>6.1.0'
|
27
27
|
require 'active_record'
|
28
28
|
end
|
29
29
|
|
@@ -64,6 +64,7 @@ require 'active_record/connection_adapters/abstract_adapter'
|
|
64
64
|
require 'active_record/connection_adapters/postgresql/database_statements'
|
65
65
|
|
66
66
|
require 'active_record/relation/where_clause'
|
67
|
+
require 'active_record/table_metadata'
|
67
68
|
|
68
69
|
# CPK overrides
|
69
70
|
require_relative 'composite_primary_keys/active_model/attribute_assignment'
|
@@ -115,3 +116,4 @@ require_relative 'composite_primary_keys/composite_relation'
|
|
115
116
|
|
116
117
|
require_relative 'composite_primary_keys/arel/to_sql'
|
117
118
|
require_relative 'composite_primary_keys/arel/sqlserver'
|
119
|
+
require_relative 'composite_primary_keys/table_metadata'
|
@@ -23,9 +23,8 @@ module ActiveRecord
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def last_chain_scope(scope, reflection, owner)
|
26
|
-
|
27
|
-
|
28
|
-
foreign_key = join_keys.foreign_key
|
26
|
+
key = reflection.join_primary_key
|
27
|
+
foreign_key = reflection.join_foreign_key
|
29
28
|
|
30
29
|
table = reflection.aliased_table
|
31
30
|
|
@@ -46,9 +45,8 @@ module ActiveRecord
|
|
46
45
|
end
|
47
46
|
|
48
47
|
def next_chain_scope(scope, reflection, next_reflection)
|
49
|
-
|
50
|
-
|
51
|
-
foreign_key = join_keys.foreign_key
|
48
|
+
key = reflection.join_primary_key
|
49
|
+
foreign_key = reflection.join_foreign_key
|
52
50
|
|
53
51
|
table = reflection.aliased_table
|
54
52
|
foreign_table = next_reflection.aliased_table
|
@@ -15,46 +15,62 @@ module ActiveRecord
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def instantiate(result_set, &block)
|
18
|
+
def instantiate(result_set, strict_loading_value, &block)
|
19
19
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
20
20
|
|
21
|
-
seen = Hash.new { |i,
|
22
|
-
i[
|
21
|
+
seen = Hash.new { |i, parent|
|
22
|
+
i[parent] = Hash.new { |j, child_class|
|
23
23
|
j[child_class] = {}
|
24
24
|
}
|
25
|
-
}
|
25
|
+
}.compare_by_identity
|
26
26
|
|
27
27
|
model_cache = Hash.new { |h, klass| h[klass] = {} }
|
28
28
|
parents = model_cache[join_root]
|
29
|
-
|
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
|
30
47
|
|
31
48
|
message_bus = ActiveSupport::Notifications.instrumenter
|
32
49
|
|
33
50
|
payload = {
|
34
|
-
|
35
|
-
|
51
|
+
record_count: result_set.length,
|
52
|
+
class_name: join_root.base_klass.name
|
36
53
|
}
|
37
54
|
|
38
55
|
message_bus.instrument("instantiation.active_record", payload) do
|
39
56
|
result_set.each { |row_hash|
|
40
57
|
# CPK
|
41
58
|
# parent_key = primary_key ? row_hash[primary_key] : row_hash
|
42
|
-
# CPK
|
43
59
|
parent_key = if primary_key.kind_of?(Array)
|
44
60
|
primary_key.map {|key| row_hash[key]}
|
45
61
|
else
|
46
62
|
primary_key ? row_hash[primary_key] : row_hash
|
47
63
|
end
|
48
64
|
|
49
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
50
|
-
construct(parent, join_root, row_hash, seen, model_cache)
|
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)
|
51
67
|
}
|
52
68
|
end
|
53
69
|
|
54
70
|
parents.values
|
55
71
|
end
|
56
72
|
|
57
|
-
def construct(ar_parent, parent, row, seen, model_cache)
|
73
|
+
def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
|
58
74
|
return if ar_parent.nil?
|
59
75
|
|
60
76
|
parent.children.each do |node|
|
@@ -63,12 +79,11 @@ module ActiveRecord
|
|
63
79
|
other.loaded!
|
64
80
|
elsif ar_parent.association_cached?(node.reflection.name)
|
65
81
|
model = ar_parent.association(node.reflection.name).target
|
66
|
-
construct(model, node, row, seen, model_cache)
|
82
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
67
83
|
next
|
68
84
|
end
|
69
85
|
|
70
86
|
key = aliases.column_alias(node, node.primary_key)
|
71
|
-
|
72
87
|
# CPK
|
73
88
|
if key.is_a?(Array)
|
74
89
|
id = Array(key).map do |column_alias|
|
@@ -80,21 +95,21 @@ module ActiveRecord
|
|
80
95
|
id = row[key]
|
81
96
|
end
|
82
97
|
|
83
|
-
if id.nil?
|
98
|
+
if id.nil?
|
84
99
|
nil_association = ar_parent.association(node.reflection.name)
|
85
100
|
nil_association.loaded!
|
86
101
|
next
|
87
102
|
end
|
88
103
|
|
89
|
-
model = seen[ar_parent
|
104
|
+
model = seen[ar_parent][node][id]
|
90
105
|
|
91
106
|
if model
|
92
|
-
construct(model, node, row, seen, model_cache)
|
107
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
93
108
|
else
|
94
|
-
model = construct_model(ar_parent, node, row, model_cache, id)
|
109
|
+
model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
|
95
110
|
|
96
|
-
seen[ar_parent
|
97
|
-
construct(model, node, row, seen, model_cache)
|
111
|
+
seen[ar_parent][node][id] = model
|
112
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
98
113
|
end
|
99
114
|
end
|
100
115
|
end
|
@@ -2,8 +2,20 @@ module ActiveRecord
|
|
2
2
|
module AttributeMethods
|
3
3
|
def has_attribute?(attr_name)
|
4
4
|
# CPK
|
5
|
-
#
|
6
|
-
|
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) }
|
7
19
|
end
|
8
20
|
end
|
9
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?
|
@@ -70,7 +70,7 @@ module ActiveRecord
|
|
70
70
|
if target_record
|
71
71
|
existing_record = target_record
|
72
72
|
else
|
73
|
-
association.add_to_target(existing_record, :
|
73
|
+
association.add_to_target(existing_record, skip_callbacks: true)
|
74
74
|
end
|
75
75
|
|
76
76
|
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
@@ -6,8 +6,8 @@ module ActiveRecord
|
|
6
6
|
scope_chain_items = join_scopes(table, predicate_builder)
|
7
7
|
klass_scope = klass_join_scope(table, predicate_builder)
|
8
8
|
|
9
|
-
key =
|
10
|
-
foreign_key =
|
9
|
+
key = join_primary_key
|
10
|
+
foreign_key = join_foreign_key
|
11
11
|
|
12
12
|
# CPK
|
13
13
|
#klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
|
@@ -25,5 +25,67 @@ module ActiveRecord
|
|
25
25
|
scope_chain_items.inject(klass_scope, &:merge!)
|
26
26
|
end
|
27
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
|
28
90
|
end
|
29
91
|
end
|
@@ -25,13 +25,15 @@ module ActiveRecord
|
|
25
25
|
end
|
26
26
|
|
27
27
|
stmt = Arel::UpdateManager.new
|
28
|
+
stmt.table(arel.join_sources.empty? ? table : arel.source)
|
29
|
+
stmt.key = table[primary_key]
|
30
|
+
|
28
31
|
# CPK
|
29
|
-
if @klass.composite?
|
32
|
+
if @klass.composite? && stmt.to_sql =~ /['"]#{primary_key.to_s}['"]/
|
33
|
+
stmt = Arel::UpdateManager.new
|
30
34
|
stmt.table(arel_table)
|
31
35
|
cpk_subquery(stmt)
|
32
36
|
else
|
33
|
-
stmt.table(arel.join_sources.empty? ? table : arel.source)
|
34
|
-
stmt.key = arel_attribute(primary_key)
|
35
37
|
stmt.wheres = arel.constraints
|
36
38
|
end
|
37
39
|
stmt.take(arel.limit)
|
@@ -42,7 +44,7 @@ module ActiveRecord
|
|
42
44
|
if klass.locking_enabled? &&
|
43
45
|
!updates.key?(klass.locking_column) &&
|
44
46
|
!updates.key?(klass.locking_column.to_sym)
|
45
|
-
attr =
|
47
|
+
attr = table[klass.locking_column]
|
46
48
|
updates[attr.name] = _increment_attribute(attr)
|
47
49
|
end
|
48
50
|
stmt.set _substitute_values(updates)
|
@@ -68,13 +70,15 @@ module ActiveRecord
|
|
68
70
|
end
|
69
71
|
|
70
72
|
stmt = Arel::DeleteManager.new
|
73
|
+
stmt.from(arel.join_sources.empty? ? table : arel.source)
|
74
|
+
stmt.key = table[primary_key]
|
71
75
|
|
72
|
-
|
76
|
+
# CPK
|
77
|
+
if @klass.composite? && stmt.to_sql =~ /['"]#{primary_key.to_s}['"]/
|
78
|
+
stmt = Arel::DeleteManager.new
|
73
79
|
stmt.from(arel_table)
|
74
80
|
cpk_subquery(stmt)
|
75
81
|
else
|
76
|
-
stmt.from(arel.join_sources.empty? ? table : arel.source)
|
77
|
-
stmt.key = arel_attribute(primary_key)
|
78
82
|
stmt.wheres = arel.constraints
|
79
83
|
end
|
80
84
|
|
@@ -138,7 +142,7 @@ module ActiveRecord
|
|
138
142
|
# reference_codes.reference_code = cpk_child.reference_code)
|
139
143
|
def cpk_exists_subquery(stmt)
|
140
144
|
arel_attributes = primary_keys.map do |key|
|
141
|
-
|
145
|
+
table[key]
|
142
146
|
end.to_composite_keys
|
143
147
|
|
144
148
|
# Clone the query
|
@@ -173,7 +177,7 @@ module ActiveRecord
|
|
173
177
|
# FROM `reference_codes`) __active_record_temp)
|
174
178
|
def cpk_mysql_subquery(stmt)
|
175
179
|
arel_attributes = primary_keys.map do |key|
|
176
|
-
|
180
|
+
table[key]
|
177
181
|
end.to_composite_keys
|
178
182
|
|
179
183
|
subselect = arel.clone
|
@@ -1,12 +1,16 @@
|
|
1
1
|
module CompositePrimaryKeys
|
2
2
|
module ActiveRecord
|
3
3
|
module Batches
|
4
|
-
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
|
4
|
+
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :asc)
|
5
5
|
relation = self
|
6
6
|
unless block_given?
|
7
7
|
return ::ActiveRecord::Batches::BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
8
8
|
end
|
9
9
|
|
10
|
+
unless [:asc, :desc].include?(order)
|
11
|
+
raise ArgumentError, ":order must be :asc or :desc, got #{order.inspect}"
|
12
|
+
end
|
13
|
+
|
10
14
|
if arel.orders.present?
|
11
15
|
act_on_ignored_order(error_on_ignore)
|
12
16
|
end
|
@@ -17,8 +21,8 @@ module CompositePrimaryKeys
|
|
17
21
|
batch_limit = remaining if remaining < batch_limit
|
18
22
|
end
|
19
23
|
|
20
|
-
relation = relation.reorder(batch_order).limit(batch_limit)
|
21
|
-
relation = apply_limits(relation, start, finish)
|
24
|
+
relation = relation.reorder(batch_order(order)).limit(batch_limit)
|
25
|
+
relation = apply_limits(relation, start, finish, order)
|
22
26
|
relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
|
23
27
|
batch_relation = relation
|
24
28
|
|
@@ -61,7 +65,9 @@ module CompositePrimaryKeys
|
|
61
65
|
end
|
62
66
|
|
63
67
|
# CPK
|
64
|
-
#
|
68
|
+
#batch_relation = relation.where(
|
69
|
+
# predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
|
70
|
+
#)
|
65
71
|
batch_relation = if composite?
|
66
72
|
# CPK
|
67
73
|
# Lexicographically select records
|
@@ -81,7 +87,9 @@ module CompositePrimaryKeys
|
|
81
87
|
end.reduce(:or)
|
82
88
|
relation.where(query)
|
83
89
|
else
|
84
|
-
relation.where(
|
90
|
+
batch_relation = relation.where(
|
91
|
+
predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
|
92
|
+
)
|
85
93
|
end
|
86
94
|
end
|
87
95
|
end
|
@@ -95,9 +103,9 @@ module CompositePrimaryKeys
|
|
95
103
|
ary.length.times.reduce([]) { |results, i| results << ary[0..i] }
|
96
104
|
end
|
97
105
|
|
98
|
-
def batch_order
|
106
|
+
def batch_order(order)
|
99
107
|
self.primary_key.map do |key|
|
100
|
-
|
108
|
+
table[key].public_send(order)
|
101
109
|
end
|
102
110
|
end
|
103
111
|
end
|
@@ -4,11 +4,12 @@ module CompositePrimaryKeys
|
|
4
4
|
def aggregate_column(column_name)
|
5
5
|
# CPK
|
6
6
|
if column_name.kind_of?(Array)
|
7
|
+
# Note: Test don't seem to run this code?
|
7
8
|
column_name.map do |column|
|
8
|
-
@klass.
|
9
|
+
@klass.arel_table[column]
|
9
10
|
end
|
10
11
|
elsif @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
|
11
|
-
@klass.
|
12
|
+
@klass.arel_table[column_name]
|
12
13
|
else
|
13
14
|
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
14
15
|
end
|
@@ -31,38 +32,33 @@ module CompositePrimaryKeys
|
|
31
32
|
relation = unscope(:order).distinct!(false)
|
32
33
|
|
33
34
|
column = aggregate_column(column_name)
|
34
|
-
|
35
35
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
36
|
-
if operation == "sum" && distinct
|
37
|
-
select_value.distinct = true
|
38
|
-
end
|
36
|
+
select_value.distinct = true if operation == "sum" && distinct
|
39
37
|
|
40
|
-
column_alias = select_value.alias
|
41
|
-
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
42
38
|
relation.select_values = [select_value]
|
43
39
|
|
44
40
|
query_builder = relation.arel
|
45
41
|
end
|
46
42
|
|
47
|
-
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder
|
48
|
-
row = result.first
|
49
|
-
value = row && row.values.first
|
50
|
-
type = result.column_types.fetch(column_alias) do
|
51
|
-
type_for(column_name)
|
52
|
-
end
|
43
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
|
53
44
|
|
54
|
-
type_cast_calculated_value(
|
45
|
+
type_cast_calculated_value(result.cast_values.first, operation) do |value|
|
46
|
+
type = column.try(:type_caster) ||
|
47
|
+
# CPK
|
48
|
+
# lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
49
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || ::ActiveRecord::Type.default_value
|
50
|
+
type.deserialize(value)
|
51
|
+
end
|
55
52
|
end
|
56
53
|
|
57
54
|
def build_count_subquery(relation, column_name, distinct)
|
58
55
|
if column_name == :all
|
56
|
+
column_alias = Arel.star
|
57
|
+
# CPK
|
58
|
+
# relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
59
59
|
relation.select_values = [ Arel.sql(::ActiveRecord::FinderMethods::ONE_AS_ONE) ] unless distinct
|
60
|
-
if relation.select_values.first.is_a?(Array)
|
61
|
-
relation.select_values = relation.select_values.first.map do |column|
|
62
|
-
Arel::Attribute.new(@klass.unscoped.table, column)
|
63
|
-
end
|
64
|
-
end
|
65
60
|
elsif column_name.is_a?(Array)
|
61
|
+
column_alias = Arel.star
|
66
62
|
relation.select_values = column_name.map do |column|
|
67
63
|
Arel::Attribute.new(@klass.unscoped.table, column)
|
68
64
|
end
|
@@ -71,10 +67,37 @@ module CompositePrimaryKeys
|
|
71
67
|
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
72
68
|
end
|
73
69
|
|
74
|
-
|
75
|
-
select_value = operation_over_aggregate_column(column_alias
|
70
|
+
subquery_alias = Arel.sql("subquery_for_count")
|
71
|
+
select_value = operation_over_aggregate_column(column_alias, "count", false)
|
72
|
+
|
73
|
+
relation.build_subquery(subquery_alias, select_value)
|
74
|
+
end
|
75
|
+
|
76
|
+
def calculate(operation, column_name)
|
77
|
+
if has_include?(column_name)
|
78
|
+
relation = apply_join_dependency
|
76
79
|
|
77
|
-
|
80
|
+
if operation.to_s.downcase == "count"
|
81
|
+
unless distinct_value || distinct_select?(column_name || select_for_count)
|
82
|
+
relation.distinct!
|
83
|
+
# CPK
|
84
|
+
# relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
85
|
+
if klass.primary_key.present? && klass.primary_key.is_a?(Array)
|
86
|
+
relation.select_values = klass.primary_key.map do |k|
|
87
|
+
"#{connection.quote_table_name(klass.table_name)}.#{connection.quote_column_name(k)}"
|
88
|
+
end
|
89
|
+
else
|
90
|
+
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
94
|
+
relation.order_values = [] if group_values.empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
relation.calculate(operation, column_name)
|
98
|
+
else
|
99
|
+
perform_calculation(operation, column_name)
|
100
|
+
end
|
78
101
|
end
|
79
102
|
end
|
80
103
|
end
|
@@ -26,12 +26,12 @@ module CompositePrimaryKeys
|
|
26
26
|
def limited_ids_for(relation)
|
27
27
|
# CPK
|
28
28
|
# values = @klass.connection.columns_for_distinct(
|
29
|
-
# connection.column_name_from_arel_node(
|
29
|
+
# connection.column_name_from_arel_node(table[primary_key]),
|
30
30
|
# relation.order_values
|
31
31
|
# )
|
32
32
|
|
33
33
|
columns = @klass.primary_keys.map do |key|
|
34
|
-
connection.visitor.compile(
|
34
|
+
connection.visitor.compile(table[key])
|
35
35
|
end
|
36
36
|
values = @klass.connection.columns_for_distinct(columns, relation.order_values)
|
37
37
|
|
@@ -110,12 +110,12 @@ module CompositePrimaryKeys
|
|
110
110
|
#
|
111
111
|
# result = limit(limit || 1)
|
112
112
|
# # CPK
|
113
|
-
# # result.order!(
|
113
|
+
# # result.order!(table[primary_key]) if order_values.empty? && primary_key
|
114
114
|
# if order_values.empty? && primary_key
|
115
115
|
# if composite?
|
116
|
-
# result.order!(primary_keys.map { |pk|
|
116
|
+
# result.order!(primary_keys.map { |pk| table[pk].asc })
|
117
117
|
# elsif
|
118
|
-
# result.order!(
|
118
|
+
# result.order!(table[primary_key])
|
119
119
|
# end
|
120
120
|
# end
|
121
121
|
#
|
@@ -224,8 +224,8 @@ module CompositePrimaryKeys
|
|
224
224
|
def ordered_relation
|
225
225
|
if order_values.empty? && (implicit_order_column || primary_key)
|
226
226
|
# CPK
|
227
|
-
# order(
|
228
|
-
order(Array(implicit_order_column || primary_key).map {|key|
|
227
|
+
# order(table[implicit_order_column || primary_key].asc)
|
228
|
+
order(Array(implicit_order_column || primary_key).map {|key| table[key].asc})
|
229
229
|
else
|
230
230
|
self
|
231
231
|
end
|
@@ -3,16 +3,35 @@ module ActiveRecord
|
|
3
3
|
class AssociationQueryValue
|
4
4
|
def queries
|
5
5
|
# CPK
|
6
|
-
if associated_table.
|
6
|
+
if associated_table.join_foreign_key.is_a?(Array)
|
7
7
|
if ids.is_a?(ActiveRecord::Relation)
|
8
8
|
ids.map do |id|
|
9
|
-
associated_table.
|
9
|
+
associated_table.join_foreign_key.zip(id.id).to_h
|
10
10
|
end
|
11
11
|
else
|
12
|
-
[associated_table.
|
12
|
+
[associated_table.join_foreign_key.zip(ids).to_h]
|
13
13
|
end
|
14
14
|
else
|
15
|
-
[associated_table.
|
15
|
+
[associated_table.join_foreign_key => ids]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def ids
|
20
|
+
case value
|
21
|
+
when Relation
|
22
|
+
value.select_values.empty? ? value.select(primary_key) : value
|
23
|
+
when Array
|
24
|
+
value.map { |v| convert_to_id(v) }
|
25
|
+
else
|
26
|
+
# CPK
|
27
|
+
# convert_to_id(value)
|
28
|
+
if value.nil?
|
29
|
+
nil
|
30
|
+
elsif value.respond_to?(:composite?) && value.composite?
|
31
|
+
value.class.primary_keys.zip(value.id)
|
32
|
+
else
|
33
|
+
convert_to_id(value)
|
34
|
+
end
|
16
35
|
end
|
17
36
|
end
|
18
37
|
end
|
@@ -4,12 +4,12 @@ module CompositePrimaryKeys
|
|
4
4
|
def reverse_sql_order(order_query)
|
5
5
|
if order_query.empty?
|
6
6
|
# CPK
|
7
|
-
# return [
|
7
|
+
# return [table[primary_key].desc] if primary_key
|
8
8
|
|
9
9
|
if primary_key
|
10
10
|
# break apart CPKs
|
11
11
|
return primary_key.map do |key|
|
12
|
-
|
12
|
+
table[key].desc
|
13
13
|
end
|
14
14
|
else
|
15
15
|
raise IrreversibleOrderError,
|
@@ -1,23 +1,18 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class Relation
|
3
3
|
class WhereClause
|
4
|
-
def to_h(table_name = nil)
|
5
|
-
equalities = equalities(predicates)
|
4
|
+
def to_h(table_name = nil, equality_only: false)
|
5
|
+
equalities = equalities(predicates, equality_only)
|
6
6
|
|
7
7
|
# CPK Adds this line, because ours are coming in with AND->{EQUALITY, EQUALITY}
|
8
8
|
equalities = predicates.grep(Arel::Nodes::And).map(&:children).flatten.grep(Arel::Nodes::Equality) if equalities.empty?
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
node.left.relation.name == table_name
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
equalities.map { |node|
|
10
|
+
equalities.each_with_object({}) do |node, hash|
|
11
|
+
next if table_name&.!= node.left.relation.name
|
17
12
|
name = node.left.name.to_s
|
18
13
|
value = extract_node_value(node.right)
|
19
|
-
[name
|
20
|
-
|
14
|
+
hash[name] = value
|
15
|
+
end
|
21
16
|
end
|
22
17
|
end
|
23
18
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class TableMetadata # :nodoc:
|
5
|
+
def associated_with?(table_name)
|
6
|
+
# CPK
|
7
|
+
# klass&._reflect_on_association(table_name) || klass&._reflect_on_association(table_name.singularize)
|
8
|
+
klass&._reflect_on_association(table_name) || klass&._reflect_on_association(table_name.to_s.singularize)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/test/fixtures/employees.yml
CHANGED
data/test/test_associations.rb
CHANGED
@@ -355,4 +355,10 @@ class TestAssociations < ActiveSupport::TestCase
|
|
355
355
|
article.reading_ids = Reading.pluck(:id)
|
356
356
|
assert_equal article.reading_ids, Reading.pluck(:id)
|
357
357
|
end
|
358
|
+
|
359
|
+
def test_find_by_association
|
360
|
+
assert_equal Membership.where(user: '1').count, 1
|
361
|
+
assert_equal Membership.where(user_id: '1').count, 1
|
362
|
+
assert_equal Membership.where(user: User.find(1)).count, 1
|
363
|
+
end
|
358
364
|
end
|
data/test/test_attributes.rb
CHANGED
@@ -2,28 +2,32 @@ require File.expand_path('../abstract_unit', __FILE__)
|
|
2
2
|
|
3
3
|
class TestAttributes < ActiveSupport::TestCase
|
4
4
|
fixtures :reference_types, :reference_codes, :products, :tariffs, :product_tariffs
|
5
|
-
|
5
|
+
|
6
6
|
CLASSES = {
|
7
7
|
:single => {
|
8
8
|
:class => ReferenceType,
|
9
9
|
:primary_keys => :reference_type_id,
|
10
10
|
},
|
11
|
-
:dual => {
|
11
|
+
:dual => {
|
12
12
|
:class => ReferenceCode,
|
13
13
|
:primary_keys => [:reference_type_id, :reference_code],
|
14
14
|
},
|
15
15
|
}
|
16
|
-
|
16
|
+
|
17
17
|
def setup
|
18
18
|
self.class.classes = CLASSES
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def test_brackets
|
22
|
+
tested_at_least_on_attribute = false
|
22
23
|
testing_with do
|
23
24
|
@first.attributes.each_pair do |attr_name, value|
|
25
|
+
next if value.nil?
|
24
26
|
assert_equal value, @first[attr_name]
|
27
|
+
tested_at_least_on_attribute = true
|
25
28
|
end
|
26
29
|
end
|
30
|
+
assert tested_at_least_on_attribute
|
27
31
|
end
|
28
32
|
|
29
33
|
def test_brackets_primary_key
|
@@ -49,6 +53,17 @@ class TestAttributes < ActiveSupport::TestCase
|
|
49
53
|
compare_indexes(tarrif, tarrif.class.primary_key, product_tariff, [:tariff_id, :tariff_start_date])
|
50
54
|
end
|
51
55
|
|
56
|
+
def test_has_attribute
|
57
|
+
tariff = tariffs(:flat)
|
58
|
+
assert(tariff.has_attribute?([:tariff_id, :start_date]))
|
59
|
+
assert(tariff.has_attribute?(['tariff_id', 'start_date']))
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_has__attribute
|
63
|
+
tariff = tariffs(:flat)
|
64
|
+
assert(tariff._has_attribute?(['tariff_id', 'start_date']))
|
65
|
+
end
|
66
|
+
|
52
67
|
private
|
53
68
|
|
54
69
|
def compare_indexes(obj1, indexes1, obj2, indexes2)
|
data/test/test_calculations.rb
CHANGED
@@ -3,7 +3,7 @@ require File.expand_path('../abstract_unit', __FILE__)
|
|
3
3
|
class TestCalculations < ActiveSupport::TestCase
|
4
4
|
fixtures :articles, :products, :tariffs, :product_tariffs, :suburbs, :streets, :restaurants,
|
5
5
|
:dorms, :rooms, :room_attributes, :room_attribute_assignments, :students, :room_assignments, :users, :readings,
|
6
|
-
:departments, :employees, :memberships, :membership_statuses
|
6
|
+
:departments, :employees, :memberships, :membership_statuses, :reference_codes, :reference_types
|
7
7
|
|
8
8
|
def test_count
|
9
9
|
assert_equal(3, Product.includes(:product_tariffs).count)
|
@@ -19,7 +19,14 @@ class TestCalculations < ActiveSupport::TestCase
|
|
19
19
|
product = products(:first_product)
|
20
20
|
assert_equal(1, product.product_tariffs.select('tariff_start_date').distinct.count)
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
|
+
def test_count_on_joined_relations_that_have_column_names_in_common
|
24
|
+
count_without_includes = ReferenceCode.count
|
25
|
+
count_with_includes = ReferenceCode.includes(:reference_type).references(:reference_type).count
|
26
|
+
assert_equal(count_without_includes, count_with_includes)
|
27
|
+
assert_equal(5, count_with_includes)
|
28
|
+
end
|
29
|
+
|
23
30
|
def test_count_not_distinct
|
24
31
|
product = products(:first_product)
|
25
32
|
assert_equal(2, product.product_tariffs.select('tariff_start_date').count)
|
data/test/test_delete.rb
CHANGED
@@ -33,13 +33,13 @@ class TestDelete < ActiveSupport::TestCase
|
|
33
33
|
def test_delete_all_with_join
|
34
34
|
employee = employees(:mindy)
|
35
35
|
|
36
|
-
assert_equal(
|
36
|
+
assert_equal(5, Department.count)
|
37
37
|
|
38
38
|
deleted = Department.joins(:employees).
|
39
39
|
where('employees.name = ?', employee.name).
|
40
40
|
delete_all
|
41
41
|
|
42
|
-
assert_equal(
|
42
|
+
assert_equal(4, Department.count)
|
43
43
|
assert_equal(1, deleted)
|
44
44
|
end
|
45
45
|
|
data/test/test_find.rb
CHANGED
@@ -129,13 +129,19 @@ class TestFind < ActiveSupport::TestCase
|
|
129
129
|
def test_find_by_all_associations
|
130
130
|
departments = Department.all
|
131
131
|
employees = Employee.where(:department => departments)
|
132
|
-
assert_equal(
|
132
|
+
assert_equal(6, employees.to_a.count)
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_find_by_some_associations
|
136
|
+
departments = Department.where(location_id: 1)
|
137
|
+
employees = Employee.where(:department => departments)
|
138
|
+
assert_equal(4, employees.to_a.count)
|
133
139
|
end
|
134
140
|
|
135
141
|
def test_expand_all
|
136
142
|
departments = Department.all
|
137
143
|
employees = Employee.where(:department => departments)
|
138
|
-
assert_equal(
|
144
|
+
assert_equal(6, employees.count)
|
139
145
|
end
|
140
146
|
|
141
147
|
def test_find_one_with_params_id
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: composite_primary_keys
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 13.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charlie Savage
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.
|
19
|
+
version: 6.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 6.
|
26
|
+
version: 6.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/composite_primary_keys/relation/query_methods.rb
|
88
88
|
- lib/composite_primary_keys/relation/where_clause.rb
|
89
89
|
- lib/composite_primary_keys/sanitization.rb
|
90
|
+
- lib/composite_primary_keys/table_metadata.rb
|
90
91
|
- lib/composite_primary_keys/transactions.rb
|
91
92
|
- lib/composite_primary_keys/validations/uniqueness.rb
|
92
93
|
- lib/composite_primary_keys/version.rb
|