composite_primary_keys 7.0.16 → 8.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +600 -623
  3. data/lib/composite_primary_keys.rb +113 -115
  4. data/lib/composite_primary_keys/associations/association.rb +23 -23
  5. data/lib/composite_primary_keys/associations/association_scope.rb +73 -77
  6. data/lib/composite_primary_keys/associations/collection_association.rb +15 -0
  7. data/lib/composite_primary_keys/associations/has_many_association.rb +69 -56
  8. data/lib/composite_primary_keys/associations/has_many_through_association.rb +30 -28
  9. data/lib/composite_primary_keys/associations/join_dependency.rb +87 -89
  10. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +22 -22
  11. data/lib/composite_primary_keys/associations/preloader/association.rb +90 -78
  12. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +19 -19
  13. data/lib/composite_primary_keys/associations/singular_association.rb +15 -0
  14. data/lib/composite_primary_keys/attribute_methods.rb +9 -0
  15. data/lib/composite_primary_keys/attribute_methods/dirty.rb +29 -26
  16. data/lib/composite_primary_keys/attribute_methods/read.rb +19 -34
  17. data/lib/composite_primary_keys/attribute_methods/write.rb +30 -36
  18. data/lib/composite_primary_keys/attribute_set/builder.rb +20 -0
  19. data/lib/composite_primary_keys/base.rb +135 -129
  20. data/lib/composite_primary_keys/composite_arrays.rb +30 -30
  21. data/lib/composite_primary_keys/composite_predicates.rb +50 -50
  22. data/lib/composite_primary_keys/composite_relation.rb +48 -48
  23. data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +2 -4
  24. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +46 -60
  25. data/lib/composite_primary_keys/core.rb +69 -47
  26. data/lib/composite_primary_keys/fixtures.rb +22 -22
  27. data/lib/composite_primary_keys/persistence.rb +56 -60
  28. data/lib/composite_primary_keys/relation.rb +68 -56
  29. data/lib/composite_primary_keys/relation/calculations.rb +79 -75
  30. data/lib/composite_primary_keys/relation/finder_methods.rb +175 -196
  31. data/lib/composite_primary_keys/relation/query_methods.rb +40 -40
  32. data/lib/composite_primary_keys/sanitization.rb +52 -52
  33. data/lib/composite_primary_keys/validations/uniqueness.rb +36 -37
  34. data/lib/composite_primary_keys/version.rb +8 -8
  35. data/tasks/databases/oracle.rake +25 -25
  36. data/tasks/databases/sqlserver.rake +27 -40
  37. data/test/abstract_unit.rb +113 -113
  38. data/test/connections/databases.ci.yml +15 -15
  39. data/test/connections/databases.example.yml +18 -18
  40. data/test/connections/native_oracle/connection.rb +11 -11
  41. data/test/connections/native_oracle_enhanced/connection.rb +16 -16
  42. data/test/connections/native_sqlserver/connection.rb +11 -14
  43. data/test/fixtures/comment.rb +7 -7
  44. data/test/fixtures/db_definitions/db2-create-tables.sql +125 -126
  45. data/test/fixtures/db_definitions/db2-drop-tables.sql +18 -18
  46. data/test/fixtures/db_definitions/mysql.sql +207 -208
  47. data/test/fixtures/db_definitions/oracle.drop.sql +45 -45
  48. data/test/fixtures/db_definitions/oracle.sql +222 -223
  49. data/test/fixtures/db_definitions/postgresql.sql +209 -210
  50. data/test/fixtures/db_definitions/sqlite.sql +196 -197
  51. data/test/fixtures/db_definitions/sqlserver.drop.sql +91 -94
  52. data/test/fixtures/db_definitions/sqlserver.sql +225 -232
  53. data/test/fixtures/dorm.rb +2 -2
  54. data/test/fixtures/employee.rb +5 -5
  55. data/test/fixtures/membership.rb +6 -6
  56. data/test/fixtures/membership_statuses.yml +16 -16
  57. data/test/fixtures/memberships.yml +10 -10
  58. data/test/fixtures/product_tariffs.yml +14 -14
  59. data/test/fixtures/reference_code.rb +7 -7
  60. data/test/fixtures/restaurants_suburb.rb +2 -2
  61. data/test/fixtures/suburb.rb +5 -5
  62. data/test/fixtures/topic.rb +5 -5
  63. data/test/fixtures/topic_source.rb +6 -6
  64. data/test/fixtures/topic_sources.yml +3 -3
  65. data/test/fixtures/topics.yml +8 -8
  66. data/test/fixtures/users.yml +10 -10
  67. data/test/test_associations.rb +295 -275
  68. data/test/test_attribute_methods.rb +63 -63
  69. data/test/test_attributes.rb +60 -60
  70. data/test/test_calculations.rb +37 -42
  71. data/test/test_callbacks.rb +99 -99
  72. data/test/test_create.rb +112 -112
  73. data/test/test_delete.rb +148 -152
  74. data/test/test_delete_all.rb +28 -26
  75. data/test/test_dumpable.rb +15 -15
  76. data/test/test_enum.rb +21 -20
  77. data/test/test_equal.rb +26 -26
  78. data/test/test_find.rb +118 -118
  79. data/test/test_habtm.rb +113 -113
  80. data/test/test_nested_attributes.rb +124 -124
  81. data/test/test_polymorphic.rb +26 -26
  82. data/test/test_predicates.rb +40 -40
  83. data/test/test_santiago.rb +23 -23
  84. data/test/test_suite.rb +33 -34
  85. data/test/test_touch.rb +23 -23
  86. data/test/test_tutorial_example.rb +21 -21
  87. data/test/test_update.rb +71 -71
  88. metadata +9 -13
  89. data/lib/composite_primary_keys/arel/visitors/to_sql.rb +0 -20
  90. data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +0 -59
  91. data/lib/composite_primary_keys/associations/join_dependency/join_part.rb +0 -39
  92. data/lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb +0 -46
  93. data/lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb +0 -17
  94. data/lib/composite_primary_keys/locking/optimistic.rb +0 -55
  95. data/test/test_optimistic.rb +0 -18
