composite_primary_keys 6.0.8 → 7.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +2 -8
  3. data/lib/composite_primary_keys.rb +9 -11
  4. data/lib/composite_primary_keys/active_model/dirty.rb +14 -0
  5. data/lib/composite_primary_keys/associations/association_scope.rb +30 -32
  6. data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +1 -2
  7. data/lib/composite_primary_keys/associations/has_many_association.rb +13 -14
  8. data/lib/composite_primary_keys/associations/has_many_through_association.rb +88 -0
  9. data/lib/composite_primary_keys/associations/join_dependency.rb +23 -15
  10. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +3 -3
  11. data/lib/composite_primary_keys/associations/preloader/association.rb +37 -21
  12. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +9 -3
  13. data/lib/composite_primary_keys/base.rb +0 -1
  14. data/lib/composite_primary_keys/composite_predicates.rb +11 -34
  15. data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +6 -5
  16. data/lib/composite_primary_keys/core.rb +28 -16
  17. data/lib/composite_primary_keys/model_schema.rb +15 -0
  18. data/lib/composite_primary_keys/nested_attributes.rb +8 -10
  19. data/lib/composite_primary_keys/persistence.rb +28 -29
  20. data/lib/composite_primary_keys/relation.rb +86 -16
  21. data/lib/composite_primary_keys/relation/finder_methods.rb +113 -59
  22. data/lib/composite_primary_keys/version.rb +2 -2
  23. data/test/abstract_unit.rb +7 -8
  24. data/test/fixtures/db_definitions/db2-create-tables.sql +13 -20
  25. data/test/fixtures/db_definitions/db2-drop-tables.sql +13 -14
  26. data/test/fixtures/db_definitions/mysql.sql +0 -7
  27. data/test/fixtures/db_definitions/oracle.drop.sql +0 -1
  28. data/test/fixtures/db_definitions/oracle.sql +0 -6
  29. data/test/fixtures/db_definitions/postgresql.sql +0 -7
  30. data/test/fixtures/db_definitions/sqlite.sql +24 -30
  31. data/test/fixtures/db_definitions/sqlserver.drop.sql +1 -4
  32. data/test/fixtures/db_definitions/sqlserver.sql +7 -16
  33. data/test/fixtures/dorm.rb +2 -2
  34. data/test/fixtures/suburb.rb +2 -2
  35. data/test/test_associations.rb +1 -2
  36. data/test/test_attribute_methods.rb +3 -3
  37. data/test/test_delete.rb +3 -5
  38. data/test/test_equal.rb +5 -5
  39. data/test/test_find.rb +2 -14
  40. data/test/test_predicates.rb +2 -2
  41. data/test/test_santiago.rb +1 -1
  42. data/test/test_serialize.rb +1 -1
  43. data/test/test_suite.rb +0 -1
  44. data/test/test_tutorial_example.rb +3 -3
  45. metadata +9 -32
  46. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +0 -17
  47. data/lib/composite_primary_keys/composite_relation.rb +0 -44
  48. data/lib/composite_primary_keys/locking/optimistic.rb +0 -51
  49. data/test/fixtures/model_with_callback.rb +0 -39
  50. data/test/fixtures/model_with_callbacks.yml +0 -3
  51. data/test/test_callbacks.rb +0 -36
  52. data/test/test_dumpable.rb +0 -15
  53. data/test/test_optimistic.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e0407c008d66e10820394e98a97bb7a507d0226a
4
- data.tar.gz: 25d4c43eb87731e104c3ec486e94a20b9568f0bf
3
+ metadata.gz: b533cad3bf71d1945db74c8626cac9731a5a6d35
4
+ data.tar.gz: c117bd2a65a362c69d36f0b2fe4e2495a9811933
5
5
  SHA512:
6
- metadata.gz: 007219da8c4b82d562742f361516772b7f74d4f97e6d065ef01b526dbb9b6047e18b2445dcf3054070d7ff129da0d7a29f5ff78fc55ef49f22503080fa894a42
7
- data.tar.gz: b712e6fb1b0d39c530b6c54c9e6dddd81bcf6552989f95b12160612379aaa27ee671ae6d1fc85a40d698c933ff2fc65eba2777b1e060ac94380f3c7ce6a603cb
6
+ metadata.gz: a08e6c2dafa9d5570a22ccc4cd12d4b9c598f3b8b463e1719d398408e76a3d05bf15278c620f3dad04939e63964f493f99c5efafa28d6cfab52bc4a06d15a50b
7
+ data.tar.gz: 114d12d9f174d079a6172ac33748c25110c12f4400274f283f677eab664e2d77a868864b643080e1976fcc8c56e67e08b5fea9807b6eaa71f01d60f02339f549
@@ -1,12 +1,6 @@
1
- == 6.0.08 (2015-01-24)
1
+ == 7.0.0 (2014-05-26)
2
2
 
