composite_primary_keys 8.1.1 → 8.1.2

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +4 -1
  3. data/lib/composite_primary_keys.rb +5 -1
  4. data/lib/composite_primary_keys/arel/visitors/to_sql.rb +24 -0
  5. data/lib/composite_primary_keys/associations/association_scope.rb +32 -58
  6. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +22 -22
  7. data/lib/composite_primary_keys/associations/preloader/association.rb +12 -6
  8. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +19 -19
  9. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +1 -2
  10. data/lib/composite_primary_keys/attribute_methods/read.rb +2 -2
  11. data/lib/composite_primary_keys/attribute_methods/write.rb +1 -1
  12. data/lib/composite_primary_keys/composite_predicates.rb +55 -50
  13. data/lib/composite_primary_keys/composite_relation.rb +48 -48
  14. data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +2 -2
  15. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +19 -46
  16. data/lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb +8 -4
  17. data/lib/composite_primary_keys/fixtures.rb +26 -22
  18. data/lib/composite_primary_keys/locking/optimistic.rb +54 -55
  19. data/lib/composite_primary_keys/relation.rb +15 -8
  20. data/lib/composite_primary_keys/relation/batches.rb +23 -19
  21. data/lib/composite_primary_keys/relation/calculations.rb +4 -2
  22. data/lib/composite_primary_keys/relation/finder_methods.rb +33 -27
  23. data/lib/composite_primary_keys/relation/predicate_builder.rb +18 -3
  24. data/lib/composite_primary_keys/relation/query_methods.rb +41 -41
  25. data/lib/composite_primary_keys/sanitization.rb +7 -5
  26. data/lib/composite_primary_keys/validations/uniqueness.rb +20 -16
  27. data/lib/composite_primary_keys/version.rb +1 -1
  28. data/tasks/databases/oracle.rake +27 -25
  29. data/tasks/databases/oracle_enhanced.rake +27 -0
  30. data/test/connections/databases.ci.yml +15 -15
  31. data/test/connections/native_oracle/connection.rb +11 -11
  32. data/test/connections/native_oracle_enhanced/connection.rb +16 -16
  33. data/test/fixtures/comment.rb +7 -7
  34. data/test/fixtures/db_definitions/db2-create-tables.sql +126 -126
  35. data/test/fixtures/db_definitions/db2-drop-tables.sql +18 -18
  36. data/test/fixtures/db_definitions/oracle.drop.sql +48 -45
  37. data/test/fixtures/db_definitions/oracle.sql +236 -223
  38. data/test/fixtures/dorm.rb +2 -2
  39. data/test/fixtures/membership.rb +6 -6
  40. data/test/fixtures/membership_statuses.yml +16 -16
  41. data/test/fixtures/memberships.yml +10 -10
  42. data/test/fixtures/product_tariffs.yml +14 -14
  43. data/test/fixtures/reference_code.rb +7 -7
  44. data/test/fixtures/restaurants_suburb.rb +2 -2
  45. data/test/fixtures/suburb.rb +5 -5
  46. data/test/fixtures/topic.rb +5 -5
  47. data/test/fixtures/topic_source.rb +6 -6
  48. data/test/fixtures/topic_sources.yml +3 -3
  49. data/test/fixtures/topics.yml +8 -8
  50. data/test/fixtures/users.yml +10 -10
  51. data/test/test_attribute_methods.rb +63 -63
  52. data/test/test_calculations.rb +42 -37
  53. data/test/test_callbacks.rb +99 -99
  54. data/test/test_delete.rb +28 -21
  55. data/test/test_delete_all.rb +5 -4
  56. data/test/test_dumpable.rb +15 -15
  57. data/test/test_nested_attributes.rb +124 -124
  58. data/test/test_optimistic.rb +18 -18
  59. data/test/test_polymorphic.rb +1 -1
  60. data/test/test_predicates.rb +40 -40
  61. data/test/test_santiago.rb +23 -23
  62. data/test/test_suite.rb +34 -34
  63. data/test/test_touch.rb +23 -23
  64. data/test/test_update.rb +71 -71
  65. metadata +9 -8
  66. data/lib/composite_primary_keys/model_schema.rb +0 -15
@@ -21,7 +21,7 @@ module CompositePrimaryKeys
21
21
  column_alias = column_name
22
22
 
23
23
  bind_values = nil
24
-
24
+
25
25
  # CPK