@@ -1,30 +1,30 @@
1
- module CompositePrimaryKeys
2
- ID_SEP = ','
3
- ID_SET_SEP = ';'
4
-
5
- module ArrayExtension
6
- def to_composite_keys
7
- CompositeKeys.new(self)
8
- end
9
- end
10
-
11
- class CompositeKeys < Array
12
- def self.parse(value)
13
- case value
14
- when Array
15
- value.to_composite_keys
16
- when String
17
- self.new(value.split(ID_SEP))
18
- else
19
- raise(ArgumentError, "Unsupported type: #{value}")
20
- end
21
- end
22
-
23
- def to_s
24
- # Doing this makes it easier to parse Base#[](attr_name)
25
- join(ID_SEP)
26
- end
27
- end
28
- end
29
-
30
- Array.send(:include, CompositePrimaryKeys::ArrayExtension)
1
+ module CompositePrimaryKeys
2
+ ID_SEP = ','
3
+ ID_SET_SEP = ';'
4
+
5
+ module ArrayExtension
6
+ def to_composite_keys
7
+ CompositeKeys.new(self)
8
+ end
9
+ end
10
+
11
+ class CompositeKeys < Array
12
+ def self.parse(value)
13
+ case value
14
+ when Array
15
+ value.to_composite_keys
16
+ when String
17
+ self.new(value.split(ID_SEP))
18
+ else
19
+ raise(ArgumentError, "Unsupported type: #{value}")
20
+ end
21
+ end
22
+
23
+ def to_s
24
+ # Doing this makes it easier to parse Base#[](attr_name)
25
+ join(ID_SEP)
26
+ end
27
+ end
28
+ end
29
+
30
+ Array.send(:include, CompositePrimaryKeys::ArrayExtension)
@@ -1,51 +1,51 @@
1
- module CompositePrimaryKeys
2
- module Predicates
3
- def cpk_and_predicate(predicates)
4
- if predicates.length == 1
5
- predicates.first
6
- else
7
- Arel::Nodes::And.new(predicates)
8
- end
9
- end
10
-
11
- def cpk_or_predicate(predicates)
12
- or_predicate = predicates.map do |predicate|
13
- ::Arel::Nodes::Grouping.new(predicate)
14
- end.inject do |memo, node|
15
- ::Arel::Nodes::Or.new(memo, node)
16
- end
17
- ::Arel::Nodes::Grouping.new(or_predicate)
18
- end
19
-
20
- def cpk_id_predicate(table, keys, values)
21
- eq_predicates = keys.zip(values).map do |key, value|
22
- table[key].eq(value)
23
- end
24
- cpk_and_predicate(eq_predicates)
25
- end
26
-
27
- def cpk_join_predicate(table1, key1, table2, key2)
28
- key1_fields = Array(key1).map {|key| table1[key]}
29
- key2_fields = Array(key2).map {|key| table2[key]}
30
-
31
- eq_predicates = key1_fields.zip(key2_fields).map do |key_field1, key_field2|
32
- key_field1.eq(key_field2)
33
- end
34
- cpk_and_predicate(eq_predicates)
35
- end
36
-
37
- def cpk_in_predicate(table, primary_keys, ids)
38
- and_predicates = ids.map do |id|
39
- cpk_id_predicate(table, primary_keys, id)
40
- end
41
- cpk_or_predicate(and_predicates)
42
- end
43
- end
44
- end
45
-
46
- ActiveRecord::Associations::AssociationScope.send(:include, CompositePrimaryKeys::Predicates)
47
- ActiveRecord::Associations::JoinDependency::JoinAssociation.send(:include, CompositePrimaryKeys::Predicates)
48
- ActiveRecord::Associations::Preloader::Association.send(:include, CompositePrimaryKeys::Predicates)
49
- ActiveRecord::Associations::HasManyThroughAssociation.send(:include, CompositePrimaryKeys::Predicates)
50
- ActiveRecord::Relation.send(:include, CompositePrimaryKeys::Predicates)
1
+ module CompositePrimaryKeys
2
+ module Predicates
3
+ def cpk_and_predicate(predicates)
4
+ if predicates.length == 1
5
+ predicates.first
6
+ else
7
+ Arel::Nodes::And.new(predicates)
8
+ end
9
+ end
10
+
11
+ def cpk_or_predicate(predicates)
12
+ or_predicate = predicates.map do |predicate|
13
+ ::Arel::Nodes::Grouping.new(predicate)
14
+ end.inject do |memo, node|
15
+ ::Arel::Nodes::Or.new(memo, node)
16
+ end
17
+ ::Arel::Nodes::Grouping.new(or_predicate)
18
+ end
19
+
20
+ def cpk_id_predicate(table, keys, values)
21
+ eq_predicates = keys.zip(values).map do |key, value|
22
+ table[key].eq(value)
23
+ end
24
+ cpk_and_predicate(eq_predicates)
25
+ end
26
+
27
+ def cpk_join_predicate(table1, key1, table2, key2)
28
+ key1_fields = Array(key1).map {|key| table1[key]}
29
+ key2_fields = Array(key2).map {|key| table2[key]}
30
+
31
+ eq_predicates = key1_fields.zip(key2_fields).map do |key_field1, key_field2|
32
+ key_field1.eq(key_field2)
33
+ end
34
+ cpk_and_predicate(eq_predicates)
35
+ end
36
+
37
+ def cpk_in_predicate(table, primary_keys, ids)
38
+ and_predicates = ids.map do |id|
39
+ cpk_id_predicate(table, primary_keys, id)
40
+ end
41
+ cpk_or_predicate(and_predicates)
42
+ end
43
+ end
44
+ end
45
+
46
+ ActiveRecord::Associations::AssociationScope.send(:include, CompositePrimaryKeys::Predicates)
47
+ ActiveRecord::Associations::JoinDependency::JoinAssociation.send(:include, CompositePrimaryKeys::Predicates)
48
+ ActiveRecord::Associations::Preloader::Association.send(:include, CompositePrimaryKeys::Predicates)
49
+ ActiveRecord::Associations::HasManyThroughAssociation.send(:include, CompositePrimaryKeys::Predicates)
50
+ ActiveRecord::Relation.send(:include, CompositePrimaryKeys::Predicates)
51
51
  ActiveRecord::PredicateBuilder.send(:extend, CompositePrimaryKeys::Predicates)