3
- * Fix habtm association #delete_records (Uros Jurglic)
4
- * Support optimistic locking (Toshio Maki)
5
- * Remove singleton classes on CPK relations (Nicolás Hock Isaza)
6
-
7
- == 6.0.7 (2014-10-06)
8
-
9
- * Support Rails 4.0.6 (Tom Hughes)
3
+ * Active Record 4.1 support! (Charlie Savage, Tom Hughes, Simon South)
10
4
 
11
5
  == 6.0.6 (2014-05-26)
12
6
 
@@ -26,36 +26,37 @@ $:.unshift(File.dirname(__FILE__)) unless
26
26
 
27
27
  unless defined?(ActiveRecord)
28
28
  require 'rubygems'
29
- gem 'activerecord', '~>4.0.6'
29
+ gem 'activerecord', '~>4.1.0'
30
30
  require 'active_record'
31
31
  end
32
32
 
33
+ # AM files we override
34
+ require 'active_model/dirty'
35
+
33
36
  # AR files we override
34
37
  require 'active_record/counter_cache'
35
38
  require 'active_record/fixtures'
39
+ require 'active_record/model_schema'
36
40
  require 'active_record/persistence'
37
41
  require 'active_record/relation'
38
42
  require 'active_record/sanitization'
39
43
 
40
44
  require 'active_record/associations/association'
41
45
  require 'active_record/associations/association_scope'
42
- require 'active_record/associations/has_and_belongs_to_many_association'
43
46
  require 'active_record/associations/has_many_association'
47
+ require 'active_record/associations/has_many_through_association'
44
48
  require 'active_record/associations/join_dependency'
45
49
  require 'active_record/associations/join_dependency/join_part'
46
50
  require 'active_record/associations/join_dependency/join_association'
47
51
  require 'active_record/associations/preloader/association'
48
52
  require 'active_record/associations/preloader/belongs_to'
49
- require 'active_record/associations/preloader/has_and_belongs_to_many'
50
53
 
51
54
 
52
55
  require 'active_model/dirty'
53
56
 
54
57
  require 'active_record/attribute_methods/dirty'
55
- require 'active_record/attribute_methods/primary_key'
56
58
  require 'active_record/attribute_methods/read'
57
59
  require 'active_record/attribute_methods/write'
58
- require 'active_record/locking/optimistic'
59
60
  require 'active_record/nested_attributes'
60
61
 
61
62
  require 'active_record/connection_adapters/abstract_adapter'
@@ -69,33 +70,32 @@ require 'active_record/validations/uniqueness'
69
70
 
70
71
 
71
72
  # CPK files
73
+ require 'composite_primary_keys/active_model/dirty'
72
74
  require 'composite_primary_keys/persistence'
73
- require 'composite_primary_keys/locking/optimistic'
74
75
  require 'composite_primary_keys/active_record_overides'
75
76
  require 'composite_primary_keys/base'
76
77
  require 'composite_primary_keys/core'
77
78
  require 'composite_primary_keys/composite_arrays'
78
79
  require 'composite_primary_keys/composite_predicates'
79
80
  require 'composite_primary_keys/fixtures'
81
+ require 'composite_primary_keys/model_schema'
80
82
  require 'composite_primary_keys/relation'
81
83
  require 'composite_primary_keys/sanitization'
82
84
  require 'composite_primary_keys/version'
83
85
 
84
86
  require 'composite_primary_keys/associations/association'
85
87
  require 'composite_primary_keys/associations/association_scope'
86
- require 'composite_primary_keys/associations/has_and_belongs_to_many_association'
87
88
  require 'composite_primary_keys/associations/has_many_association'
89
+ require 'composite_primary_keys/associations/has_many_through_association'
88
90
  require 'composite_primary_keys/associations/join_dependency'
89
91
  require 'composite_primary_keys/associations/join_dependency/join_part'
90
92
  require 'composite_primary_keys/associations/join_dependency/join_association'
91
93
  require 'composite_primary_keys/associations/preloader/association'
