composite_primary_keys 13.0.7 → 13.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,181 +1,181 @@
1
- = Composite Primary Keys for ActiveRecords
2
-
3
- == Summary
4
-
5
- ActiveRecords infamously doesn't support composite primary keys.
6
- This gem, composite_primary_keys, or CPK for short, extends ActiveRecord
7
- to support composite keys.
8
-
9
- == Installation
10
-
11
- gem install composite_primary_keys
12
-
13
- If you are using Rails add the following to your Gemfile:
14
-
15
- gem 'composite_primary_keys', '=x.x.x' (see next section about what version to use)
16
-
17
- == Versions
18
-
19
- Every major version of ActiveRecord has included numerous internal changes. As a result,
20
- CPK has to be rewritten for each version of ActiveRecord. To help keep
21
- things straight, here is the mapping:
22
-
23
- Version 13.x is designed to work with ActiveRecord 6.1.x
24
- Version 12.x is designed to work with ActiveRecord 6.0.x
25
- Version 11.x is designed to work with ActiveRecord 5.2.x
26
- Version 10.x is designed to work with ActiveRecord 5.1.x
27
- Version 9.x is designed to work with ActiveRecord 5.0.x
28
- Version 8.x is designed to work with ActiveRecord 4.2.x
29
- Version 7.x is designed to work with ActiveRecord 4.1.x
30
- Version 6.x is designed to work with ActiveRecord 4.0.x
31
- Version 5.x is designed to work with ActiveRecord 3.2.x
32
- Version 4.x is designed to work with ActiveRecord 3.1.x
33
-
34
- Run the following command to list available versions:
35
-
36
- gem list composite_primary_keys -ra
37
-
38
- == The basics
39
-
40
- A model with composite primary keys is defined like this:
41
-
42
- class Membership < ActiveRecord::Base
43
- self.primary_keys = :user_id, :group_id
44
- belongs_to :user
45
- belongs_to :group
46
- has_many :statuses, :class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
47
- end
48
-
49
- Note the addition of the line:
50
-
51
- self.primary_keys = :user_id, :group_id
52
-
53
-
54
- A model associated with a composite key model is defined like this:
55
-
56
- class MembershipStatus < ActiveRecord::Base
57
- belongs_to :membership, :foreign_key => [:user_id, :group_id]
58
- end
59
-
60
- That is, associations can include composite keys too. All Rails association types are supported. Nice.
61
-
62
- == Usage
63
-
64
- Once you’ve created your models to specify composite primary keys (such as the Membership class)
65
- and associations (such as MembershipStatus#membership), you can use them like any normal model
66
- with associations.
67
-
68
- But first, lets check out our primary keys.
69
-
70
- MembershipStatus.primary_key # => "id" # normal single key
71
- Membership.primary_key # => [:user_id, :group_id] # composite keys
72
- Membership.primary_key.to_s # => "user_id,group_id"
73
-
74
- Now we want to be able to find instances using the same syntax we always use for ActiveRecords…
75
-
76
- MembershipStatus.find(1) # single id returns single instance
77
- => <MembershipStatus:0x392a8c8 @attributes={"id"=>"1", "status"=>"Active"}>
78
-
79
- Membership.find([1,1]) # composite ids returns single instance
80
- => <Membership:0x39218b0 @attributes={"user_id"=>"1", "group_id"=>"1"}>
81
-
82
- Notice the use of an array to specify the composite key values.
83
-
84
- NOTE - API CHANGE. CPK Version 6.x and earlier used to allow composite keys to be listed out
85
- like this:
86
-
87
- Membership.find(1,1)
88
-
89
- This usage is no longer supported.
90
-
91
- == Databases
92
-
93
- CPK supports the following databases:
94
-
95
- * PostgreSQL
96
- * MySQL
97
- * MariaDB
98
- * Oracle
99
- * DB2
100
- * SQLite
101
- * SQLServer
102
-
103
- == Tests
104
-
105
- To run tests you first need to install the appropriate gems for the database you want to test. Database gems are
106
- divided into the following bundler groups:
107
-
108
- * mysql
109
- * oracle
110
- * postgresql
111
- * sqlite
112
- * sqlserver
113
-
114
- Since it is likely you do not have all the above databases installed on your computer, you want to install just the
115
- gems for your database. For example, to test postgresql you would install the appropriate gems like this:
116
-
117
- bundler config set --local without "mysql oracle sqlite sqlserver"
118
- bundler install
119
-
120
- Once you have installed the appropriate gems, the next step is to create the test database. There is a rake
121
- command for each database. Using our example:
122
-
123
- rake postgresql:build_database
124
-
125
- You can also rebuild the database if it already exists using this command:
126
-
127
- rake postgresql:rebuild_database
128
-
129
- To get a list of commands for your database use:
130
-
131
- Rake -T
132
-
133
- Finally, to run tests:
134
-
135
- rake postgresql:test
136
-
137
- Travis build status: {<img src="https://travis-ci.com/composite-primary-keys/composite_primary_keys.svg" alt="Build Status" />}[https://travis-ci.com/composite-primary-keys/composite_primary_keys]
138
-
139
- === DB2
140
-
141
- DB2 is no longer supported due to difficulties in getting the ibm_db2 gem to build. Thus tests
142
- have not been run against db2.
143
-
144
- === MariaDb (mysql)
145
-
146
- MariaDb is fully supported with all tests passing.
147
-
148
- === Oracle
149
-
150
- Oracle is fully supported with all tests passing.
151
-
152
- === Postgresql
153
-
154
- Postgresql is fully supported with all tests passing.
155
-
156
- === Sqlite 3
157
-
158
- The sqlite database is created at the path composite_primary_keys/db. Note you must *first* create the database using the
159
- built-in rake task before running tests:
160
-
161
- rake sqlite:build_database
162
-
163
- For sqlite3 to work correctly, you must manually require 'composite_primary_keys/connection_adapters/sqlite3_adapter' after
164
- loading the CPK gem.
165
-
166
- === SqlServer
167
-
168
- SqlServer is partially supported. There are a number of failing tests - patches welcomed.
169
-
170
- == Questions, Discussion and Contributions
171
-
172
- For help please visit https://github.com/composite-primary-keys/composite_primary_keys.
173
-
174
- == Author
175
-
176
- First version was written by Dr Nic Williams.
177
-
178
- Maintained by Charlie Savage
179
-
180
- Contributions by many!
181
-
1
+ = Composite Primary Keys for ActiveRecords
2
+
3
+ == Summary
4
+
5
+ ActiveRecords infamously doesn't support composite primary keys.
6
+ This gem, composite_primary_keys, or CPK for short, extends ActiveRecord
7
+ to support composite keys.
8
+
9
+ == Installation
10
+
11
+ gem install composite_primary_keys
12
+
13
+ If you are using Rails add the following to your Gemfile:
14
+
15
+ gem 'composite_primary_keys', '=x.x.x' (see next section about what version to use)
16
+
17
+ == Versions
18
+
19
+ Every major version of ActiveRecord has included numerous internal changes. As a result,
20
+ CPK has to be rewritten for each version of ActiveRecord. To help keep
21
+ things straight, here is the mapping:
22
+
23
+ Version 13.x is designed to work with ActiveRecord 6.1.x
24
+ Version 12.x is designed to work with ActiveRecord 6.0.x
25
+ Version 11.x is designed to work with ActiveRecord 5.2.x
26
+ Version 10.x is designed to work with ActiveRecord 5.1.x
27
+ Version 9.x is designed to work with ActiveRecord 5.0.x
28
+ Version 8.x is designed to work with ActiveRecord 4.2.x
29
+ Version 7.x is designed to work with ActiveRecord 4.1.x
30
+ Version 6.x is designed to work with ActiveRecord 4.0.x
31
+ Version 5.x is designed to work with ActiveRecord 3.2.x
32
+ Version 4.x is designed to work with ActiveRecord 3.1.x
33
+
34
+ Run the following command to list available versions:
35
+
36
+ gem list composite_primary_keys -ra
37
+
38
+ == The basics
39
+
40
+ A model with composite primary keys is defined like this:
41
+
42
+ class Membership < ActiveRecord::Base
43
+ self.primary_keys = :user_id, :group_id
44
+ belongs_to :user
45
+ belongs_to :group
46
+ has_many :statuses, :class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
47
+ end
48
+
49
+ Note the addition of the line:
50
+
51
+ self.primary_keys = :user_id, :group_id
52
+
53
+
54
+ A model associated with a composite key model is defined like this:
55
+
56
+ class MembershipStatus < ActiveRecord::Base
57
+ belongs_to :membership, :foreign_key => [:user_id, :group_id]
58
+ end
59
+
60
+ That is, associations can include composite keys too. All Rails association types are supported. Nice.
61
+
62
+ == Usage
63
+
64
+ Once you’ve created your models to specify composite primary keys (such as the Membership class)
65
+ and associations (such as MembershipStatus#membership), you can use them like any normal model
66
+ with associations.
67
+
68
+ But first, lets check out our primary keys.
69
+
70
+ MembershipStatus.primary_key # => "id" # normal single key
71
+ Membership.primary_key # => [:user_id, :group_id] # composite keys
72
+ Membership.primary_key.to_s # => "user_id,group_id"
73
+
74
+ Now we want to be able to find instances using the same syntax we always use for ActiveRecords…
75
+
76
+ MembershipStatus.find(1) # single id returns single instance
77
+ => <MembershipStatus:0x392a8c8 @attributes={"id"=>"1", "status"=>"Active"}>
78
+
79
+ Membership.find([1,1]) # composite ids returns single instance
80
+ => <Membership:0x39218b0 @attributes={"user_id"=>"1", "group_id"=>"1"}>
81
+
82
+ Notice the use of an array to specify the composite key values.
83
+
84
+ NOTE - API CHANGE. CPK Version 6.x and earlier used to allow composite keys to be listed out
85
+ like this:
86
+
87
+ Membership.find(1,1)
88
+
89
+ This usage is no longer supported.
90
+
91
+ == Databases
92
+
93
+ CPK supports the following databases:
94
+
95
+ * PostgreSQL
96
+ * MySQL
97
+ * MariaDB
98
+ * Oracle
99
+ * DB2
100
+ * SQLite
101
+ * SQLServer
102
+
103
+ == Tests
104
+
105
+ To run tests you first need to install the appropriate gems for the database you want to test. Database gems are
106
+ divided into the following bundler groups:
107
+
108
+ * mysql
109
+ * oracle
110
+ * postgresql
111
+ * sqlite
112
+ * sqlserver
113
+
114
+ Since it is likely you do not have all the above databases installed on your computer, you want to install just the
115
+ gems for your database. For example, to test postgresql you would install the appropriate gems like this:
116
+
117
+ bundler config set --local without "mysql oracle sqlite sqlserver"
118
+ bundler install
119
+
120
+ Once you have installed the appropriate gems, the next step is to create the test database. There is a rake
121
+ command for each database. Using our example:
122
+
123
+ rake postgresql:build_database
124
+
125
+ You can also rebuild the database if it already exists using this command:
126
+
127
+ rake postgresql:rebuild_database
128
+
129
+ To get a list of commands for your database use:
130
+
131
+ Rake -T
132
+
133
+ Finally, to run tests:
134
+
135
+ rake postgresql:test
136
+
137
+ Travis build status: {<img src="https://travis-ci.com/composite-primary-keys/composite_primary_keys.svg" alt="Build Status" />}[https://travis-ci.com/composite-primary-keys/composite_primary_keys]
138
+
139
+ === DB2
140
+
141
+ DB2 is no longer supported due to difficulties in getting the ibm_db2 gem to build. Thus tests
142
+ have not been run against db2.
143
+
144
+ === MariaDb (mysql)
145
+
146
+ MariaDb is fully supported with all tests passing.
147
+
148
+ === Oracle
149
+
150
+ Oracle is fully supported with all tests passing.
151
+
152
+ === Postgresql
153
+
154
+ Postgresql is fully supported with all tests passing.
155
+
156
+ === Sqlite 3
157
+
158
+ The sqlite database is created at the path composite_primary_keys/db. Note you must *first* create the database using the
159
+ built-in rake task before running tests:
160
+
161
+ rake sqlite:build_database
162
+
163
+ For sqlite3 to work correctly, you must manually require 'composite_primary_keys/connection_adapters/sqlite3_adapter' after
164
+ loading the CPK gem.
165
+
166
+ === SqlServer
167
+
168
+ SqlServer is partially supported. There are a number of failing tests - patches welcomed.
169
+
170
+ == Questions, Discussion and Contributions
171
+
172
+ For help please visit https://github.com/composite-primary-keys/composite_primary_keys.
173
+
174
+ == Author
175
+
176
+ First version was written by Dr Nic Williams.
177
+
178
+ Maintained by Charlie Savage
179
+
180
+ Contributions by many!
181
+
@@ -11,8 +11,8 @@ module ActiveRecord
11
11
  attributes[key1] = owner[key2]
12
12
  end
13
13
 
14
- if reflection.options[:as]
15
- attributes[reflection.type] = owner.class.base_class.name
14
+ if reflection.type
15
+ attributes[reflection.type] = owner.class.polymorphic_name
16
16
  end
17
17
  end
18
18
 
@@ -64,4 +64,4 @@ module ActiveRecord
64
64
  end
65
65
  end
66
66
  end
67
- end
67
+ end
@@ -1,32 +1,32 @@
1
- module CompositePrimaryKeys
2
- module CollectionAssociation
3
- def ids_writer(ids)
4
- primary_key = reflection.association_primary_key
5
- pk_type = klass.type_for_attribute(primary_key)
6
- ids = Array(ids).reject(&:blank?)
7
- ids.map! { |i| pk_type.cast(i) }
8
-
9
- # CPK-
10
- if primary_key.is_a?(Array)
11
- predicate = CompositePrimaryKeys::Predicates.cpk_in_predicate(klass.arel_table, reflection.association_primary_key, ids)
12
- records = klass.where(predicate).index_by do |r|
13
- reflection.association_primary_key.map{ |k| r.send(k) }
14
- end.values_at(*ids)
15
- else
16
- records = klass.where(primary_key => ids).index_by do |r|
17
- r.public_send(primary_key)
18
- end.values_at(*ids).compact
19
- end
20
-
21
- if records.size != ids.size
22
- found_ids = records.map { |record| record.public_send(primary_key) }
23
- not_found_ids = ids - found_ids
24
- klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
25
- else
26
- replace(records)
27
- end
28
- end
29
- end
30
- end
31
-
1
+ module CompositePrimaryKeys
2
+ module CollectionAssociation
3
+ def ids_writer(ids)
4
+ primary_key = reflection.association_primary_key
5
+ pk_type = klass.type_for_attribute(primary_key)
6
+ ids = Array(ids).reject(&:blank?)
7
+ ids.map! { |i| pk_type.cast(i) }
8
+
9
+ # CPK-
10
+ if primary_key.is_a?(Array)
11
+ predicate = CompositePrimaryKeys::Predicates.cpk_in_predicate(klass.arel_table, reflection.association_primary_key, ids)
12
+ records = klass.where(predicate).index_by do |r|
13
+ reflection.association_primary_key.map{ |k| r.send(k) }
14
+ end.values_at(*ids)
15
+ else
16
+ records = klass.where(primary_key => ids).index_by do |r|
17
+ r.public_send(primary_key)
18
+ end.values_at(*ids).compact
19
+ end
20
+
21
+ if records.size != ids.size
22
+ found_ids = records.map { |record| record.public_send(primary_key) }
23
+ not_found_ids = ids - found_ids
24
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
25
+ else
26
+ replace(records)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
32
  ActiveRecord::Associations::CollectionAssociation.prepend CompositePrimaryKeys::CollectionAssociation
@@ -1,61 +1,61 @@
1
- module ActiveRecord
2
- module Associations
3
- class Preloader
4
- class Association
5
- def records_for(ids)
6
- records = if association_key_name.is_a?(Array)
7
- predicate = cpk_in_predicate(klass.arel_table, association_key_name, ids)
8
- scope.where(predicate)
9
- else
10
- scope.where(association_key_name => ids)
11
- end
12
- records.load do |record|
13
- # Processing only the first owner
14
- # because the record is modified but not an owner
15
- owner = owners_by_key[convert_key(record[association_key_name])].first
16
- association = owner.association(reflection.name)
17
- association.set_inverse_instance(record)
18
- end
19
- end
20
-
21
- def owners_by_key
22
- @owners_by_key ||= owners.each_with_object({}) do |owner, result|
23
- # CPK
24
- # key = convert_key(owner[owner_key_name])
25
- key = if owner_key_name.is_a?(Array)
26
- Array(owner_key_name).map do |key_name|
27
- convert_key(owner[key_name])
28
- end
29
- else
30
- convert_key(owner[owner_key_name])
31
- end
32
- (result[key] ||= []) << owner if key
33
- end
34
- end
35
-
36
- def load_records
37
- # owners can be duplicated when a relation has a collection association join
38
- # #compare_by_identity makes such owners different hash keys
39
- @records_by_owner = {}.compare_by_identity
40
- raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
41
-
42
- @preloaded_records = raw_records.select do |record|
43
- assignments = false
44
-
45
- owners_by_key[convert_key(record[association_key_name])].each do |owner|
46
- entries = (@records_by_owner[owner] ||= [])
47
-
48
- if reflection.collection? || entries.empty?
49
- entries << record
50
- assignments = true
51
- end
52
- end
53
-
54
- assignments
55
- end
56
- end
57
-
58
- end
59
- end
60
- end
61
- end
1
+ module ActiveRecord
2
+ module Associations
3
+ class Preloader
4
+ class Association
5
+ def records_for(ids)
6
+ records = if association_key_name.is_a?(Array)
7
+ predicate = cpk_in_predicate(klass.arel_table, association_key_name, ids)
8
+ scope.where(predicate)
9
+ else
10
+ scope.where(association_key_name => ids)
11
+ end
12
+ records.load do |record|
13
+ # Processing only the first owner
14
+ # because the record is modified but not an owner
15
+ owner = owners_by_key[convert_key(record[association_key_name])].first
16
+ association = owner.association(reflection.name)
17
+ association.set_inverse_instance(record)
18
+ end
19
+ end
20
+
21
+ def owners_by_key
22
+ @owners_by_key ||= owners.each_with_object({}) do |owner, result|
23
+ # CPK
24
+ # key = convert_key(owner[owner_key_name])
25
+ key = if owner_key_name.is_a?(Array)
26
+ Array(owner_key_name).map do |key_name|
27
+ convert_key(owner[key_name])
28
+ end
29
+ else
30
+ convert_key(owner[owner_key_name])
31
+ end
32
+ (result[key] ||= []) << owner if key
33
+ end
34
+ end
35
+
36
+ def load_records
37
+ # owners can be duplicated when a relation has a collection association join
38
+ # #compare_by_identity makes such owners different hash keys
39
+ @records_by_owner = {}.compare_by_identity
40
+ raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
41
+
42
+ @preloaded_records = raw_records.select do |record|
43
+ assignments = false
44
+
45
+ owners_by_key[convert_key(record[association_key_name])].each do |owner|
46
+ entries = (@records_by_owner[owner] ||= [])
47
+
48
+ if reflection.collection? || entries.empty?
49
+ entries << record
50
+ assignments = true
51
+ end
52
+ end
53
+
54
+ assignments
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,24 +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 !self.source_reflection.polymorphic? && 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
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 !self.source_reflection.polymorphic? && 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