composite_primary_keys 9.0.10 → 10.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +30 -4
  3. data/README.rdoc +10 -9
  4. data/lib/composite_primary_keys/arel/sqlserver.rb +2 -1
  5. data/lib/composite_primary_keys/associations/association_scope.rb +13 -16
  6. data/lib/composite_primary_keys/associations/collection_association.rb +1 -1
  7. data/lib/composite_primary_keys/associations/join_dependency.rb +2 -2
  8. data/lib/composite_primary_keys/associations/preloader/association.rb +4 -2
  9. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +2 -2
  10. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +13 -0
  11. data/lib/composite_primary_keys/attribute_methods/write.rb +14 -0
  12. data/lib/composite_primary_keys/base.rb +6 -5
  13. data/lib/composite_primary_keys/composite_predicates.rb +4 -0
  14. data/lib/composite_primary_keys/connection_adapters/postgresql/database_statements.rb +26 -0
  15. data/lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb +1 -1
  16. data/lib/composite_primary_keys/core.rb +9 -12
  17. data/lib/composite_primary_keys/locking/optimistic.rb +21 -31
  18. data/lib/composite_primary_keys/nested_attributes.rb +1 -1
  19. data/lib/composite_primary_keys/relation/batches.rb +24 -5
  20. data/lib/composite_primary_keys/relation/predicate_builder/association_query_handler.rb +33 -0
  21. data/lib/composite_primary_keys/relation.rb +13 -7
  22. data/lib/composite_primary_keys/version.rb +2 -2
  23. data/lib/composite_primary_keys.rb +4 -3
  24. data/test/test_find.rb +9 -3
  25. data/test/test_preload.rb +7 -0
  26. metadata +7 -6
  27. data/lib/composite_primary_keys/relation/predicate_builder.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 133a4025b9fe7ee82617de7c3c34373d8f52d38d5e88c03ab742ebd954bf6064
4
- data.tar.gz: 579e4d9e564e4c9e40f976d0d56b76533296c8f7e5b5ba925d7244141604fb08
3
+ metadata.gz: f506d34f44d88354550c10d7b79f38c206dfa739c9c903e59c888252db3ae9ed
4
+ data.tar.gz: 06e5788af10e07e0dbe785338aa35c8cce99df310a7e43d3605b1cefb0eaa0ac
5
5
  SHA512:
6
- metadata.gz: 334933952b9a84db67746c48c074c38d197215fa607640e134bf7aedd564e122a0c809318fab4529e0ad5bd181081baa792395419b25a4b19149066122cdfdfc
7
- data.tar.gz: 2820381f390429bbdf61b0a04434c0432faa88d2cffdb04c5cd74f9dfc26682893809531b2d4dc14b03620614bec0ce1a69e128624b6a511b5718b19e24c9970
6
+ metadata.gz: 83ec3b8f8f42e7a5c92f11c60cb86fdb8fd4bdc9e6a58f1f3305ffb5c9b6af9b2abaabcaa43eeaa2e40e19cddab4cad5d7e5501905643e7b4fad0749525ac0b7
7
+ data.tar.gz: d1a8bad2cdea8ff1eab3ba7d5c456d68f008583b26b6000a930f58d319842aa6b2a24e66d7d509bd531cce4852f062797b96e53c0f929ed8aa0c6a19d40621c2
data/History.rdoc CHANGED
@@ -1,10 +1,36 @@
1
- == 9.0.10 (2018-06-24)
1
+ == 10.0.5 (2018-06-24)
2
+ * Fix in_batches (Urmo Rae)
2
3
 