26
26
  # if operation == "count" && (relation.limit_value || relation.offset_value)
27
27
  if operation == "count"
@@ -36,6 +36,7 @@ module CompositePrimaryKeys
36
36
  select_value = operation_over_aggregate_column(column, operation, distinct)
37
37
 
38
38
  column_alias = select_value.alias
39
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
39
40
  relation.select_values = [select_value]
40
41
 
41
42
  query_builder = relation.arel
@@ -69,6 +70,7 @@ module CompositePrimaryKeys
69
70
  subquery = relation.arel.as(subquery_alias)
70
71
 
71
72
  sm = Arel::SelectManager.new relation.engine
73
+ sm.bind_values = relation.arel.bind_values
72
74
  # CPK
73
75
  # select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
74
76
  select_value = operation_over_aggregate_column(Arel.sql("*"), 'count', false)
@@ -76,4 +78,4 @@ module CompositePrimaryKeys
76
78
  end
77
79
  end
78
80
  end
79
- end
81
+ end
@@ -11,7 +11,7 @@ module CompositePrimaryKeys
11
11
  if relation.limit_value
12
12
  limited_ids = limited_ids_for(relation)
13
13
  # CPK
14
- #limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
14
+ # limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
15
15
  limited_ids.empty? ? relation.none! : relation.where!(cpk_in_predicate(table, self.primary_keys, limited_ids))
16
16
  end
17
17
  relation.except(:limit, :offset)
@@ -20,16 +20,17 @@ module CompositePrimaryKeys
20
20
 
21
21
  def limited_ids_for(relation)
22
22
  # CPK
23
- #values = @klass.connection.columns_for_distinct(
24
- # "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
23
+ # values = @klass.connection.columns_for_distinct(
24
+ # "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
25
25
  columns = @klass.primary_keys.map do |key|
26
26
  "#{quoted_table_name}.#{connection.quote_column_name(key)}"
27
27
  end
28
28
  values = @klass.connection.columns_for_distinct(columns, relation.order_values)
29
29
 
30
30
  relation = relation.except(:select).select(values).distinct!
31
+ arel = relation.arel
31
32
 
32
- id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
33
+ id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
33
34
 
34
35
  # CPK
35
36
  #id_rows.map {|row| row[primary_key]}
@@ -37,12 +38,14 @@ module CompositePrimaryKeys
37
38
  end
38
39
 
39
40
  def exists?(conditions = :none)
40
- # conditions can be:
41
- # Array - ['department_id = ? and location_id = ?', 1, 1]
42
- # Array -> [1,2]
43
- # CompositeKeys -> [1,2]
41
+ if ::ActiveRecord::Base === conditions
42
+ conditions = conditions.id
43
+ ::ActiveSupport::Deprecation.warn(<<-MSG.squish)
44
+ You are passing an instance of ActiveRecord::Base to `exists?`.
45
+ Please pass the id of the object by calling `.id`
46
+ MSG
47
+ end
44
48
 
45
- conditions = conditions.id if ::ActiveRecord::Base === conditions
46
49
  return false if !conditions
47
50
 
48
51
  relation = apply_join_dependency(self, construct_join_dependency)
@@ -50,14 +53,14 @@ module CompositePrimaryKeys
50
53
 