@@ -1,48 +1,48 @@
1
- module CompositePrimaryKeys
2
- module CompositeRelation
3
- include CompositePrimaryKeys::ActiveRecord::Batches
4
- include CompositePrimaryKeys::ActiveRecord::Calculations
5
- include CompositePrimaryKeys::ActiveRecord::FinderMethods
6
- include CompositePrimaryKeys::ActiveRecord::QueryMethods
7
-
8
- def delete(id_or_array)
9
- # CPK
10
- if self.composite?
11
- id_or_array = if id_or_array.is_a?(CompositePrimaryKeys::CompositeKeys)
12
- [id_or_array]
13
- else
14
- Array(id_or_array)
15
- end
16
-
17
- id_or_array.each do |id|
18
- # Is the passed in id actually a record?
19
- id = id.kind_of?(::ActiveRecord::Base) ? id.id : id
20
- where(cpk_id_predicate(table, self.primary_key, id)).delete_all
21
- end
22
- else
23
- where(primary_key => id_or_array).delete_all
24
- end
25
- end
26
-
27
- def destroy(id_or_array)
28
- # Without CPK:
29
- #if id.is_a?(Array)
30
- # id.map { |one_id| destroy(one_id) }
31
- #else
32
- # find(id).destroy
33
- #end
34
-
35
- id_or_array = if id_or_array.kind_of?(CompositePrimaryKeys::CompositeKeys)
36
- [id_or_array]
37
- else
38
- Array(id_or_array)
39
- end
40
-
41
- id_or_array.each do |id|
42
- where(cpk_id_predicate(table, self.primary_key, id)).each do |record|
43
- record.destroy
44
- end
45
- end
46
- end
47
- end
48
- end
1
+ module CompositePrimaryKeys
2
+ module CompositeRelation
3
+ include CompositePrimaryKeys::ActiveRecord::Batches
4
+ include CompositePrimaryKeys::ActiveRecord::Calculations
5
+ include CompositePrimaryKeys::ActiveRecord::FinderMethods
6
+ include CompositePrimaryKeys::ActiveRecord::QueryMethods
7
+
8
+ def delete(id_or_array)
9
+ # CPK
10
+ if self.composite?
11
+ id_or_array = if id_or_array.is_a?(CompositePrimaryKeys::CompositeKeys)
12
+ [id_or_array]
13
+ else
14
+ Array(id_or_array)
15
+ end
16
+
17
+ id_or_array.each do |id|
18
+ # Is the passed in id actually a record?
19
+ id = id.kind_of?(::ActiveRecord::Base) ? id.id : id
20
+ where(cpk_id_predicate(table, self.primary_key, id)).delete_all
21
+ end
22
+ else
23
+ where(primary_key => id_or_array).delete_all
24
+ end
25
+ end
26
+
27
+ def destroy(id_or_array)
28
+ # Without CPK:
29
+ #if id.is_a?(Array)
30
+ # id.map { |one_id| destroy(one_id) }
31
+ #else
32
+ # find(id).destroy
33
+ #end
34
+
35
+ id_or_array = if id_or_array.kind_of?(CompositePrimaryKeys::CompositeKeys)
36
+ [id_or_array]
37
+ else
38
+ Array(id_or_array)
39
+ end
40
+
41
+ id_or_array.each do |id|
42
+ where(cpk_id_predicate(table, self.primary_key, id)).each do |record|
43
+ record.destroy
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -4,9 +4,6 @@ module ActiveRecord
4
4
  if (adapter.to_s =~ /postgresql/) or (adapter.to_s =~ /postgis/)
