composite_primary_keys 8.1.0 → 8.1.1

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 (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,52 +1,52 @@
1
- module ActiveRecord
2
- module Sanitization
3
- module ClassMethods
4
- protected
5
- # Accepts a hash of SQL conditions and replaces those attributes
6
- # that correspond to a +composed_of+ relationship with their expanded
7
- # aggregate attribute values.
8
- # Given:
9
- # class Person < ActiveRecord::Base
10
- # composed_of :address, class_name: "Address",
11
- # mapping: [%w(address_street street), %w(address_city city)]
12
- # end
13
- # Then:
14
- # { address: Address.new("813 abc st.", "chicago") }
15
- # # => { address_street: "813 abc st.", address_city: "chicago" }
16
- def expand_hash_conditions_for_aggregates(attrs)
17
- expanded_attrs = {}
18
- attrs.each do |attr, value|
19
- if attr.is_a?(CompositePrimaryKeys::CompositeKeys)
20
- attr.each_with_index do |key,i|
21
- expanded_attrs[key] = value.respond_to?(:flatten) ? value.flatten[i] : value
22
- end
23
- elsif aggregation = reflect_on_aggregation(attr.to_sym)
24
- mapping = aggregation.mapping
25
- mapping.each do |field_attr, aggregate_attr|
26
- if mapping.size == 1 && !value.respond_to?(aggregate_attr)
27
- expanded_attrs[field_attr] = value
28
- else
29
- expanded_attrs[field_attr] = value.send(aggregate_attr)
30
- end
31
- end
32
- else
33
- expanded_attrs[attr] = value
34
- end
35
- end
36
- expanded_attrs
37
- end
38
-
39
- def quoted_id
40
- # CPK
41
- #quote_value(id, column_for_attribute(self.class.primary_key))
42
- if self.composite?
43
- [self.class.primary_keys, ids].
44
- transpose.
45
- map {|attr_name,id| quote_value(id, column_for_attribute(attr_name))}
46
- else
47
- quote_value(id, column_for_attribute(self.class.primary_key))
48
- end
49
- end
50
- end
51
- end
52
- end
1
+ module ActiveRecord
2
+ module Sanitization
3
+ module ClassMethods
4
+ protected
5
+ # Accepts a hash of SQL conditions and replaces those attributes
6
+ # that correspond to a +composed_of+ relationship with their expanded
7
+ # aggregate attribute values.
8
+ # Given:
9
+ # class Person < ActiveRecord::Base
10
+ # composed_of :address, class_name: "Address",
11
+ # mapping: [%w(address_street street), %w(address_city city)]
12
+ # end
13
+ # Then:
14
+ # { address: Address.new("813 abc st.", "chicago") }
15
+ # # => { address_street: "813 abc st.", address_city: "chicago" }
16
+ def expand_hash_conditions_for_aggregates(attrs)
17
+ expanded_attrs = {}
18
+ attrs.each do |attr, value|
19
+ if attr.is_a?(CompositePrimaryKeys::CompositeKeys)
20
+ attr.each_with_index do |key,i|
21
+ expanded_attrs[key] = value.respond_to?(:flatten) ? value.flatten[i] : value
22
+ end
23
+ elsif aggregation = reflect_on_aggregation(attr.to_sym)
24
+ mapping = aggregation.mapping
25
+ mapping.each do |field_attr, aggregate_attr|
26
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
27
+ expanded_attrs[field_attr] = value
28
+ else
29
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
30
+ end
31
+ end
32
+ else
33
+ expanded_attrs[attr] = value
34
+ end
35
+ end
36
+ expanded_attrs
37
+ end
38
+
39
+ def quoted_id
40
+ # CPK
41
+ #quote_value(id, column_for_attribute(self.class.primary_key))
42
+ if self.composite?
43
+ [self.class.primary_keys, ids].
44
+ transpose.
45
+ map {|attr_name,id| quote_value(id, column_for_attribute(attr_name))}
46
+ else
47
+ quote_value(id, column_for_attribute(self.class.primary_key))
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,37 +1,37 @@
1
- module ActiveRecord
2
- module Validations
3
- class UniquenessValidator
4
- def validate_each(record, attribute, value)
5
- finder_class = find_finder_class_for(record)
6
- table = finder_class.arel_table
7
- value = map_enum_attribute(finder_class, attribute, value)
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
16
-
17
- condition = not_eq_conditions.shift
18
- not_eq_conditions.each do |not_eq_condition|
19
- condition = condition.or(not_eq_condition)
20
- end
21
- relation = relation.and(condition)
22
- 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
-
28
- if relation.exists?
29
- error_options = options.except(:case_sensitive, :scope, :conditions)
30
- error_options[:value] = value
31
-
32
- record.errors.add(attribute, :taken, error_options)
33
- end
34
- end
35
- end
36
- end
1
+ module ActiveRecord
2
+ module Validations
3
+ class UniquenessValidator
4
+ def validate_each(record, attribute, value)
5
+ finder_class = find_finder_class_for(record)
6
+ table = finder_class.arel_table
7
+ value = map_enum_attribute(finder_class, attribute, value)
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
16
+
17
+ condition = not_eq_conditions.shift
18
+ not_eq_conditions.each do |not_eq_condition|
19
+ condition = condition.or(not_eq_condition)
20
+ end
21
+ relation = relation.and(condition)
22
+ 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
+
28
+ if relation.exists?
29
+ error_options = options.except(:case_sensitive, :scope, :conditions)
30
+ error_options[:value] = value
31
+
32
+ record.errors.add(attribute, :taken, error_options)
33
+ end
34
+ end
35
+ end
36
+ end
37
37
  end
@@ -1,8 +1,8 @@
1
- module CompositePrimaryKeys
2
- module VERSION
3
- MAJOR = 8
4
- MINOR = 1
5
- TINY = 0
6
- STRING = [MAJOR, MINOR, TINY].join('.')
7
- end
8
- end
1
+ module CompositePrimaryKeys
2
+ module VERSION
3
+ MAJOR = 8
4
+ MINOR = 1
5
+ TINY = 1
6
+ STRING = [MAJOR, MINOR, TINY].join('.')
7
+ end
8
+ end
@@ -1,27 +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 :sqlserver do
5
- desc 'Build the SQL Server 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', 'sqlserver.sql')
10
- sh %( sqsh #{options_str} -i #{schema} )
11
- end
12
-
13
- desc 'Drop the SQL Server test database'
14
- task :drop_database => :load_connection do
15
- options_str = connection_string
16
-
17
- schema = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'sqlserver.drop.sql')
18
- sh %( sqsh #{options_str} -i #{schema} )
19
- end
20
-
21
- desc 'Rebuild the SQL Server 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_sqlserver", "connection")
26
- end
27
- end
1
+ require File.join(PROJECT_ROOT, 'lib', 'composite_primary_keys')
2
+ require File.join(PROJECT_ROOT, 'test', 'connections', 'connection_spec')
3
+
4
+ namespace :sqlserver do
5
+ desc 'Build the SQL Server 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', 'sqlserver.sql')
10
+ sh %( sqsh #{options_str} -i #{schema} )
11
+ end
12
+
13
+ desc 'Drop the SQL Server test database'
14
+ task :drop_database => :load_connection do
15
+ options_str = connection_string
16
+
17
+ schema = File.join(PROJECT_ROOT, 'test', 'fixtures', 'db_definitions', 'sqlserver.drop.sql')
18
+ sh %( sqsh #{options_str} -i #{schema} )
19
+ end
20
+
21
+ desc 'Rebuild the SQL Server 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_sqlserver", "connection")
26
+ end
27
+ end
@@ -1,113 +1,114 @@
1
- PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
-
3
- # To make debugging easier, test within this source tree versus an installed gem
4
- $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
5
- require 'composite_primary_keys'
6
- require 'minitest/autorun'
7
- require 'active_support/test_case'
8
-
9
- # Now load the connection spec
10
- require File.join(PROJECT_ROOT, "test", "connections", "connection_spec")
11
-
12
- spec_name = ENV['ADAPTER'] || 'postgresql'
13
- spec = CompositePrimaryKeys::ConnectionSpec[spec_name]
14
-
15
- # And now connect to the database
16
- adapter = spec['adapter']
17
- require File.join(PROJECT_ROOT, "test", "connections", "native_#{spec_name}", "connection")
18
-
19
- # Tell active record about the configuration
20
- ActiveRecord::Base.configurations[:test] = spec
21
-
22
- # Tell ActiveRecord where to find models
23
- ActiveSupport::Dependencies.autoload_paths << File.join(PROJECT_ROOT, 'test', 'fixtures')
24
-
25
- I18n.config.enforce_available_locales = true
26
-
27
- class ActiveSupport::TestCase
28
- include ActiveRecord::TestFixtures
29
-
30
- self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
31
- self.use_instantiated_fixtures = false
32
- self.use_transactional_fixtures = true
33
-
34
- def assert_date_from_db(expected, actual, message = nil)
35
- # SQL Server doesn't have a separate column type just for dates,
36
- # so the time is in the string and incorrectly formatted
37
- if current_adapter?(:SQLServerAdapter)
38
- assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
39
- elsif current_adapter?(:SybaseAdapter)
40
- assert_equal expected.to_s, actual.to_date.to_s, message
41
- else
42
- assert_equal expected.to_s, actual.to_s, message
43
- end
44
- end
45
-
46
- def assert_queries(num = 1)
47
- ActiveRecord::Base.connection.class.class_eval do
48
- self.query_count = 0
49
- alias_method :execute, :execute_with_query_counting
50
- end
51
- yield
52
- ensure
53
- ActiveRecord::Base.connection.class.class_eval do
54
- alias_method :execute, :execute_without_query_counting
55
- end
56
- assert_equal num, ActiveRecord::Base.connection.query_count, "#{ActiveRecord::Base.connection.query_count} instead of #{num} queries were executed."
57
- end
58
-
59
- def assert_no_queries(&block)
60
- assert_queries(0, &block)
61
- end
62
-
63
- cattr_accessor :classes
64
-
65
- protected
66
-
67
- def testing_with(&block)
68
- classes.keys.each do |key_test|
69
- @key_test = key_test
70
- @klass_info = classes[@key_test]
71
- @klass, @primary_keys = @klass_info[:class], @klass_info[:primary_keys]
72
- order = @klass.primary_key.is_a?(String) ? @klass.primary_key : @klass.primary_key.join(',')
73
- @first = @klass.order(order).first
74
- yield
75
- end
76
- end
77
-
78
- def first_id
79
- ids = (1..@primary_keys.length).map {|num| 1}
80
- composite? ? ids.to_composite_ids : ids.first
81
- end
82
-
83
- def composite?
84
- @key_test != :single
85
- end
86
-
87
- # Oracle metadata is in all caps.
88
- def with_quoted_identifiers(s)
89
- s.gsub(/"(\w+)"/) { |m|
90
- if ActiveRecord::Base.configurations[:test]['adapter'] =~ /oracle/i
91
- m.upcase
92
- else
93
- m
94
- end
95
- }
96
- end
97
- end
98
-
99
- def current_adapter?(type)
100
- ActiveRecord::ConnectionAdapters.const_defined?(type) &&
101
- ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
102
- end
103
-
104
- ActiveRecord::Base.connection.class.class_eval do
105
- cattr_accessor :query_count
106
- alias_method :execute_without_query_counting, :execute
107
- def execute_with_query_counting(sql, name = nil)
108
- self.query_count += 1
109
- execute_without_query_counting(sql, name)
110
- end
111
- end
112
-
113
- ActiveRecord::Base.logger = Logger.new(ENV['CPK_LOGFILE'] || STDOUT)
1
+ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
+
3
+ # To make debugging easier, test within this source tree versus an installed gem
4
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
5
+ require 'composite_primary_keys'
6
+ require 'minitest/autorun'
7
+ require 'active_support/test_case'
8
+
9
+ # Now load the connection spec
10
+ require File.join(PROJECT_ROOT, "test", "connections", "connection_spec")
11
+
12
+ spec_name = ENV['ADAPTER'] || 'postgresql'
13
+ spec = CompositePrimaryKeys::ConnectionSpec[spec_name]
14
+
15
+ # And now connect to the database
16
+ adapter = spec['adapter']
17
+ require File.join(PROJECT_ROOT, "test", "connections", "native_#{spec_name}", "connection")
18
+
19
+ # Tell active record about the configuration
20
+ ActiveRecord::Base.configurations[:test] = spec
21
+
22
+ # Tell ActiveRecord where to find models
23
+ ActiveSupport::Dependencies.autoload_paths << File.join(PROJECT_ROOT, 'test', 'fixtures')
24
+
25
+ I18n.config.enforce_available_locales = true
26
+
27
+ class ActiveSupport::TestCase
28
+ include ActiveRecord::TestFixtures
29
+
30
+ self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
31
+ self.use_instantiated_fixtures = false
32
+ self.use_transactional_fixtures = true
33
+ self.test_order = :random
34
+
35
+ def assert_date_from_db(expected, actual, message = nil)
36
+ # SQL Server doesn't have a separate column type just for dates,
37
+ # so the time is in the string and incorrectly formatted
38
+ if current_adapter?(:SQLServerAdapter)
39
+ assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
40
+ elsif current_adapter?(:SybaseAdapter)
41
+ assert_equal expected.to_s, actual.to_date.to_s, message
42
+ else
43
+ assert_equal expected.to_s, actual.to_s, message
44
+ end
45
+ end
46
+
47
+ def assert_queries(num = 1)
48
+ ActiveRecord::Base.connection.class.class_eval do
49
+ self.query_count = 0
50
+ alias_method :execute, :execute_with_query_counting
51
+ end
52
+ yield
53
+ ensure
54
+ ActiveRecord::Base.connection.class.class_eval do
55
+ alias_method :execute, :execute_without_query_counting
56
+ end
57
+ assert_equal num, ActiveRecord::Base.connection.query_count, "#{ActiveRecord::Base.connection.query_count} instead of #{num} queries were executed."
58
+ end
59
+
60
+ def assert_no_queries(&block)
61
+ assert_queries(0, &block)
62
+ end
63
+
64
+ cattr_accessor :classes
65
+
66
+ protected
67
+
68
+ def testing_with(&block)
69
+ classes.keys.each do |key_test|
70
+ @key_test = key_test
71
+ @klass_info = classes[@key_test]
72
+ @klass, @primary_keys = @klass_info[:class], @klass_info[:primary_keys]
73
+ order = @klass.primary_key.is_a?(String) ? @klass.primary_key : @klass.primary_key.join(',')
74
+ @first = @klass.order(order).first
75
+ yield
76
+ end
77
+ end
78
+
79
+ def first_id
80
+ ids = (1..@primary_keys.length).map {|num| 1}
81
+ composite? ? ids.to_composite_ids : ids.first
82
+ end
83
+
84
+ def composite?
85
+ @key_test != :single
86
+ end
87
+
88
+ # Oracle metadata is in all caps.
89
+ def with_quoted_identifiers(s)
90
+ s.gsub(/"(\w+)"/) { |m|
91
+ if ActiveRecord::Base.configurations[:test]['adapter'] =~ /oracle/i
92
+ m.upcase
93
+ else
94
+ m
95
+ end
96
+ }
97
+ end
98
+ end
99
+
100
+ def current_adapter?(type)
101
+ ActiveRecord::ConnectionAdapters.const_defined?(type) &&
102
+ ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
103
+ end
104
+
105
+ ActiveRecord::Base.connection.class.class_eval do
106
+ cattr_accessor :query_count
107
+ alias_method :execute_without_query_counting, :execute
108
+ def execute_with_query_counting(sql, name = nil)
109
+ self.query_count += 1
110
+ execute_without_query_counting(sql, name)
111
+ end
112
+ end
113
+
114
+ ActiveRecord::Base.logger = Logger.new(ENV['CPK_LOGFILE'] || STDOUT)