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,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)