5
5
  require "composite_primary_keys/connection_adapters/postgresql_adapter.rb"
6
6
  end
7
- if (adapter.to_s =~ /sqlserver/)
8
- require "composite_primary_keys/connection_adapters/sqlserver_adapter.rb"
9
- end
10
7
  end
11
8
 
12
9
  def self.establish_connection(spec = ENV["DATABASE_URL"])
@@ -64,7 +61,8 @@ module ActiveRecord
64
61
  connection_handler.clear_active_connections!
65
62
  end
66
63
 
67
- delegate :clear_reloadable_connections!, :clear_all_connections!, :to => :connection_handler
64
+ delegate :clear_reloadable_connections!,
65
+ :clear_all_connections!,:verify_active_connections!, :to => :connection_handler
68
66
  end
69
67
  end
70
68
  end
@@ -1,60 +1,46 @@
1
- module ActiveRecord
2
- module ConnectionAdapters
3
- class PostgreSQLAdapter
4
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
5
- unless pk
6
- # Extract the table from the insert sql. Yuck.
7
- table_ref = extract_table_ref_from_insert_sql(sql)
8
- pk = primary_key(table_ref) if table_ref
9
- end
10
-
11
- if pk
12
- # CPK
13
- # select_value("#{sql} RETURNING #{quote_column_name(pk)}")
14
- select_value("#{sql} RETURNING #{quote_column_names(pk)}")
15
- else
16
- super
17
- end
18
- end
19
- alias :create :insert
20
-
21
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
22
- unless pk
23
- # Extract the table from the insert sql. Yuck.
24
- table_ref = extract_table_ref_from_insert_sql(sql)
25
- pk = primary_key(table_ref) if table_ref
26
- end
27
-
28
- # CPK
29
- # sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
30
- sql = "#{sql} RETURNING #{quote_column_names(pk)}" if pk
31
-
32
- [sql, binds]
33
- end
34
-
35
- # Returns a single value if query returns a single element
36
- # otherwise returns an array coresponding to the composite keys
37
- #
38
- def last_inserted_id(result)
39
- row = result && result.rows.first
40
- if Array === row
41
- row.size == 1 ? row[0] : row
42
- end
43
- end
44
-
45
- module Quoting
46
- def quote_column_name(name) #:nodoc:
47
- # CPK
48
- # PGconn.quote_ident(name.to_s)
49
- if name.is_a?(Array)
50
- name.map do |column|
51
- PGconn.quote_ident(column.to_s)
52
- end.join(', ')
53
- else
54
- PGconn.quote_ident(name.to_s)
55
- end
56
- end
57
- end
58
- end
59
- end
60
- end
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter
4
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
5
+ unless pk
6
+ # Extract the table from the insert sql. Yuck.
7
+ table_ref = extract_table_ref_from_insert_sql(sql)
8
+ pk = primary_key(table_ref) if table_ref
9
+ end
10
+
11
+ if pk
12
+ # CPK
13
+ # select_value("#{sql} RETURNING #{quote_column_name(pk)}")
14
+ select_value("#{sql} RETURNING #{quote_column_names(pk)}")
15
+ else
16
+ super
17
+ end
18
+ end
19
+ alias :create :insert
20
+
21
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
22
+ unless pk
23
+ # Extract the table from the insert sql. Yuck.
24
+ table_ref = extract_table_ref_from_insert_sql(sql)
25
+ pk = primary_key(table_ref) if table_ref
26
+ end
27
+
28
+ # CPK
29
+ # sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
30
+ sql = "#{sql} RETURNING #{quote_column_names(pk)}" if pk
31
+
32
+ [sql, binds]
33
+ end
34
+
35
+ # Returns a single value if query returns a single element
36
+ # otherwise returns an array coresponding to the composite keys
37
+ #
38
+ def last_inserted_id(result)
39
+ row = result && result.rows.first
40
+ if Array === row
41
+ row.size == 1 ? row[0] : row
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,47 +1,69 @@
1
- module ActiveRecord
2
- module Core
3
- def init_internals
4
- pk = self.class.primary_key
5
-
6
- # CPK
7
- #@attributes[pk] = nil unless @attributes.key?(pk)
8
- unless self.composite?
9
- @attributes[pk] = nil unless @attributes.key?(pk)
10
- end
11
-
12
- @aggregation_cache = {}
13
- @association_cache = {}
14
- @attributes_cache = {}
15
- @readonly = false
16
- @destroyed = false
17
- @marked_for_destruction = false
18
- @destroyed_by_association = nil
19
- @new_record = true
20
- @txn = nil
21
- @_start_transaction_state = {}
22
- @transaction_state = nil
23
- @reflects_state = [false]
24
- end
25
-
26
- def initialize_dup(other) # :nodoc:
27
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
28
- self.class.initialize_attributes(cloned_attributes, :serialized => false)
29
-
30
- @attributes = cloned_attributes
31
-
32
- # CPK
33
- #@attributes[self.class.primary_key] = nil
34
- Array(self.class.primary_key).each {|key| @attributes[key] = nil}
35
-
36
- run_callbacks(:initialize) unless _initialize_callbacks.empty?
37
-
38
- @aggregation_cache = {}
39
- @association_cache = {}
40
- @attributes_cache = {}
41
-
42
- @new_record = true
43
-
44
- super
45
- end
46
- end
47
- end
1
+ module ActiveRecord
2
+ module Core
3
+ def initialize_dup(other) # :nodoc:
4
+ @attributes = @attributes.dup
5
+ # CPK
6
+ # @attributes.reset(self.class.primary_key)
7
+ Array(self.class.primary_key).each {|key| @attributes.reset(key)}
8
+
9
+ run_callbacks(:initialize) unless _initialize_callbacks.empty?
10
+
11
+ @aggregation_cache = {}
12
+ @association_cache = {}
13
+
14
+ @new_record = true
15
+ @destroyed = false
16
+
17
+ super
18
+ end
19
+
20
+
21
+ end
22
+ end
23
+
24
+
25
+ module CompositePrimaryKeys
26
+ module ActiveRecordCoreConcernIncludedExtension
27
+ extend ActiveSupport::Concern
28
+
29
+ included do
30
+ def self.find(*ids)
31
+ if composite?
32
+ super(cpk_parse_ids(ids))
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ private
39
+ def self.cpk_parse_ids(ids)
40
+ result = []
41
+ ids.each do |id|
42
+ if id.is_a?(String)
43
+ if id.index(",")
44
+ result << [id.split(",")]
45
+ else
46
+ result << [id]
47
+ end
48
+ elsif id.is_a?(Array) && id.count > 1 && id.first.to_s.index(",")
49
+ result << id.map{|subid| subid.split(",")}
50
+ else
51
+ result << [id]
52
+ end
53
+ end
54
+
55
+ copy_to_find_depth, depth = result.dup, -1
56
+
57
+ until copy_to_find_depth == result.flatten
58
+ depth += 1
59
+ copy_to_find_depth = copy_to_find_depth.flatten(1)
60
+ end
61
+
62
+ result = result.flatten(depth)
63
+ return result
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ ActiveRecord::Base.send(:include, CompositePrimaryKeys::ActiveRecordCoreConcernIncludedExtension)