51
54
  relation = relation.except(:select, :order).select(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit(1)
52
55
 
53
- # CPK
54
- #case conditions
55
- #when Array, Hash
56
- # relation = relation.where(conditions)
57
- #else
58
- # relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
59
- #end
60
-
56
+ # case conditions
57
+ # when Array, Hash
58
+ # relation = relation.where(conditions)
59
+ # else
60
+ # unless conditions == :none
61
+ # relation = relation.where(primary_key => conditions)
62
+ # end
63
+ # end
61
64
  case conditions
62
65
  when CompositePrimaryKeys::CompositeKeys
63
66
  relation = relation.where(cpk_id_predicate(table, primary_key, conditions))
@@ -80,14 +83,14 @@ module CompositePrimaryKeys
80
83
  raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
81
84
 
82
85
  # CPK
83
- #expects_array = ids.first.kind_of?(Array)
86
+ # expects_array = ids.first.kind_of?(Array)
84
87
  ids = CompositePrimaryKeys.normalize(ids)
85
88
  expects_array = ids.flatten != ids.flatten(1)
86
89
 
87
90
  return ids.first if expects_array && ids.first.empty?
88
91
 
89
92
  # CPK
90
- #ids = ids.flatten.compact.uniq
93
+ # ids = ids.flatten.compact.uniq
91
94
  ids = expects_array ? ids.first : ids
92
95
 
93
96
  case ids.size
@@ -105,24 +108,27 @@ module CompositePrimaryKeys
105
108
 
106
109
  def find_one(id)
107
110
  # CPK
108
- #id = id.id if ActiveRecord::Base === id
109
- id = id.id if ::ActiveRecord::Base === id
111
+ # if ActiveRecord::Base === id
112
+ if ::ActiveRecord::Base === id
113
+ id = id.id
114
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
115
+ You are passing an instance of ActiveRecord::Base to `find`.
116
+ Please pass the id of the object by calling `.id`
117
+ MSG
118
+ end
110
119
 
111
- # CPK
112
- #column = columns_hash[primary_key]
113
- #substitute = connection.substitute_at(column, bind_values.length)
114
- #relation = where(table[primary_key].eq(substitute))
115
- #relation.bind_values += [[column, id]]
116
- #record = relation.take
117
120
  relation = self
118
121
  values = primary_keys.each_with_index.map do |primary_key, i|
119
122
  column = columns_hash[primary_key]
120
123
  relation.bind_values += [[column, id[i]]]
121
124
  connection.substitute_at(column, bind_values.length - 1)
122
125
  end
126
+
123
127
  relation = relation.where(cpk_id_predicate(table, primary_keys, values))
124
128
  record = relation.take
129
+
125
130
  raise_record_not_found_exception!(id, 0, 1) unless record
131
+
126
132
  record
127
133
  end
128
134
 
@@ -2,12 +2,27 @@ module ActiveRecord
2
2
  class PredicateBuilder
3
3
  def self.expand(klass, table, column, value)
4
4
  queries = []
5
- if klass && reflection = klass.reflect_on_association(column.to_sym)
6
- if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
5
+
6
+ # Find the foreign key when using queries such as:
7
+ # Post.where(author: author)
8
+ #
9
+ # For polymorphic relationships, find the foreign key and type:
10
+ # PriceEstimate.where(estimate_of: treasure)
11
+ if klass && reflection = klass._reflect_on_association(column)
12
+ base_class = polymorphic_base_class_from_value(value)
13
+
14
+ if reflection.polymorphic? && base_class
7
15
  queries << build(table[reflection.foreign_type], base_class)
8
16
  end
9
17
 
10
18
  column = reflection.foreign_key
19
+
20
+ # CPK
21
+ # if base_class
22
+ if base_class && !(Base === value && value.composite?)
23
+ primary_key = reflection.association_primary_key(base_class)
24
+ value = convert_value_to_association_ids(value, primary_key)
25
+ end
11
26
  end
12
27
 
13
28
  #CPK
@@ -19,4 +34,4 @@ module ActiveRecord
19
34
  queries
20
35
  end
21
36
  end
22
- end
37
+ end
@@ -1,41 +1,41 @@
1
- module CompositePrimaryKeys::ActiveRecord::QueryMethods
2
-
3
- def reverse_sql_order(order_query)
4
- # CPK
5
- # order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
6
-
7
- # break apart CPKs
8
- order_query = primary_key.map do |key|
9
- "#{quoted_table_name}.#{connection.quote_column_name(key)} ASC"
10
- end if order_query.empty?
11
-
12
- order_query.map do |o|
13
- case o
14
- when Arel::Nodes::Ordering
15
- o.reverse
16
- when String, Symbol
17
- o.to_s.split(',').collect do |s|
18
- s.strip!
19
- s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
20
- end
21
- else
22
- o
23
- end
24
- end.flatten
25
- end
26
-
27
-
28
- def order(*args)
29
- args.map! do |arg|
30
- if arg.is_a?(Arel::Nodes::Ordering) && arg.expr.name.is_a?(Array)
31
- arg = arg.expr.name.map do |key|
32
- cloned_node = arg.clone
33
- cloned_node.expr.name = key
34
- cloned_node
35
- end
36
- end
37
- arg
38
- end if composite?
39
- super(*args)
40
- end
41
- end
1
+ module CompositePrimaryKeys::ActiveRecord::QueryMethods
2
+
3
+ def reverse_sql_order(order_query)
4
+ # CPK
5
+ # order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
6
+
7
+ # break apart CPKs
8
+ order_query = primary_key.map do |key|
9
+ "#{quoted_table_name}.#{connection.quote_column_name(key)} ASC"
10
+ end if order_query.empty?
11
+
12
+ order_query.map do |o|
13
+ case o
14
+ when Arel::Nodes::Ordering
15
+ o.reverse
16
+ when String, Symbol
17
+ o.to_s.split(',').collect do |s|
18
+ s.strip!
19
+ s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
20
+ end
21
+ else
22
+ o
23
+ end
24
+ end.flatten
25
+ end
26
+
27
+
28
+ def order(*args)
29
+ args.map! do |arg|
30
+ if arg.is_a?(Arel::Nodes::Ordering) && arg.expr.name.is_a?(Array)
31
+ arg = arg.expr.name.map do |key|
32
+ cloned_node = arg.clone
33
+ cloned_node.expr.name = key
34
+ cloned_node
35
+ end
36
+ end
37
+ arg
38
+ end if composite?
39
+ super(*args)
40
+ end
41
+ end
@@ -16,6 +16,8 @@ module ActiveRecord
16
16
  def expand_hash_conditions_for_aggregates(attrs)
17
17
  expanded_attrs = {}
18
18
  attrs.each do |attr, value|
19
+ # CPK
20
+ # if aggregation = reflect_on_aggregation(attr.to_sym)
19
21
  if attr.is_a?(CompositePrimaryKeys::CompositeKeys)
20
22
  attr.each_with_index do |key,i|
21
23
  expanded_attrs[key] = value.respond_to?(:flatten) ? value.flatten[i] : value
@@ -38,13 +40,13 @@ module ActiveRecord
38
40
 
39
41
  def quoted_id
40
42
  # CPK
41
- #quote_value(id, column_for_attribute(self.class.primary_key))
43
+ # self.class.quote_value(id, column_for_attribute(self.class.primary_key))
42
44
  if self.composite?
43
- [self.class.primary_keys, ids].
44
- transpose.
45
- map {|attr_name,id| quote_value(id, column_for_attribute(attr_name))}
45
+ [self.class.primary_keys, ids].transpose.map { |attr_name,id|
46
+ self.class.quote_value(id, column_for_attribute(attr_name))
47
+ }
46
48
  else
47
- quote_value(id, column_for_attribute(self.class.primary_key))
49
+ self.class.quote_value(id, column_for_attribute(self.class.primary_key))
48
50
  end
49
51
  end
50
52
  end
@@ -6,24 +6,28 @@ module ActiveRecord
6
6
  table = finder_class.arel_table
7
7
  value = map_enum_attribute(finder_class, attribute, value)
8
8
 
9
- relation = build_relation(finder_class, table, attribute, value)
10
- # CPK
11
- # relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
12
- if record.persisted?
13
- not_eq_conditions = Array(finder_class.primary_key).zip(Array(record.send(:id))).map do |name, value|
14
- table[name.to_sym].not_eq(value)
15
- end
9
+ begin
10
+ relation = build_relation(finder_class, table, attribute, value)
11
+ # CPK
12
+ # relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
13
+ if record.persisted?
14
+ not_eq_conditions = Array(finder_class.primary_key).zip(Array(record.send(:id))).map do |name, value|
15
+ table[name.to_sym].not_eq(value)
16
+ end
16
17
 
17
- condition = not_eq_conditions.shift
18
- not_eq_conditions.each do |not_eq_condition|
19
- condition = condition.or(not_eq_condition)
18
+ condition = not_eq_conditions.shift
19
+ not_eq_conditions.each do |not_eq_condition|
20
+ condition = condition.or(not_eq_condition)
21
+ end
22
+ relation = relation.and(condition)
20
23
  end
21
- relation = relation.and(condition)
24
+ # End CPK
25
+ relation = scope_relation(record, table, relation)
26
+ relation = finder_class.unscoped.where(relation)
27
+ relation = relation.merge(options[:conditions]) if options[:conditions]
28
+ rescue RangeError
29
+ relation = finder_class.none
22
30
  end
23
-
24
- relation = scope_relation(record, table, relation)
25
- relation = finder_class.unscoped.where(relation)
26
- relation = relation.merge(options[:conditions]) if options[:conditions]
27
31
 
28
32
  if relation.exists?
29
33
  error_options = options.except(:case_sensitive, :scope, :conditions)
@@ -34,4 +38,4 @@ module ActiveRecord
34
38
  end
35
39
  end
36
40
  end
37
- end
41
+ end
@@ -2,7 +2,7 @@ module CompositePrimaryKeys
2
2
  module VERSION
3
3
  MAJOR = 8
4
4
  MINOR = 1
5
- TINY = 1
5
+ TINY = 2
6
6
  STRING = [MAJOR, MINOR, TINY].join('.')
7
7
  end
8
8
  end
@@ -1,25 +1,27 @@
1
- require File.join(PROJECT_ROOT, 'lib', 'composite_primary_keys')
2
- require File.join(PROJECT_ROOT, 'test', 'connections', 'connection_spec')
3
-
4
- namespace :oracle do
5
- desc 'Build the Oracle test database'
6
- task :build_database => :load_connection do
7
- options_str = connection_string
8
-
9
- schema = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.sql')
10
- sh %( sqlplus #{options_str} < #{schema} )
11
- end
12
-
13
- desc 'Drop the Oracle test database'
14
- task :drop_database => :load_connection do
15
- options_str = connection_string
16
- sh %( sqlplus #{options_str} < #{File.join(SCHEMA_PATH, 'oracle.drop.sql')} )
17
- end
18
-
19
- desc 'Rebuild the Oracle test database'
20
- task :rebuild_database => [:drop_database, :build_database]
21
-
22
- task :load_connection do
23
- require File.join(PROJECT_ROOT, "test", "connections", "native_oracle", "connection")
24
- end
25
- end
1
+ require File.join(PROJECT_ROOT, 'lib', 'composite_primary_keys')
2
+ require File.join(PROJECT_ROOT, 'test', 'connections', 'connection_spec')
3
+
4
+ namespace :oracle do
5
+ desc 'Build the Oracle test database'
6
+ task :build_database => :load_connection do
7
+ options_str = connection_string
8
+
9
+ sql = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.sql')
10
+ sh %( sqlplus #{options_str} < #{sql} )
11
+ end
12
+
13
+ desc 'Drop the Oracle test database'
14
+ task :drop_database => :load_connection do
15
+ options_str = connection_string
16
+
17
+ sql = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.drop.sql')
18
+ sh %( sqlplus #{options_str} < #{sql} )
19
+ end
20
+
21
+ desc 'Rebuild the Oracle test database'
22
+ task :rebuild_database => [:drop_database, :build_database]
23
+
24
+ task :load_connection do
25
+ require File.join(PROJECT_ROOT, "test", "connections", "native_oracle", "connection")
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(PROJECT_ROOT, 'lib', 'composite_primary_keys')
2
+ require File.join(PROJECT_ROOT, 'test', 'connections', 'connection_spec')
3
+
4
+ namespace :oracle_enhanced do
5
+ desc 'Build the Oracle test database'
6
+ task :build_database => :load_connection do
7
+ options_str = connection_string
8
+
9
+ sql = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.sql')
10
+ sh %( sqlplus #{options_str} < #{sql} )
11
+ end
12
+
13
+ desc 'Drop the Oracle test database'
14
+ task :drop_database => :load_connection do
15
+ options_str = connection_string
16
+
17
+ sql = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'oracle.drop.sql')
18
+ sh %( sqlplus #{options_str} < #{sql} )
19
+ end
20
+
21
+ desc 'Rebuild the Oracle test database'
22
+ task :rebuild_database => [:drop_database, :build_database]
23
+
24
+ task :load_connection do
25
+ require File.join(PROJECT_ROOT, "test", "connections", "native_oracle_enhanced", "connection")
26
+ end
27
+ end
@@ -1,15 +1,15 @@
1
- mysql:
2
- adapter: mysql2
3
- username: travis
4
- password: ""
5
- database: composite_primary_keys_unittest
6
-
7
- sqlite3:
8
- adapter: sqlite3
9
- database: db/composite_primary_keys_unittest.sqlite
10
-
11
- postgresql:
12
- adapter: postgresql
13
- database: composite_primary_keys_unittest
14
- username: postgres
15
- host: localhost
1
+ mysql:
2
+ adapter: mysql2
3
+ username: travis
4
+ password: ""
5
+ database: composite_primary_keys_unittest
6
+
7
+ sqlite3:
8
+ adapter: sqlite3
9
+ database: db/composite_primary_keys_unittest.sqlite
10
+
11
+ postgresql:
12
+ adapter: postgresql
13
+ database: composite_primary_keys_unittest
14
+ username: postgres
15
+ host: localhost