3
- * Fix AR 5.0.7 (Jordan Owens)
4
+ == 10.0.4 (2018-06-17)
5
+
6
+ * Lock mysql2 gem to 0.4.x (Le Trung Kien)
7
+ * Set inversion when using preload (Le Trung Kien)
8
+ * Fixes for AR 5.1.6 (Tim Morgan)
9
+
10
+ == 10.0.3 (2018-02-25)
11
+
12
+ * Require a minimum of activerecord version 5.1.5 (Tom Hughes)
13
+
14
+ == 10.0.2 (2017-12-02)
15
+
16
+ * Fix frozen string mutation for mysql (Sergey Sein)
17
+ * Fix autosave for belongs_to associations (Fabian Mersch)
18
+ * Require a minimum of activerecord version 5.1.4 (Charlie Savage)
4
19
 
5
- == 9.0.9 (2018-03-06)
20
+ == 10.0.1 (2017-10-08)
6
21
 
7
- * Fix update statements that incorrectly pass NULL values for the primary key components (Sean Linsley)
22
+ * Fixed an error that happened when a table attribute had the same name as the table (Taishi Kasuga)
23
+
24
+ == 10.0.0 (2017-10-08)
25
+
26
+ * Support ActiveRecord 5.1.x (Pinak Thakkar, Jordan Owens)
27
+ * Fix Paper Trail compatibility (Sean Linsley)
28
+ * Fix typo in Readme (Mike Gunderloy)
29
+ * Improved sql server support (Artyom Nikolaev)
30
+
31
+ == 9.0.10 (2018-06-24)
32
+
33
+ * Fix AR 5.0.7 (Jordan Owens)
8
34
 
9
35
  == 9.0.8 (2017-10-11)
10
36
 
data/README.rdoc CHANGED
@@ -12,7 +12,7 @@ to support composite keys.
12
12
 
13
13
  If you are using Rails add the following to your Gemfile:
14
14
 
15
- gem 'composite_primary_keys', '=x.x.x' (see next section about what verison to use)
15
+ gem 'composite_primary_keys', '=x.x.x' (see next section about what version to use)
16
16
 
17
17
  == Versions
18
18
 
@@ -20,12 +20,13 @@ 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 9.x is designed to work with ActiveRecord 5.0.x
24
- Version 8.x is designed to work with ActiveRecord 4.2.x
25
- Version 7.x is designed to work with ActiveRecord 4.1.x
26
- Version 6.x is designed to work with ActiveRecord 4.0.x
27
- Version 5.x is designed to work with ActiveRecord 3.2.x
28
- Version 4.x is designed to work with ActiveRecord 3.1.x
23
+ Version 10.x is designed to work with ActiveRecord 5.1.x (note this is in progress)
24
+ Version 9.x is designed to work with ActiveRecord 5.0.x
25
+ Version 8.x is designed to work with ActiveRecord 4.2.x
26
+ Version 7.x is designed to work with ActiveRecord 4.1.x
27
+ Version 6.x is designed to work with ActiveRecord 4.0.x
28
+ Version 5.x is designed to work with ActiveRecord 3.2.x
29
+ Version 4.x is designed to work with ActiveRecord 3.1.x
29
30
 
30
31
  Run the following command to list available versions:
31
32
 
@@ -155,8 +156,8 @@ built-in rake task before running tests:
155
156
 
156
157
  rake sqlite:build_database
157
158
 
158
- Note not all tests currently pass on sqlite3. These failures happen when trying to DELETE records where the query
159
- includes a JOIN.
159
+ For sqlite3 to work correctly, you must manually require 'composite_primary_keys/connection_adapters/sqlite3_adapter' after
160
+ loading the CPK gem.
160
161
 
161
162
  === SqlServer
162
163
 
@@ -16,7 +16,8 @@ module Arel
16
16
 
17
17
  def primary_Key_From_Table t
18
18
  return unless t
19
- column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.second.try(:name)
19
+ column_name = @connection.schema_cache.primary_keys(t.name) ||
20
+ @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
20
21
 
21
22
  # CPK
22
23
  # column_name ? t[column_name] : nil
@@ -1,7 +1,6 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class AssociationScope
4
-
5
4
  def self.get_bind_values(owner, chain)
6
5
  binds = []
7
6
  last_reflection = chain.last
