composite_primary_keys 6.0.8 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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