composite_primary_keys 8.1.0 → 8.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +642 -625
  3. data/README.rdoc +5 -2
  4. data/lib/composite_primary_keys.rb +115 -115
  5. data/lib/composite_primary_keys/associations/association.rb +23 -23
  6. data/lib/composite_primary_keys/associations/association_scope.rb +73 -73
  7. data/lib/composite_primary_keys/associations/collection_association.rb +14 -14
  8. data/lib/composite_primary_keys/associations/has_many_association.rb +69 -69
  9. data/lib/composite_primary_keys/associations/join_dependency.rb +87 -87
  10. data/lib/composite_primary_keys/associations/preloader/association.rb +90 -90
  11. data/lib/composite_primary_keys/associations/singular_association.rb +18 -18
  12. data/lib/composite_primary_keys/attribute_methods.rb +9 -9
  13. data/lib/composite_primary_keys/attribute_methods/dirty.rb +29 -29
  14. data/lib/composite_primary_keys/attribute_methods/read.rb +24 -24
  15. data/lib/composite_primary_keys/attribute_methods/write.rb +30 -30
  16. data/lib/composite_primary_keys/attribute_set/builder.rb +19 -19
  17. data/lib/composite_primary_keys/base.rb +129 -135
  18. data/lib/composite_primary_keys/composite_arrays.rb +43 -43
  19. data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +2 -3
  20. data/lib/composite_primary_keys/core.rb +60 -60
  21. data/lib/composite_primary_keys/persistence.rb +56 -56
  22. data/lib/composite_primary_keys/relation.rb +68 -68
  23. data/lib/composite_primary_keys/relation/calculations.rb +78 -78
  24. data/lib/composite_primary_keys/relation/finder_methods.rb +179 -179
  25. data/lib/composite_primary_keys/sanitization.rb +52 -52
  26. data/lib/composite_primary_keys/validations/uniqueness.rb +36 -36
  27. data/lib/composite_primary_keys/version.rb +8 -8
  28. data/tasks/databases/sqlserver.rake +27 -27
  29. data/test/abstract_unit.rb +114 -113
  30. data/test/connections/databases.example.yml +25 -25
  31. data/test/connections/native_sqlserver/connection.rb +11 -11
  32. data/test/fixtures/db_definitions/mysql.sql +218 -218
  33. data/test/fixtures/db_definitions/postgresql.sql +220 -220
  34. data/test/fixtures/db_definitions/sqlite.sql +206 -206
  35. data/test/fixtures/db_definitions/sqlserver.drop.sql +91 -91
  36. data/test/fixtures/db_definitions/sqlserver.sql +226 -226
  37. data/test/fixtures/employee.rb +11 -11
  38. data/test/fixtures/salary.rb +5 -5
  39. data/test/test_associations.rb +341 -340
  40. data/test/test_attributes.rb +60 -60
  41. data/test/test_create.rb +157 -157
  42. data/test/test_delete.rb +158 -158
  43. data/test/test_delete_all.rb +33 -28
  44. data/test/test_enum.rb +21 -21
  45. data/test/test_equal.rb +26 -26
  46. data/test/test_find.rb +119 -118
  47. data/test/test_habtm.rb +117 -113
  48. data/test/test_polymorphic.rb +27 -26
  49. data/test/test_tutorial_example.rb +25 -25
  50. metadata +44 -2