@@ -23,31 +22,29 @@ module ActiveRecord
23
22
  binds
24
23
  end
25
24
 
26
- def last_chain_scope(scope, table, reflection, owner, association_klass)
27
- join_keys = reflection.join_keys(association_klass)
25
+ def last_chain_scope(scope, table, reflection, owner)
26
+ join_keys = reflection.join_keys
28
27
  key = join_keys.key
29
28
  foreign_key = join_keys.foreign_key
30
29
 
31
30
  # CPK
32
- #value = transform_value(owner[foreign_key])
33
- #scope = scope.where(table.name => { key => value })
34
- mappings = Array(key).zip(Array(foreign_key))
35
- joins = mappings.reduce(Hash.new) do |hash, mapping|
36
- hash[mapping.first] = transform_value(owner[mapping.last])
37
- hash
31
+ # value = transform_value(owner[foreign_key])
32
+ # scope = apply_scope(scope, table, key, value)
33
+ Array(key).zip(Array(foreign_key)).each do |a_join_key, a_foreign_key|
34
+ value = transform_value(owner[a_foreign_key])
35
+ scope = apply_scope(scope, table, a_join_key, value)
38
36
  end
39
- scope = scope.where(table.name => joins)
40
37
 
41
38
  if reflection.type
42
39
  polymorphic_type = transform_value(owner.class.base_class.name)
43
- scope = scope.where(table.name => { reflection.type => polymorphic_type })
40
+ scope = apply_scope(scope, table, reflection.type, polymorphic_type)
44
41
  end
45
42
 
46
43
  scope
47
44
  end
48
45
 
49
- def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
50
- join_keys = reflection.join_keys(association_klass)
46
+ def next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
47
+ join_keys = reflection.join_keys
51
48
  key = join_keys.key
52
49
  foreign_key = join_keys.foreign_key
53
50
 
@@ -57,11 +54,11 @@ module ActiveRecord
57
54
 
58
55
  if reflection.type
59
56
  value = transform_value(next_reflection.klass.base_class.name)
60
- scope = scope.where(table.name => { reflection.type => value })
57
+ scope = apply_scope(scope, table, reflection.type, value)
61
58
  end
62
59
 
63
- scope = scope.joins(join(foreign_table, constraint))
60
+ scope.joins!(join(foreign_table, constraint))
64
61
  end
65
62
  end
66
63
  end
67
- end
64
+ end
@@ -29,7 +29,7 @@ module CompositePrimaryKeys
29
29
  ids.map! { |i| pk_type.cast(i) }
30
30
  # CPK
31
31
  if reflection.association_primary_key.is_a?(Array)
32
- predicate = Class.new.extend(CompositePrimaryKeys::Predicates).cpk_in_predicate(klass.arel_table, reflection.association_primary_key, ids)
32
+ predicate = CompositePrimaryKeys::Predicates.cpk_in_predicate(klass.arel_table, reflection.association_primary_key, ids)
33
33
  records = klass.where(predicate).index_by do |r|
34
34
  reflection.association_primary_key.map{ |k| r.send(k) }
35
35
  end.values_at(*ids)
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  silence_warnings do
21
- def instantiate(result_set, aliases)
21
+ def instantiate(result_set, &block)
22
22
  primary_key = aliases.column_alias(join_root, join_root.primary_key)
23
23
 
24
24
  seen = Hash.new { |i, object_id|
@@ -47,7 +47,7 @@ module ActiveRecord
47
47
  primary_key ? row_hash[primary_key] : row_hash
48
48
  end
49
49
 
50
- parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
50
+ parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
51
51
  construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
52
52
  }
53
53
  end
@@ -3,9 +3,9 @@ module ActiveRecord
3
3
  class Preloader
4
4
  class Association
5
5
  silence_warnings do
6
- def query_scope(ids)
6
+ def records_for(ids)
7
7
  # CPK