92
94
  require 'composite_primary_keys/associations/preloader/belongs_to'
93
- require 'composite_primary_keys/associations/preloader/has_and_belongs_to_many'
94
95
 
95
96
  require 'composite_primary_keys/dirty'
96
97
 
97
98
  require 'composite_primary_keys/attribute_methods/dirty'
98
- require 'composite_primary_keys/attribute_methods/primary_key'
99
99
  require 'composite_primary_keys/attribute_methods/read'
100
100
  require 'composite_primary_keys/attribute_methods/write'
101
101
  require 'composite_primary_keys/nested_attributes'
@@ -109,5 +109,3 @@ require 'composite_primary_keys/relation/finder_methods'
109
109
  require 'composite_primary_keys/relation/query_methods'
110
110
 
111
111
  require 'composite_primary_keys/validations/uniqueness'
112
-
113
- require 'composite_primary_keys/composite_relation'
@@ -0,0 +1,14 @@
1
+ module ActiveModel
2
+ module Dirty
3
+ def attribute_was(attr)
4
+ # CPK
5
+ if self.composite? && attr == "id"
6
+ self.class.primary_keys.map do |attr|
7
+ attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
8
+ end
9
+ else
10
+ attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,31 +1,18 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class AssociationScope
4
- def add_constraints(scope)
5
- tables = construct_tables
4
+ def add_constraints(scope, owner, assoc_klass, refl, tracker)
5
+ chain = refl.chain
6
+ scope_chain = refl.scope_chain
7
+
8
+ tables = construct_tables(chain, assoc_klass, refl, tracker)
6
9
 
7
10
  chain.each_with_index do |reflection, i|
8
11
  table, foreign_table = tables.shift, tables.first
9
12
 
10
- if reflection.source_macro == :has_and_belongs_to_many
11
- join_table = tables.shift
12
-
13
- # CPK
14
- # scope = scope.joins(join(
15
- # join_table,
16
- # table[reflection.active_record_primary_key].
17
- # eq(join_table[reflection.association_foreign_key])
18
- #))
19
- predicate = cpk_join_predicate(table, reflection.association_primary_key,
20
- join_table, reflection.association_foreign_key)
21
- scope = scope.joins(join(join_table, predicate))
22
-
23
- table, foreign_table = join_table, tables.first
24
- end
25
-
26
13
  if reflection.source_macro == :belongs_to
27
14
  if reflection.options[:polymorphic]
28
- key = reflection.association_primary_key(self.klass)
15
+ key = reflection.association_primary_key(assoc_klass)
29
16
  else
30
17
  key = reflection.association_primary_key
31
18
  end
@@ -36,39 +23,50 @@ module ActiveRecord
36
23
  foreign_key = reflection.active_record_primary_key
37
24
  end
38
25
 
39
-
40
26
  if reflection == chain.last
41
- # CPK
42
- # scope = scope.where(table[key].eq(owner[foreign_key]))
27
+ # CPK - TODO add back in tracker support
28
+ #bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
29
+ #scope = scope.where(table[key].eq(bind_val))
43
30
  predicate = cpk_join_predicate(table, key, owner, foreign_key)
44
31
  scope = scope.where(predicate)
45
32
 
46
33
  if reflection.type
47
- scope = scope.where(table[reflection.type].eq(owner.class.base_class.name))
34
+ value = owner.class.base_class.name
35
+ bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
36
+ scope = scope.where(table[reflection.type].eq(bind_val))
48
37
  end
49
38
  else
50
39
  # CPK
51
- # constraint = table[key].eq(foreign_table[foreign_key])
40
+ #constraint = table[key].eq(foreign_table[foreign_key])
52
41
  constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
53
42
 
54
43
  if reflection.type
55
- type = chain[i + 1].klass.base_class.name
56
- constraint = constraint.and(table[reflection.type].eq(type))
44
+ value = chain[i + 1].klass.base_class.name
45
+ bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
46
+ scope = scope.where(table[reflection.type].eq(bind_val))
57
47
  end
58
48
 
59
49
  scope = scope.joins(join(foreign_table, constraint))
60
50
  end
61
-
51
+
52
+ is_first_chain = i == 0
53
+ klass = is_first_chain ? assoc_klass : reflection.klass
54
+
55
+ # Exclude the scope of the association itself, because that
56
+ # was already merged in the #scope method.
62
57
  scope_chain[i].each do |scope_chain_item|