@@ -1,43 +1,43 @@
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
- def self.normalize(ids)
12
- ids.map do |id|
13
- if id.is_a?(Array)
14
- normalize(id)
15
- elsif id.is_a?(String) && id.index(ID_SEP)
16
- id.split(ID_SEP)
17
- else
18
- id
19
- end
20
- end
21
- end
22
-
23
- class CompositeKeys < Array
24
-
25
- def self.parse(value)
26
- case value
27
- when Array
28
- value.to_composite_keys
29
- when String
30
- self.new(value.split(ID_SEP))
31
- else
32
- raise(ArgumentError, "Unsupported type: #{value}")
33
- end
34
- end
35
-
36
- def to_s
37
- # Doing this makes it easier to parse Base#[](attr_name)
38
- join(ID_SEP)
39
- end
40
- end
41
- end
42
-
43
- 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
+ def self.normalize(ids)
12
+ ids.map do |id|
13
+ if id.is_a?(Array)
14
+ normalize(id)
15
+ elsif id.is_a?(String) && id.index(ID_SEP)
16
+ id.split(ID_SEP)
17
+ else
18
+ id
19
+ end
20
+ end
21
+ end
22
+
23
+ class CompositeKeys < Array
24
+
25
+ def self.parse(value)
26
+ case value
27
+ when Array
28
+ value.to_composite_keys
29
+ when String
30
+ self.new(value.split(ID_SEP))
31
+ else
32
+ raise(ArgumentError, "Unsupported type: #{value}")
33
+ end
34
+ end
35
+
36
+ def to_s
37
+ # Doing this makes it easier to parse Base#[](attr_name)
38
+ join(ID_SEP)
39
+ end
40
+ end
41
+ end
42
+
43
+ Array.send(:include, CompositePrimaryKeys::ArrayExtension)
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  end
10
10
  end
11
11
 
12
- def self.establish_connection(spec = ENV["DATABASE_URL"])
12
+ def self.establish_connection(spec = nil)
13
13
  spec ||= ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_sym
14
14
  resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations
15
15
  spec = resolver.spec(spec)
@@ -64,8 +64,7 @@ module ActiveRecord
64
64
  connection_handler.clear_active_connections!
65
65
  end
66
66
 
67
- delegate :clear_reloadable_connections!,
68
- :clear_all_connections!,:verify_active_connections!, :to => :connection_handler
67
+ delegate :clear_reloadable_connections!, :clear_all_connections!, :to => :connection_handler
69
68
  end
70
69
  end
71
70
  end
@@ -1,61 +1,61 @@
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
- module ClassMethods
21
- def find(*ids) # :nodoc:
22
- # We don't have cache keys for this stuff yet
23
- return super unless ids.length == 1
24
- # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
25
- return super if ids.first.kind_of?(Symbol)
26
- return super if block_given? ||
27
- primary_key.nil? ||
28
- default_scopes.any? ||
29
- current_scope ||
30
- columns_hash.include?(inheritance_column) ||
31
- ids.first.kind_of?(Array)
32
-
33
- # CPK
34
- return super if self.composite?
35
-
36
- id = ids.first
37
- if ActiveRecord::Base === id
38
- id = id.id
39
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
40
- You are passing an instance of ActiveRecord::Base to `find`.
41
- Please pass the id of the object by calling `.id`
42
- MSG
43
- end
44
- key = primary_key
45
-
46
- s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
47
- find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
48
- where(key => params.bind).limit(1)
49
- }
50
- }
51
- record = s.execute([id], self, connection).first
52
- unless record
53
- raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
54
- end
55
- record
56
- rescue RangeError
57
- raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
58
- end
59
- end
60
- 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
+ module ClassMethods
21
+ def find(*ids) # :nodoc:
22
+ # We don't have cache keys for this stuff yet
23
+ return super unless ids.length == 1
24
+ # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
25
+ return super if ids.first.kind_of?(Symbol)
26
+ return super if block_given? ||
27
+ primary_key.nil? ||
28
+ default_scopes.any? ||
29
+ current_scope ||
30
+ columns_hash.include?(inheritance_column) ||
31
+ ids.first.kind_of?(Array)
32
+
33
+ # CPK
34
+ return super if self.composite?
35
+
36
+ id = ids.first
37
+ if ActiveRecord::Base === id
38
+ id = id.id
39
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
40
+ You are passing an instance of ActiveRecord::Base to `find`.
41
+ Please pass the id of the object by calling `.id`
42
+ MSG
43
+ end
44
+ key = primary_key
45
+
46
+ s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
47
+ find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
48
+ where(key => params.bind).limit(1)
49
+ }
50
+ }
51
+ record = s.execute([id], self, connection).first
52
+ unless record
53
+ raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
54
+ end
55
+ record
56
+ rescue RangeError
57
+ raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
58
+ end
59
+ end
60
+ end
61
61
  end
