composite_primary_keys 11.1.0 → 11.2.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 +7 -0
- data/lib/composite_primary_keys.rb +4 -4
- data/lib/composite_primary_keys/associations/has_many_through_association.rb +0 -59
- data/lib/composite_primary_keys/associations/preloader/association.rb +1 -1
- data/lib/composite_primary_keys/associations/through_association.rb +24 -0
- data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +17 -0
- data/lib/composite_primary_keys/persistence.rb +20 -0
- data/lib/composite_primary_keys/relation/batches.rb +24 -4
- data/lib/composite_primary_keys/version.rb +1 -1
- data/test/fixtures/db_definitions/postgresql.sql +2 -2
- data/test/fixtures/db_definitions/sqlite.sql +13 -13
- data/test/test_associations.rb +14 -0
- data/test/test_create.rb +11 -0
- data/test/test_delete.rb +13 -0
- data/test/test_preload.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76e658e30475707dc7495c8f5f5647c18ad0a67edc3cc5787a312a1785418524
|
4
|
+
data.tar.gz: f824d0c22fb21ec5d7d8311047433a6661cb5c134549ee46460e6c693b0cbe4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0721b3f2dcb649fd585baf7facf0747d73edf8929e1f7c2ef9b4522ce3824b23d94cec7caafd810d7ea77e03043ee3cc31abfd6dca16eb012556be147c634fe0
|
7
|
+
data.tar.gz: 40282b497748d0a9eda0cc461c38c4dc91714560022fa37e7466e085a65ea2bd53b85f50cd5845560a2ab43e0c24544b76faf493457f9f9f166ce3761b420285
|
data/History.rdoc
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 11.2.0 (2019-03-16)
|
2
|
+
* When creating new records, honor composite key autoincrementing fields if possible (Antti Pitkänen)
|
3
|
+
* Update Association#run to more closely match ActiveRecord (Fabian Mersch)
|
4
|
+
* Updates needed due to changes in ActiveRecord (Sammy Larbi)
|
5
|
+
* Fix construct_join_attributes method (Brad Slaughter)
|
6
|
+
* Fix destroy_all on HABTM (Brad Slaughter)
|
7
|
+
|
1
8
|
== 11.1.0 (2018-11-14)
|
2
9
|
* Support ActiveRecord 5.2.1 (S1Artem Tselovalnikov, Caleb Buxton, Charlie Savage)
|
3
10
|
* Fix counter cache (Tomonori Murakami)
|
@@ -50,21 +50,19 @@ require 'active_record/associations/join_dependency'
|
|
50
50
|
require 'active_record/associations/preloader/association'
|
51
51
|
require 'active_record/associations/singular_association'
|
52
52
|
require 'active_record/associations/collection_association'
|
53
|
+
require 'active_record/associations/through_association'
|
53
54
|
|
54
55
|
require 'active_record/attribute_methods/primary_key'
|
55
56
|
require 'active_record/attribute_methods/read'
|
56
57
|
require 'active_record/attribute_methods/write'
|
57
58
|
require 'active_record/nested_attributes'
|
58
59
|
|
60
|
+
require 'active_record/connection_adapters/abstract/database_statements'
|
59
61
|
require 'active_record/connection_adapters/abstract_adapter'
|
60
62
|
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
61
63
|
require 'active_record/connection_adapters/postgresql/database_statements'
|
62
64
|
|
63
|
-
require 'active_record/relation/batches'
|
64
65
|
require 'active_record/relation/where_clause'
|
65
|
-
require 'active_record/relation/calculations'
|
66
|
-
require 'active_record/relation/finder_methods'
|
67
|
-
require 'active_record/relation/query_methods'
|
68
66
|
require 'active_record/relation/predicate_builder/association_query_value'
|
69
67
|
|
70
68
|
require 'active_record/validations/uniqueness'
|
@@ -92,12 +90,14 @@ require 'composite_primary_keys/associations/has_many_through_association'
|
|
92
90
|
require 'composite_primary_keys/associations/join_dependency'
|
93
91
|
require 'composite_primary_keys/associations/preloader/association'
|
94
92
|
require 'composite_primary_keys/associations/collection_association'
|
93
|
+
require 'composite_primary_keys/associations/through_association'
|
95
94
|
|
96
95
|
require 'composite_primary_keys/attribute_methods/primary_key'
|
97
96
|
require 'composite_primary_keys/attribute_methods/read'
|
98
97
|
require 'composite_primary_keys/attribute_methods/write'
|
99
98
|
require 'composite_primary_keys/nested_attributes'
|
100
99
|
|
100
|
+
require 'composite_primary_keys/connection_adapters/abstract/database_statements'
|
101
101
|
require 'composite_primary_keys/connection_adapters/abstract_adapter'
|
102
102
|
require 'composite_primary_keys/connection_adapters/abstract_mysql_adapter'
|
103
103
|
require 'composite_primary_keys/connection_adapters/postgresql/database_statements'
|
@@ -1,66 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class HasManyThroughAssociation
|
4
|
-
def cpk_join_through_predicate(*records)
|
5
|
-
ensure_mutable
|
6
4
|
|
7
|
-
ids = records.map do |record|
|
8
|
-
source_reflection.association_primary_key(reflection.klass).map do |key|
|
9
|
-
record.send(key)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
cpk_in_predicate(through_association.scope.klass.arel_table, source_reflection.foreign_key, ids)
|
14
|
-
end
|
15
|
-
|
16
|
-
def delete_records(records, method)
|
17
|
-
ensure_not_nested
|
18
|
-
|
19
|
-
scope = through_association.scope
|
20
|
-
# CPK
|
21
|
-
# scope.where! construct_join_attributes(*records)
|
22
|
-
if source_reflection.klass.composite?
|
23
|
-
scope.where! cpk_join_through_predicate(*records)
|
24
|
-
else
|
25
|
-
scope.where! construct_join_attributes(*records)
|
26
|
-
end
|
27
|
-
|
28
|
-
case method
|
29
|
-
when :destroy
|
30
|
-
if scope.klass.primary_key
|
31
|
-
count = scope.destroy_all.length
|
32
|
-
else
|
33
|
-
scope.to_a.each do |record|
|
34
|
-
record.run_callbacks :destroy
|
35
|
-
end
|
36
|
-
|
37
|
-
arel = scope.arel
|
38
|
-
|
39
|
-
stmt = Arel::DeleteManager.new arel.engine
|
40
|
-
stmt.from scope.klass.arel_table
|
41
|
-
stmt.wheres = arel.constraints
|
42
|
-
|
43
|
-
count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values)
|
44
|
-
end
|
45
|
-
when :nullify
|
46
|
-
count = scope.update_all(source_reflection.foreign_key => nil)
|
47
|
-
else
|
48
|
-
count = scope.delete_all
|
49
|
-
end
|
50
|
-
|
51
|
-
delete_through_records(records)
|
52
|
-
|
53
|
-
if source_reflection.options[:counter_cache] && method != :destroy
|
54
|
-
counter = source_reflection.counter_cache_column
|
55
|
-
klass.decrement_counter counter, records.map(&:id)
|
56
|
-
end
|
57
|
-
|
58
|
-
if through_reflection.collection? && update_through_counter?(method)
|
59
|
-
update_counter(-count, through_reflection)
|
60
|
-
end
|
61
|
-
|
62
|
-
update_counter(-count)
|
63
|
-
end
|
64
5
|
|
65
6
|
def through_records_for(record)
|
66
7
|
# CPK
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
module ThroughAssociation
|
4
|
+
alias :original_construct_join_attributes :construct_join_attributes
|
5
|
+
|
6
|
+
def construct_join_attributes(*records)
|
7
|
+
# CPK
|
8
|
+
if source_reflection.klass.composite?
|
9
|
+
ensure_mutable
|
10
|
+
|
11
|
+
ids = records.map do |record|
|
12
|
+
source_reflection.association_primary_key(reflection.klass).map do |key|
|
13
|
+
record.send(key)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
cpk_in_predicate(through_association.scope.klass.arel_table, source_reflection.foreign_key, ids)
|
18
|
+
else
|
19
|
+
original_construct_join_attributes(*records)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module DatabaseStatements
|
4
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
5
|
+
sql, binds = to_sql_and_binds(arel, binds)
|
6
|
+
value = exec_insert(sql, name, binds, pk, sequence_name)
|
7
|
+
|
8
|
+
# CPK
|
9
|
+
if value && pk.is_a?(Array)
|
10
|
+
id_value || value.rows.first
|
11
|
+
else
|
12
|
+
id_value || last_inserted_id(value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -56,5 +56,25 @@ module ActiveRecord
|
|
56
56
|
connection.delete(dm, "#{self} Destroy")
|
57
57
|
end
|
58
58
|
end
|
59
|
+
|
60
|
+
def _create_record(attribute_names = self.attribute_names)
|
61
|
+
attribute_names &= self.class.column_names
|
62
|
+
attributes_values = attributes_with_values_for_create(attribute_names)
|
63
|
+
|
64
|
+
new_id = self.class._insert_record(attributes_values)
|
65
|
+
|
66
|
+
# CPK
|
67
|
+
if self.composite? && self.id.compact.empty?
|
68
|
+
self.id = new_id
|
69
|
+
else
|
70
|
+
self.id ||= new_id if self.class.primary_key
|
71
|
+
end
|
72
|
+
|
73
|
+
@new_record = false
|
74
|
+
|
75
|
+
yield(self) if block_given?
|
76
|
+
|
77
|
+
id
|
78
|
+
end
|
59
79
|
end
|
60
80
|
end
|
@@ -7,12 +7,19 @@ module CompositePrimaryKeys
|
|
7
7
|
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
8
8
|
end
|
9
9
|
|
10
|
-
if arel.orders.present?
|
11
|
-
|
10
|
+
if arel.orders.present?
|
11
|
+
act_on_ignored_order(error_on_ignore)
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
batch_limit = of
|
15
|
+
if limit_value
|
16
|
+
remaining = limit_value
|
17
|
+
batch_limit = remaining if remaining < batch_limit
|
18
|
+
end
|
19
|
+
|
20
|
+
relation = relation.reorder(batch_order).limit(batch_limit)
|
15
21
|
relation = apply_limits(relation, start, finish)
|
22
|
+
relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
|
16
23
|
batch_relation = relation
|
17
24
|
|
18
25
|
loop do
|
@@ -39,7 +46,20 @@ module CompositePrimaryKeys
|
|
39
46
|
|
40
47
|
yield yielded_relation
|
41
48
|
|
42
|
-
break if ids.length <
|
49
|
+
break if ids.length < batch_limit
|
50
|
+
|
51
|
+
if limit_value
|
52
|
+
remaining -= ids.length
|
53
|
+
|
54
|
+
if remaining == 0
|
55
|
+
# Saves a useless iteration when the limit is a multiple of the
|
56
|
+
# batch size.
|
57
|
+
break
|
58
|
+
elsif remaining < batch_limit
|
59
|
+
relation = relation.limit(remaining)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
43
63
|
# CPK
|
44
64
|
# batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
|
45
65
|
batch_relation = if composite?
|
@@ -20,8 +20,8 @@ create table reference_types (
|
|
20
20
|
);
|
21
21
|
|
22
22
|
create table reference_codes (
|
23
|
-
reference_type_id int
|
24
|
-
reference_code int
|
23
|
+
reference_type_id int,
|
24
|
+
reference_code int not null,
|
25
25
|
code_label varchar(50) default null,
|
26
26
|
abbreviation varchar(50) default null,
|
27
27
|
description varchar(50) default null,
|
@@ -29,14 +29,14 @@ create table reference_codes (
|
|
29
29
|
);
|
30
30
|
|
31
31
|
create table products (
|
32
|
-
id int
|
32
|
+
id int not null primary key,
|
33
33
|
name varchar(50) default null,
|
34
34
|
created_at TIMESTAMP,
|
35
35
|
updated_at TIMESTAMP
|
36
36
|
);
|
37
37
|
|
38
38
|
create table tariffs (
|
39
|
-
tariff_id int
|
39
|
+
tariff_id int not null,
|
40
40
|
start_date date not null,
|
41
41
|
amount integer(11) default null,
|
42
42
|
created_at TIMESTAMP,
|
@@ -45,23 +45,23 @@ create table tariffs (
|
|
45
45
|
);
|
46
46
|
|
47
47
|
create table product_tariffs (
|
48
|
-
product_id int
|
49
|
-
tariff_id int
|
48
|
+
product_id int not null,
|
49
|
+
tariff_id int not null,
|
50
50
|
tariff_start_date date not null,
|
51
51
|
primary key (product_id, tariff_id, tariff_start_date)
|
52
52
|
);
|
53
53
|
|
54
54
|
create table suburbs (
|
55
|
-
city_id int(
|
56
|
-
suburb_id int(
|
55
|
+
city_id int identity(1,1) not null,
|
56
|
+
suburb_id int identity(1,1) not null,
|
57
57
|
name varchar(50) not null,
|
58
58
|
primary key (city_id, suburb_id)
|
59
59
|
);
|
60
60
|
|
61
61
|
create table streets (
|
62
62
|
id integer not null primary key autoincrement,
|
63
|
-
city_id int
|
64
|
-
suburb_id int
|
63
|
+
city_id int not null,
|
64
|
+
suburb_id int not null,
|
65
65
|
name varchar(50) not null
|
66
66
|
);
|
67
67
|
|
@@ -77,9 +77,9 @@ create table articles (
|
|
77
77
|
|
78
78
|
create table readings (
|
79
79
|
id integer not null primary key autoincrement,
|
80
|
-
user_id int
|
81
|
-
article_id int
|
82
|
-
rating int
|
80
|
+
user_id int not null,
|
81
|
+
article_id int not null,
|
82
|
+
rating int not null
|
83
83
|
);
|
84
84
|
|
85
85
|
create table groups (
|
data/test/test_associations.rb
CHANGED
@@ -33,6 +33,11 @@ class TestAssociations < ActiveSupport::TestCase
|
|
33
33
|
assert_equal(3, products.inject(0) {|sum, product| sum + product.product_tariffs.length})
|
34
34
|
end
|
35
35
|
|
36
|
+
def test_find_includes_2
|
37
|
+
products = ProductTariff.where(:tariff_id => 2).order('product_id, tariff_id').includes(:tariff)
|
38
|
+
assert_equal(2, products.length)
|
39
|
+
end
|
40
|
+
|
36
41
|
def test_find_includes_eager_loading
|
37
42
|
product = products(:second_product)
|
38
43
|
product_tarrif = product_tariffs(:second_free)
|
@@ -59,6 +64,15 @@ class TestAssociations < ActiveSupport::TestCase
|
|
59
64
|
assert_equal(3, tariffs.inject(0) {|sum, tariff| sum + tariff.product_tariffs.length})
|
60
65
|
end
|
61
66
|
|
67
|
+
def test_find_each_does_not_throw_error
|
68
|
+
tariffs = Tariff.includes(:product_tariffs)
|
69
|
+
worked = false
|
70
|
+
tariffs.first.product_tariffs.order(:tariff_id).find_each do |pt|
|
71
|
+
worked = true
|
72
|
+
end
|
73
|
+
assert worked
|
74
|
+
end
|
75
|
+
|
62
76
|
def test_association_with_composite_primary_key_can_be_autosaved
|
63
77
|
room = Room.new(dorm_id: 1000, room_id: 1001)
|
64
78
|
room_assignment = RoomAssignment.new(student_id: 1000)
|
data/test/test_create.rb
CHANGED
@@ -48,6 +48,17 @@ class TestCreate < ActiveSupport::TestCase
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
def test_create_generated_keys
|
52
|
+
# Not all databases support columns with multiple identity fields
|
53
|
+
if defined?(ActiveRecord::ConnectionAdapters::PostgreSQL) ||
|
54
|
+
defined?(ActiveRecord::ConnectionAdapters::SQLite3)
|
55
|
+
|
56
|
+
suburb = Suburb.create!(:name => 'Capitol Hill')
|
57
|
+
refute_nil(suburb.city_id)
|
58
|
+
refute_nil(suburb.suburb_id)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
51
62
|
def test_create_on_association
|
52
63
|
suburb = Suburb.first
|
53
64
|
suburb.streets.create(:name => "my street")
|
data/test/test_delete.rb
CHANGED
@@ -116,6 +116,19 @@ class TestDelete < ActiveSupport::TestCase
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
+
def test_create_destroy_all_has_and_belongs_to_many_on_non_cpk
|
120
|
+
records_before = ActiveRecord::Base.connection.execute('select * from employees_groups')
|
121
|
+
employee = Employee.create!(department_id: 3, location_id: 2, name: 'Jon')
|
122
|
+
employee.groups << Group.create(name: 'test')
|
123
|
+
employee.groups.destroy_all
|
124
|
+
records_after = ActiveRecord::Base.connection.execute('select * from employees_groups')
|
125
|
+
if records_before.respond_to?(:count)
|
126
|
+
assert_equal records_before.count, records_after.count
|
127
|
+
elsif records_before.respond_to?(:row_count) # OCI8:Cursor for oracle adapter
|
128
|
+
assert_equal records_before.row_count, records_after.row_count
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
119
132
|
def test_delete_not_destroy_on_cpk
|
120
133
|
tariff = Tariff.where(tariff_id: 2).first
|
121
134
|
tariff.delete
|
data/test/test_preload.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path('../abstract_unit', __FILE__)
|
2
2
|
|
3
3
|
class TestPreload < ActiveSupport::TestCase
|
4
|
-
fixtures :comments, :users, :employees, :groups, :hacks
|
4
|
+
fixtures :comments, :users, :employees, :groups, :hacks, :readings
|
5
5
|
|
6
6
|
class UserForPreload < User
|
7
7
|
has_many :comments_with_include_condition, -> { where('person_type = ?', 'User')},
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: composite_primary_keys
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 11.
|
4
|
+
version: 11.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charlie Savage
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -100,6 +100,7 @@ files:
|
|
100
100
|
- lib/composite_primary_keys/associations/has_many_through_association.rb
|
101
101
|
- lib/composite_primary_keys/associations/join_dependency.rb
|
102
102
|
- lib/composite_primary_keys/associations/preloader/association.rb
|
103
|
+
- lib/composite_primary_keys/associations/through_association.rb
|
103
104
|
- lib/composite_primary_keys/attribute_methods.rb
|
104
105
|
- lib/composite_primary_keys/attribute_methods/primary_key.rb
|
105
106
|
- lib/composite_primary_keys/attribute_methods/read.rb
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- lib/composite_primary_keys/composite_arrays.rb
|
110
111
|
- lib/composite_primary_keys/composite_predicates.rb
|
111
112
|
- lib/composite_primary_keys/composite_relation.rb
|
113
|
+
- lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb
|
112
114
|
- lib/composite_primary_keys/connection_adapters/abstract_adapter.rb
|
113
115
|
- lib/composite_primary_keys/connection_adapters/abstract_mysql_adapter.rb
|
114
116
|
- lib/composite_primary_keys/connection_adapters/postgresql/database_statements.rb
|
@@ -270,8 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
272
|
- !ruby/object:Gem::Version
|
271
273
|
version: '0'
|
272
274
|
requirements: []
|
273
|
-
|
274
|
-
rubygems_version: 2.7.6
|
275
|
+
rubygems_version: 3.0.2
|
275
276
|
signing_key:
|
276
277
|
specification_version: 4
|
277
278
|
summary: Composite key support for ActiveRecord
|