63
- klass = i == 0 ? self.klass : reflection.klass
64
- item = eval_scope(klass, scope_chain_item)
58
+ item = eval_scope(klass, scope_chain_item, owner)
59
+
60
+ if scope_chain_item == refl.scope
61
+ scope.merge! item.except(:where, :includes, :bind)
62
+ end
65
63
 
66
- if scope_chain_item == self.reflection.scope
67
- scope.merge! item.except(:where, :includes)
64
+ if is_first_chain
65
+ scope.includes! item.includes_values
68
66
  end
69
67
 
70
- scope.includes! item.includes_values
71
68
  scope.where_values += item.where_values
69
+ scope.order_values |= item.order_values
72
70
  end
73
71
  end
74
72
 
@@ -49,8 +49,7 @@ module ActiveRecord
49
49
 
50
50
  predicate1 = cpk_id_predicate(relation, Array(reflection.foreign_key), Array(owner.id))
51
51
  predicate2 = cpk_in_predicate(relation, Array(reflection.association_foreign_key), records.map { |x| x.id }) unless records == :all
52
- conds = predicate2.nil?? predicate1 : predicate1.and(predicate2)
53
- stmt = relation.where(conds).compile_delete
52
+ stmt = relation.where(predicate1.and(predicate2)).compile_delete
54
53
 
55
54
  owner.class.connection.delete stmt.to_sql
56
55
  end
@@ -3,15 +3,15 @@ module ActiveRecord
3
3
  class HasManyAssociation
4
4
  def delete_records(records, method)
5
5
  if method == :destroy
6
- records.each { |r| r.destroy }
6
+ records.each(&:destroy!)
7
7
  update_counter(-records.length) unless inverse_updates_counter_cache?
8
8
  else
9
- if records == :all
9
+ if records == :all || !reflection.klass.primary_key
10
10
  scope = self.scope
11
11
  else
12
12
  # CPK
13
- # keys = records.map { |r| r[reflection.association_primary_key] }
14
- # scope = scope.where(reflection.association_primary_key => keys)
13
+ # scope = self.scope.where(reflection.klass.primary_key => records)
14
+ # scope = self.scope.where(reflection.klass.primary_key => records)
15
15
  table = Arel::Table.new(reflection.table_name)
16
16
  and_conditions = records.map do |record|
17
17
  eq_conditions = Array(reflection.association_primary_key).map do |name|
@@ -31,21 +31,20 @@ module ActiveRecord
31
31
  if method == :delete_all
32
32
  update_counter(-scope.delete_all)
33
33
  else
34
- # CPK
35
- # update_counter(-scope.update_all(reflection.foreign_key => nil))
36
- updates = Array(reflection.foreign_key).inject(Hash.new) do |hash, name|
37
- hash[name] = nil
38
- hash
39
- end
40
- update_counter(-scope.update_all(updates))
34
+ update_counter(-scope.update_all(reflection.foreign_key => nil))
41
35
  end
42
36
  end
43
37
  end
44
38
 
45
39
  def foreign_key_present?
46
- # CPK
47
- # owner.attribute_present?(reflection.association_primary_key)
48
- Array(reflection.association_primary_key).all? {|key| owner.attribute_present?(key)}
40
+ if reflection.klass.primary_key
41
+ # CPK
42
+ #owner.attribute_present?(reflection.association_primary_key)
43
+ owner.attribute_present?(reflection.association_primary_key)
44
+ Array(reflection.klass.primary_key).all? {|key| owner.attribute_present?(key)}
45
+ else
46
+ false
47
+ end
49
48
  end
50
49
  end
51
50
  end