8
- # scope.where(association_key.in(ids))
8
+ # scope.where(association_key_name => ids)
9
9
 
10
10
  if reflection.foreign_key.is_a?(Array)
11
11
  predicate = cpk_in_predicate(table, reflection.foreign_key, ids)
@@ -43,6 +43,8 @@ module ActiveRecord
43
43
  records.each do |record, owner_key|
44
44
  owners_map[owner_key].each do |owner|
45
45
  records_by_owner[owner] << record
46
+ association = owner.association(reflection.name)
47
+ association.set_inverse_instance(record)
46
48
  end
47
49
  end
48
50
  end
@@ -2,7 +2,7 @@ module ActiveRecord
2
2
  module Associations
3
3
  class Preloader
4
4
  class BelongsTo
5
- def query_scope(ids)
5
+ def records_for(ids)
6
6
  # CPK
7
7
  # scope.where(association_key.in(ids))
8
8
 
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  predicate = cpk_in_predicate(table, association_key_name, ids)
11
11
  scope.where(predicate)
12
12
  else
13
- scope.where(association_key.in(ids))
13
+ scope.where(association_key_name => ids)
14
14
  end
15
15
  end
16
16
  end
@@ -16,6 +16,19 @@ module ActiveRecord
16
16
  end
17
17
 
18
18
  end
19
+
20
+ def id_in_database
21
+ sync_with_transaction_state
22
+ # CPK
23
+ # attribute_in_database(self.class.primary_key)
24
+ if self.composite?
25
+ self.class.primary_keys.map do |key_attr|
26
+ attribute_in_database(key_attr)
27
+ end
28
+ else
29
+ attribute_in_database(self.class.primary_key)
30
+ end
31
+ end
19
32
  end
20
33
  end
21
34
  end
@@ -2,6 +2,20 @@ module ActiveRecord
2
2
  module AttributeMethods
3
3
  module Write
4
4
  silence_warnings do
5
+ def write_attribute(attr_name, value)
6
+ name = if self.class.attribute_alias?(attr_name)
7
+ # CPK
8
+ # self.class.attribute_alias(attr_name).to_s
9
+ self.class.attribute_alias(attr_name)
10
+ else
11
+ # CPK
12
+ # attr_name.to_s
13
+ attr_name
14
+ end
15
+
16
+ write_attribute_with_type_cast(name, value, true)
17
+ end
18
+
5
19
  def write_attribute_with_type_cast(attr_name, value, should_type_cast)
6
20
  # CPK
7
21
  if attr_name.kind_of?(Array)
@@ -47,15 +47,16 @@ module ActiveRecord
47
47
 
48
48
  module CompositeClassMethods
49
49
  def primary_keys
50
- unless defined?(@primary_keys)
51
- reset_primary_keys
52
- end
50
+ @primary_keys = reset_primary_keys unless defined? @primary_keys
53
51
  @primary_keys
54
52
  end
55
53
 
56
54
  # Don't like this method name, but its modeled after how AR does it
57
- def reset_primary_keys
58
- if self != base_class
55
+ def reset_primary_keys #:nodoc:
56
+ if self == base_class
57
+ # CPK
58
+ self.primary_keys = get_primary_key(base_class.name)
59
+ else
59
60
  self.primary_keys = base_class.primary_keys
60
61
  end
61
62
  end
@@ -1,5 +1,9 @@
1
1
  module CompositePrimaryKeys
2
2
  module Predicates
3
+ # Similar to module_function, but does not make instance methods private.
4
+ # https://idiosyncratic-ruby.com/8-self-improvement.html
5
+ extend self
6
+
3
7
  def cpk_and_predicate(predicates)
4
8
  if predicates.length == 1
5
9
  predicates.first