@@ -1,57 +1,57 @@
1
- module ActiveRecord
2
- module Persistence
3
- def relation_for_destroy
4
- # CPK
5
- if self.composite?
6
- relation = self.class.unscoped
7
-
8
- Array(self.class.primary_key).each_with_index do |key, index|
9
- column = self.class.columns_hash[key]
10
- substitute = self.class.connection.substitute_at(column, index)
11
- relation = relation.where(self.class.arel_table[key].eq(substitute))
12
- relation.bind_values += [[column, self[key]]]
13
- end
14
-
15
- relation
16
- else
17
- pk = self.class.primary_key
18
- column = self.class.columns_hash[pk]
19
- substitute = self.class.connection.substitute_at(column, 0)
20
-
21
- relation = self.class.unscoped.where(
22
- self.class.arel_table[pk].eq(substitute))
23
-
24
- relation.bind_values = [[column, id]]
25
- relation
26
- end
27
- end
28
-
29
- def touch(*names)
30
- raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
31
-
32
- attributes = timestamp_attributes_for_update_in_model
33
- attributes.concat(names)
34
-
35
- unless attributes.empty?
36
- current_time = current_time_from_proper_timezone
37
- changes = {}
38
-
39
- attributes.each do |column|
40
- column = column.to_s
41
- changes[column] = write_attribute(column, current_time)
42
- end
43
-
44
- changes[self.class.locking_column] = increment_lock if locking_enabled?
45
-
46
- clear_attribute_changes(changes.keys)
47
- primary_key = self.class.primary_key
48
- # CPK
49
- #self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
50
- primary_key_predicate = self.class.unscoped.cpk_id_predicate(self.class.arel_table, Array(primary_key), Array(id))
51
- self.class.unscoped.where(primary_key_predicate).update_all(changes) == 1
52
- else
53
- true
54
- end
55
- end
56
- end
1
+ module ActiveRecord
2
+ module Persistence
3
+ def relation_for_destroy
4
+ # CPK
5
+ if self.composite?
6
+ relation = self.class.unscoped
7
+
8
+ Array(self.class.primary_key).each_with_index do |key, index|
9
+ column = self.class.columns_hash[key]
10
+ substitute = self.class.connection.substitute_at(column, index)
11
+ relation = relation.where(self.class.arel_table[key].eq(substitute))
12
+ relation.bind_values += [[column, self[key]]]
13
+ end
14
+
15
+ relation
16
+ else
17
+ pk = self.class.primary_key
18
+ column = self.class.columns_hash[pk]
19
+ substitute = self.class.connection.substitute_at(column, 0)
20
+
21
+ relation = self.class.unscoped.where(
22
+ self.class.arel_table[pk].eq(substitute))
23
+
24
+ relation.bind_values = [[column, id]]
25
+ relation
26
+ end
27
+ end
28
+
29
+ def touch(*names)
30
+ raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
31
+
32
+ attributes = timestamp_attributes_for_update_in_model
33
+ attributes.concat(names)
34
+
35
+ unless attributes.empty?
36
+ current_time = current_time_from_proper_timezone
37
+ changes = {}
38
+
39
+ attributes.each do |column|
40
+ column = column.to_s
41
+ changes[column] = write_attribute(column, current_time)
42
+ end
43
+
44
+ changes[self.class.locking_column] = increment_lock if locking_enabled?
45
+
46
+ clear_attribute_changes(changes.keys)
47
+ primary_key = self.class.primary_key
48
+ # CPK
49
+ #self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
50
+ primary_key_predicate = self.class.unscoped.cpk_id_predicate(self.class.arel_table, Array(primary_key), Array(id))
51
+ self.class.unscoped.where(primary_key_predicate).update_all(changes) == 1
52
+ else
53
+ true
54
+ end
55
+ end
56
+ end
57
57
  end