@@ -0,0 +1,88 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class HasManyThroughAssociation
4
+ def cpk_join_through_predicate(*records)
5
+ ensure_mutable
6
+
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
+ # This is unoptimised; it will load all the target records
20
+ # even when we just want to delete everything.
21
+ records = load_target if records == :all
22
+
23
+ scope = through_association.scope
24
+
25
+ # CPK
26
+ # scope.where! construct_join_attributes(*records)
27
+ if source_reflection.klass.composite?
28
+ scope.where! cpk_join_through_predicate(*records)
29
+ else
30
+ scope.where! construct_join_attributes(*records)
31
+ end
32
+
33
+ case method
34
+ when :destroy
35
+ if scope.klass.primary_key
36
+ count = scope.destroy_all.length
37
+ else
38
+ scope.to_a.each do |record|
39
+ record.run_callbacks :destroy
40
+ end
41
+
42
+ arel = scope.arel
43
+
44
+ stmt = Arel::DeleteManager.new arel.engine
45
+ stmt.from scope.klass.arel_table
46
+ stmt.wheres = arel.constraints
47
+
48
+ count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values)
49
+ end
50
+ when :nullify
51
+ count = scope.update_all(source_reflection.foreign_key => nil)
52
+ else
53
+ count = scope.delete_all
54
+ end
55
+
56
+ delete_through_records(records)
57
+
58
+ if source_reflection.options[:counter_cache] && method != :destroy
59
+ counter = source_reflection.counter_cache_column
60
+ klass.decrement_counter counter, records.map(&:id)
61
+ end
62
+
63
+ if through_reflection.macro == :has_many && update_through_counter?(method)
64
+ update_counter(-count, through_reflection)
65
+ end
66
+
67
+ update_counter(-count)
68
+ end
69
+
70
+ def through_records_for(record)
71
+ # CPK
72
+ #attributes = construct_join_attributes(record)
73
+ #candidates = Array.wrap(through_association.target)
74
+ #candidates.find_all { |c| c.attributes.slice(*attributes.keys) == attributes }
75
+
76
+ if record.composite?
77
+ candidates = Array.wrap(through_association.target)
78
+ candidates.find_all { |c| c.attributes.slice(*source_reflection.association_primary_key) == record.ids_hash }
79
+ else
80
+ attributes = construct_join_attributes(record)
81
+ candidates = Array.wrap(through_association.target)
82
+ candidates.find_all { |c| c.attributes.slice(*attributes.keys) == attributes }
83
+ end
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -1,25 +1,33 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class JoinDependency
4
- def instantiate(rows)
5
- primary_key = join_base.aliased_primary_key
6
- parents = {}
4
+ def instantiate(result_set, aliases)
5
+ primary_key = aliases.column_alias(join_root, join_root.primary_key)
6
+ type_caster = result_set.column_type primary_key
7
7
 
8
- records = rows.map { |model|
8
+ seen = Hash.new { |h,parent_klass|
9
+ h[parent_klass] = Hash.new { |i,parent_id|
10
+ i[parent_id] = Hash.new { |j,child_klass| j[child_klass] = {} }
11
+ }
12
+ }
13
+
14
+ model_cache = Hash.new { |h,klass| h[klass] = {} }
15
+ parents = model_cache[join_root]
16
+ column_aliases = aliases.column_aliases join_root
17
+
18
+ result_set.each { |row_hash|
9
19
  # CPK
10
- #primary_id = model[primary_key]
11
- primary_id = if primary_key.kind_of?(Array)
12
- primary_key.map {|key| model[key]}
20
+ #primary_id = type_caster.type_cast row_hash[primary_key]
21
+ primary_id = if row_hash[primary_key].kind_of?(Array)
22
+ row_hash[primary_key].map {|key| type_caster.type_cast key}
13
23
  else
14
- model[primary_key]
24
+ type_caster.type_cast row_hash[primary_key]
15
25
  end
16
- parent = parents[primary_id] ||= join_base.instantiate(model)
17
- construct(parent, @associations, join_associations, model)
18
- parent
19
- }.uniq
26
+ parent = parents[primary_id] ||= join_root.instantiate(row_hash, column_aliases)
27
+ construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
28
+ }
20
29
 
21
- remove_duplicate_results!(base_klass, records, @associations)
22
- records
30
+ parents.values
23
31
  end
24
32
 
25
33
  protected
@@ -68,4 +76,4 @@ module ActiveRecord
68
76
  end
69
77
  end
70
78
  end
71
- end
79
+ end
@@ -2,15 +2,15 @@ module ActiveRecord
2
2
  module Associations
3
3
  class JoinDependency
4
4
  class JoinAssociation
5
- def build_constraint(reflection, table, key, foreign_table, foreign_key)
5
+ def build_constraint(klass, table, key, foreign_table, foreign_key)
6
6
  # CPK
7
7
  # constraint = table[key].eq(foreign_table[foreign_key])
8
8
  constraint = cpk_join_predicate(table, key, foreign_table, foreign_key)
9
9
 
10
- if reflection.klass.finder_needs_type_condition?
10
+ if klass.finder_needs_type_condition?
11
11
  constraint = table.create_and([
12
12
  constraint,
13
- reflection.klass.send(:type_condition, table)
13
+ klass.send(:type_condition, table)
14
14
  ])
15
15
  end
16
16