@@ -0,0 +1,26 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module DatabaseStatements
5
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
6
+ if pk.nil?
7
+ # Extract the table from the insert sql. Yuck.
8
+ table_ref = extract_table_ref_from_insert_sql(sql)
9
+ pk = primary_key(table_ref) if table_ref
10
+ end
11
+
12
+ # CPK
13
+ # if pk = suppress_composite_primary_key(pk)
14
+ # sql = "#{sql} RETURNING #{quote_column_name(pk)}"
15
+ #end
16
+ # NOTE pk can be false.
17
+ if pk
18
+ sql = "#{sql} RETURNING #{quote_column_names(pk)}"
19
+ end
20
+
21
+ super
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  # quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
8
8
  # sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
9
9
  quoted_pks = [pk].flatten.map {|pk| "INSERTED.#{SQLServer::Utils.extract_identifiers(pk).quoted}"}
10
- sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT #{quoted_pks.join(", ")}"
10
+ sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT #{quoted_pks.join(", ")}"
11
11
  else
12
12
  "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
13
13
  end
@@ -27,36 +27,33 @@ module ActiveRecord
27
27
  return super if block_given? ||
28
28
  primary_key.nil? ||
29
29
  scope_attributes? ||
30
- columns_hash.include?(inheritance_column) ||
31
- ids.first.kind_of?(Array)
30
+ columns_hash.include?(inheritance_column)
31
+
32
32
  # CPK
33
33
  return super if self.composite?
34
34
 
35
- id = ids.first
36
- if ActiveRecord::Base === id
37
- id = id.id
38
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
39
- You are passing an instance of ActiveRecord::Base to `find`.
40
- Please pass the id of the object by calling `.id`
41
- MSG
42
- end
35
+ id = ids.first
36
+
37
+ return super if id.kind_of?(Array) ||
38
+ id.is_a?(ActiveRecord::Base)
43
39
 
44
40
  key = primary_key
45
41
 
46
42
  statement = cached_find_by_statement(key) { |params|
47
43
  where(key => params.bind).limit(1)
48
44
  }
45
+
49
46
  record = statement.execute([id], self, connection).first
50
47
  unless record
51
48
  raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
52
49
  name, primary_key, id)
53
50
  end
54
51
  record
55
- rescue RangeError
52
+ rescue ::RangeError
56
53
  raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
57
54
  name, primary_key)
58
55
  end
59
56
  end
60
57
  end
61
58
  end
62
- end
59
+ end
@@ -5,50 +5,40 @@ module ActiveRecord
5
5
  private
6
6
 
7
7
  silence_warnings do
8
- def _update_record(attribute_names = self.attribute_names) #:nodoc:
8
+ def _update_row(attribute_names, attempted_action = "update")
9
9
  return super unless locking_enabled?
10
- return 0 if attribute_names.empty?
11
-
12
- lock_col = self.class.locking_column
13
- previous_lock_value = send(lock_col).to_i
14
- increment_lock
15
-
16
- attribute_names += [lock_col]
17
- attribute_names.uniq!
18
10
 
19
11
  begin