@@ -1,68 +1,68 @@
1
- module ActiveRecord
2
- class Relation
3
- alias :initialize_without_cpk :initialize
4
- def initialize(klass, table, values = {})
5
- initialize_without_cpk(klass, table, values)
6
- add_cpk_support if klass && klass.composite?
7
- end
8
-
9
- alias :initialize_copy_without_cpk :initialize_copy
10
- def initialize_copy(other)
11
- initialize_copy_without_cpk(other)
12
- add_cpk_support if klass.composite?
13
- end
14
-
15
- def add_cpk_support
16
- extend CompositePrimaryKeys::CompositeRelation
17
- end
18
-
19
- # CPK adds this so that it finds the Equality nodes beneath the And node:
20
- # equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
21
- # node.left.relation.name == table_name
22
- # }
23
- alias :where_values_hash_without_cpk :where_values_hash
24
- def where_values_hash(relation_table_name = table_name)
25
- nodes_from_and = where_values.grep(Arel::Nodes::And).map {|and_node| and_node.children.grep(Arel::Nodes::Equality) }.flatten
26
-
27
- equalities = (nodes_from_and + where_values.grep(Arel::Nodes::Equality)).find_all { |node|
28
- node.left.relation.name == relation_table_name
29
- }
30
-
31
- binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
32
-
33
- Hash[equalities.map { |where|
34
- name = where.left.name
35
- [name, binds.fetch(name.to_s) {
36
- case where.right
37
- when Array then where.right.map(&:val)
38
- else
39
- where.right.val
40
- end
41
- }]
42
- }]
43
- end
44
-
45
- def _update_record(values, id, id_was)
46
- substitutes, binds = substitute_values values
47
-
48
- # CPK
49
- um = if self.composite?
50
- relation = @klass.unscoped.where(cpk_id_predicate(@klass.arel_table, @klass.primary_key, id_was || id))
51
- relation.arel.compile_update(substitutes, @klass.primary_key)
52
- else
53
- scope = @klass.unscoped
54
-
55
- if @klass.finder_needs_type_condition?
56
- scope.unscope!(where: @klass.inheritance_column)
57
- end
58
-
59
- scope.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
60
- end
61
-
62
- @klass.connection.update(
63
- um,
64
- 'SQL',
65
- binds)
66
- end
67
- end
68
- end
1
+ module ActiveRecord
2
+ class Relation
3
+ alias :initialize_without_cpk :initialize
4
+ def initialize(klass, table, values = {})
5
+ initialize_without_cpk(klass, table, values)
6
+ add_cpk_support if klass && klass.composite?
7
+ end
8
+
9
+ alias :initialize_copy_without_cpk :initialize_copy
10
+ def initialize_copy(other)
11
+ initialize_copy_without_cpk(other)
12
+ add_cpk_support if klass.composite?
13
+ end
14
+
15
+ def add_cpk_support
16
+ extend CompositePrimaryKeys::CompositeRelation
17
+ end
18
+
19
+ # CPK adds this so that it finds the Equality nodes beneath the And node:
20
+ # equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
21
+ # node.left.relation.name == table_name
22
+ # }
23
+ alias :where_values_hash_without_cpk :where_values_hash
24
+ def where_values_hash(relation_table_name = table_name)
25
+ nodes_from_and = where_values.grep(Arel::Nodes::And).map {|and_node| and_node.children.grep(Arel::Nodes::Equality) }.flatten
26
+
27
+ equalities = (nodes_from_and + where_values.grep(Arel::Nodes::Equality)).find_all { |node|
28
+ node.left.relation.name == relation_table_name
29
+ }
30
+
31
+ binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
32
+
33
+ Hash[equalities.map { |where|
34
+ name = where.left.name
35
+ [name, binds.fetch(name.to_s) {
36
+ case where.right
37
+ when Array then where.right.map(&:val)
38
+ else
39
+ where.right.val
40
+ end
41
+ }]
42
+ }]
43
+ end
44
+
45
+ def _update_record(values, id, id_was)
46
+ substitutes, binds = substitute_values values
47
+
48
+ # CPK
49
+ um = if self.composite?
50
+ relation = @klass.unscoped.where(cpk_id_predicate(@klass.arel_table, @klass.primary_key, id_was || id))
51
+ relation.arel.compile_update(substitutes, @klass.primary_key)
52
+ else
53
+ scope = @klass.unscoped
54
+
55
+ if @klass.finder_needs_type_condition?
56
+ scope.unscope!(where: @klass.inheritance_column)
57
+ end
58
+
59
+ scope.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
60
+ end
61
+
62
+ @klass.connection.update(
63
+ um,
64
+ 'SQL',
65
+ binds)
66
+ end
67
+ end
68
+ end