20
- relation = self.class.unscoped
21
-
22
- if self.composite?
23
- affected_rows = relation.where(
24
- relation.cpk_id_predicate(relation.table, self.class.primary_key, id_was)
25
- ).where(
26
- lock_col => previous_lock_value
27
- ).update_all(
28
- attributes_for_update(attribute_names).map do |name|
29
- [name, _read_attribute(name)]
30
- end.to_h
12
+ locking_column = self.class.locking_column
13
+ previous_lock_value = read_attribute_before_type_cast(locking_column)
14
+ attribute_names << locking_column
15
+
16
+ self[locking_column] += 1
17
+
18
+ if composite?
19
+ affected_rows = self.class.unscoped._update_record(
20
+ arel_attributes_with_values(attribute_names),
21
+ Hash[self.class.primary_key.zip(id_in_database)].merge(
22
+ locking_column => previous_lock_value
23
+ )
31
24
  )
32
25
  else
33
- affected_rows = relation.where(
34
- self.class.primary_key => id,
35
- lock_col => previous_lock_value,
36
- ).update_all(
37
- attributes_for_update(attribute_names).map do |name|
38
- [name, _read_attribute(name)]
39
- end.to_h
26
+ affected_rows = self.class.unscoped._update_record(
27
+ arel_attributes_with_values(attribute_names),
28
+ self.class.primary_key => id_in_database,
29
+ locking_column => previous_lock_value
40
30
  )
41
31
  end
42
32
 
43
- unless affected_rows == 1
44
- raise ActiveRecord::StaleObjectError.new(self, "update")
33
+ if affected_rows != 1
34
+ raise ActiveRecord::StaleObjectError.new(self, attempted_action)
45
35
  end
46
36
 
47
37
  affected_rows
48
38
 
49
- # If something went wrong, revert the version.
39
+ # If something went wrong, revert the locking_column value.
50
40
  rescue Exception
51
- send(lock_col + '=', previous_lock_value)
41
+ self[locking_column] = previous_lock_value.to_i
52
42
  raise
53
43
  end
54
44
  end
@@ -41,7 +41,7 @@ module ActiveRecord
41
41
  attribute_ids = attribute_collection['id'] || attribute_collection[:id]
42
42
  if attribute_ids
43
43
  ids = CompositePrimaryKeys::CompositeKeys.parse(attribute_ids)
44
- eq_predicates = Class.new.extend(CompositePrimaryKeys::Predicates).cpk_id_predicate(association.klass.arel_table, association.klass.primary_key, ids)
44
+ eq_predicates = CompositePrimaryKeys::Predicates.cpk_id_predicate(association.klass.arel_table, association.klass.primary_key, ids)
45
45
  association.scope.where(eq_predicates).to_a
46
46
  else
47
47
  []
@@ -7,11 +7,17 @@ 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? || arel.taken.present?
11
- act_on_order_or_limit_ignored(error_on_ignore)
10
+ if arel.orders.present?
11
+ act_on_ignored_order(error_on_ignore)
12
12
  end
13
13
 
14
- relation = relation.reorder(batch_order).limit(of)
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)
16
22
  batch_relation = relation
17
23
 
@@ -39,9 +45,22 @@ module CompositePrimaryKeys
39
45
 
40
46
  yield yielded_relation
41
47
 
42
- break if ids.length < of
48
+ break if ids.length < batch_limit
49
+
50
+ if limit_value
51
+ remaining -= ids.length
52
+
53
+ if remaining == 0
54
+ # Saves a useless iteration when the limit is a multiple of the
55
+ # batch size.
56
+ break
57
+ elsif remaining < batch_limit
58
+ relation = relation.limit(remaining)
59
+ end
60
+ end
61
+
43
62
  # CPK
44
- # batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
63
+ batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
45
64
  batch_relation = if composite?
46
65
  # CPK
47
66
  # Lexicographically select records
@@ -0,0 +1,33 @@
1
+ module ActiveRecord
2
+ class PredicateBuilder
3
+ class AssociationQueryHandler
4
+ def call(attribute, value)
5
+ queries = {}
6
+
7
+ table = value.associated_table
8
+ if value.base_class
9
+ queries[table.association_foreign_type.to_s] = value.base_class.name
10
+ end
11
+
12
+ # CPK
13
+ # queries[table.association_foreign_key.to_s] = value.ids
14
+ # predicate_builder.build_from_hash(queries)
15
+ if table.association_foreign_key.is_a?(Array)
16
+ values = case value.value
17
+ when Relation
18
+ value.ids.map {|record| record.ids}
19
+ when Array
20
+ value.ids
21
+ else
22
+ [value.ids]
23
+ end
24
+
25
+ CompositePrimaryKeys::Predicates.cpk_in_predicate(attribute.relation, table.association_foreign_key, values)
26
+ else
27
+ queries[table.association_foreign_key.to_s] = value.ids
28
+ predicate_builder.build_from_hash(queries)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  end
18
18
 
19
19
  silence_warnings do
20
- def _update_record(values, id, id_was) # :nodoc:
20
+ def _update_record(values, constraints) # :nodoc:
21
21
  substitutes, binds = substitute_values values
22
22
 
23
23
  scope = @klass.unscoped
@@ -27,15 +27,21 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  # CPK
30
+ # relation = scope.where(constraints)
30
31
  if self.composite?
31
- # Not sure if this is a good idea, but replace nil id_was with the new id
32
- id_update = id_was.each_with_index.map do |value, i|
33
- value || id[i]
32
+ if constraints[@klass.primary_key]
33
+ cpks = @klass.primary_key.zip(constraints[@klass.primary_key]).to_h
34
+ others = constraints.except(@klass.primary_key)
35
+ else
36
+ cpks = Hash.new
37
+ others = Hash.new
38
+ constraints.each do |field, value|
39
+ (@klass.primary_key.include?(field) ? cpks : others)[field] = value
40
+ end
34
41
  end
35
-
36
- relation = @klass.unscoped.where(cpk_id_predicate(@klass.arel_table, @klass.primary_key, id_update))
42
+ relation = @klass.unscoped.where(cpk_id_predicate(@klass.arel_table, cpks.keys, cpks.values)).where(others)
37
43
  else
38
- relation = scope.where(@klass.primary_key => (id_was || id))
44
+ relation = scope.where(constraints)
39
45
  end
40
46
 
41
47
 
@@ -1,8 +1,8 @@
1
1
  module CompositePrimaryKeys
2
2
  module VERSION
3
- MAJOR = 9
3
+ MAJOR = 10
4
4
  MINOR = 0
5
- TINY = 10
5
+ TINY = 5
6
6
  STRING = [MAJOR, MINOR, TINY].join('.')
7
7
  end
8
8
  end
@@ -26,7 +26,7 @@ $:.unshift(File.dirname(__FILE__)) unless
26
26
 
27
27
  unless defined?(ActiveRecord)
28
28
  require 'rubygems'
29
- gem 'activerecord', '~>5.0.0.beta3'
29
+ gem 'activerecord', ['~>5.1.0', '>= 5.1.5']
30
30
  require 'active_record'
31
31
  end
32
32
 
@@ -59,12 +59,12 @@ require 'active_record/nested_attributes'
59
59
 
60
60
  require 'active_record/connection_adapters/abstract_adapter'
61
61
  require 'active_record/connection_adapters/abstract_mysql_adapter'
62
+ require 'active_record/connection_adapters/postgresql/database_statements'
62
63
 
63
64
  require 'active_record/relation/batches'
64
65
  require 'active_record/relation/where_clause'
65
66
  require 'active_record/relation/calculations'
66
67
  require 'active_record/relation/finder_methods'
67
- require 'active_record/relation/predicate_builder'
68
68
  require 'active_record/relation/query_methods'
69
69
 
70
70
  # CPK files
@@ -98,12 +98,13 @@ require 'composite_primary_keys/nested_attributes'
98
98
 
99
99
  require 'composite_primary_keys/connection_adapters/abstract_adapter'
100
100
  require 'composite_primary_keys/connection_adapters/abstract_mysql_adapter'
101
+ require 'composite_primary_keys/connection_adapters/postgresql/database_statements'
101
102
 
102
103
  require 'composite_primary_keys/relation/batches'
103
104
  require 'composite_primary_keys/relation/where_clause'
104
105
  require 'composite_primary_keys/relation/calculations'
105
106
  require 'composite_primary_keys/relation/finder_methods'
106
- require 'composite_primary_keys/relation/predicate_builder'
107
+ require 'composite_primary_keys/relation/predicate_builder/association_query_handler'
107
108
  require 'composite_primary_keys/relation/query_methods'
108
109
 
109
110
  require 'composite_primary_keys/composite_relation'
data/test/test_find.rb CHANGED
@@ -93,13 +93,19 @@ class TestFind < ActiveSupport::TestCase
93
93
  assert_equal(Department.count, num_found)
94
94
  end
95
95
 
96
- def test_expand
96
+ def test_find_by_one_association
97
97
  department = departments(:engineering)
98
98
  employees = Employee.where(:department => department)
99
- assert_equal(2, employees.count)
99
+ assert_equal(2, employees.to_a.count)
100
100
  end
101
101
 
102
- def test_expand_with_multiple
102
+ def test_find_by_all_associations
103
+ departments = Department.all
104
+ employees = Employee.where(:department => departments)
105
+ assert_equal(4, employees.to_a.count)
106
+ end
107
+
108
+ def test_expand_all
103
109
  departments = Department.all
104
110
  employees = Employee.where(:department => departments)
105
111
  assert_equal(4, employees.count)
data/test/test_preload.rb CHANGED
@@ -91,4 +91,11 @@ class TestPreload < ActiveSupport::TestCase
91
91
  assert_equal(1, employees.first.groups_2.size)
92
92
  assert_equal(2, employees.second.groups_2.size)
93
93
  end
94
+
95
+ def test_preload_settings_inversion
96
+ users = User.preload(:readings).all
97
+ reading = users.first.readings.first
98
+
99
+ assert_equal(true, reading.association(:user).loaded?)
100
+ end
94
101
  end
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: 9.0.10
4
+ version: 10.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Savage
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 5.0.0
19
+ version: 5.1.0
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 5.0.7
22
+ version: 5.1.6
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: 5.0.0
29
+ version: 5.1.0
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 5.0.7
32
+ version: 5.1.6
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rake
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +119,7 @@ files:
119
119
  - lib/composite_primary_keys/composite_relation.rb
120
120
  - lib/composite_primary_keys/connection_adapters/abstract_adapter.rb
121
121
  - lib/composite_primary_keys/connection_adapters/abstract_mysql_adapter.rb
122
+ - lib/composite_primary_keys/connection_adapters/postgresql/database_statements.rb
122
123
  - lib/composite_primary_keys/connection_adapters/sqlite3_adapter.rb
123
124
  - lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb
124
125
  - lib/composite_primary_keys/core.rb
@@ -130,7 +131,7 @@ files:
130
131
  - lib/composite_primary_keys/relation/batches.rb
131
132
  - lib/composite_primary_keys/relation/calculations.rb
132
133
  - lib/composite_primary_keys/relation/finder_methods.rb
133
- - lib/composite_primary_keys/relation/predicate_builder.rb
134
+ - lib/composite_primary_keys/relation/predicate_builder/association_query_handler.rb
134
135
  - lib/composite_primary_keys/relation/query_methods.rb
135
136
  - lib/composite_primary_keys/relation/where_clause.rb
136
137
  - lib/composite_primary_keys/sanitization.rb
@@ -1,26 +0,0 @@
1
- module ActiveRecord
2
- class PredicateBuilder
3
- silence_warnings do
4
- def expand(column, value)
5
- # Find the foreign key when using queries such as:
6
- # Post.where(author: author)
7
- #
8
- # For polymorphic relationships, find the foreign key and type:
9
- # PriceEstimate.where(estimate_of: treasure)
10
-
11
- # CPK
12
- if Base === Array(value).first && Array(value).first.composite? && reflection = table.associated_with?(column)
13
- columns = reflection.foreign_key
14
- values = Array(value).map{|v| columns.map{|c| v.public_send(c) }}
15
- cpk_predicate_builder = Class.new.extend(CompositePrimaryKeys::Predicates)
16
- predicate = cpk_predicate_builder.cpk_in_predicate(table.send(:arel_table), columns, values)
17
- return predicate
18
- else
19
- # Original code
20
- value = AssociationQueryHandler.value_for(table, column, value) if table.associated_with?(column)
21
- build(table.arel_attribute(column), value)
22
- end
23
- end
24
- end
25
- end
26
- end