composite_primary_keys 2.3.5.1 → 3.0.0.b2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/History.txt +26 -0
  2. data/README.txt +1 -1
  3. data/Rakefile +41 -51
  4. data/lib/composite_primary_keys.rb +19 -7
  5. data/lib/composite_primary_keys/association_preload.rb +75 -170
  6. data/lib/composite_primary_keys/associations.rb +98 -400
  7. data/lib/composite_primary_keys/associations/association_proxy.rb +32 -0
  8. data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +30 -0
  9. data/lib/composite_primary_keys/associations/has_many_association.rb +72 -0
  10. data/lib/composite_primary_keys/associations/has_one_association.rb +19 -0
  11. data/lib/composite_primary_keys/associations/through_association_scope.rb +103 -0
  12. data/lib/composite_primary_keys/base.rb +148 -305
  13. data/lib/composite_primary_keys/calculations.rb +22 -64
  14. data/lib/composite_primary_keys/composite_arrays.rb +3 -10
  15. data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +9 -0
  16. data/lib/composite_primary_keys/connection_adapters/oracle_enhanced_adapter.rb +17 -0
  17. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +1 -1
  18. data/lib/composite_primary_keys/finder_methods.rb +71 -0
  19. data/lib/composite_primary_keys/fixtures.rb +1 -1
  20. data/lib/composite_primary_keys/read.rb +25 -0
  21. data/lib/composite_primary_keys/reflection.rb +30 -10
  22. data/lib/composite_primary_keys/relation.rb +31 -0
  23. data/lib/composite_primary_keys/validations/uniqueness.rb +106 -66
  24. data/lib/composite_primary_keys/version.rb +4 -4
  25. data/scripts/console.rb +1 -1
  26. data/tasks/Rakefile.rb +13 -0
  27. data/tasks/databases/mysql.rake +11 -15
  28. data/tasks/databases/oracle.rake +10 -11
  29. data/tasks/databases/postgresql.rake +10 -13
  30. data/tasks/databases/sqlite3.rake +9 -9
  31. data/test/README_tests.txt +1 -45
  32. data/test/abstract_unit.rb +17 -14
  33. data/test/connections/connection_spec.rb +19 -0
  34. data/test/connections/databases.example.yml +11 -0
  35. data/test/connections/databases.yml +13 -0
  36. data/test/connections/native_mysql/connection.rb +10 -2
  37. data/test/connections/native_oracle/connection.rb +7 -4
  38. data/test/connections/native_oracle_enhanced/connection.rb +23 -0
  39. data/test/connections/native_postgresql/connection.rb +13 -5
  40. data/test/connections/native_sqlite/connection.rb +7 -3
  41. data/test/fixtures/article_group.rb +4 -0
  42. data/test/fixtures/article_groups.yml +7 -0
  43. data/test/fixtures/db_definitions/postgresql.sql +2 -1
  44. data/test/fixtures/debug.log +133 -0
  45. data/test/fixtures/dorm.rb +3 -0
  46. data/test/fixtures/dorms.yml +2 -0
  47. data/test/fixtures/kitchen_sink.rb +3 -0
  48. data/test/fixtures/kitchen_sinks.yml +5 -0
  49. data/test/fixtures/reference_codes.yml +2 -0
  50. data/test/fixtures/reference_type.rb +1 -1
  51. data/test/fixtures/restaurant.rb +6 -0
  52. data/test/fixtures/restaurants.yml +5 -0
  53. data/test/fixtures/restaurants_suburbs.yml +11 -0
  54. data/test/fixtures/room.rb +10 -0
  55. data/test/fixtures/room_assignment.rb +4 -0
  56. data/test/fixtures/room_assignments.yml +4 -0
  57. data/test/fixtures/room_attribute.rb +3 -0
  58. data/test/fixtures/room_attribute_assignment.rb +5 -0
  59. data/test/fixtures/room_attribute_assignments.yml +4 -0
  60. data/test/fixtures/room_attributes.yml +3 -0
  61. data/test/fixtures/rooms.yml +3 -0
  62. data/test/fixtures/seat.rb +5 -0
  63. data/test/fixtures/seats.yml +4 -0
  64. data/test/fixtures/student.rb +4 -0
  65. data/test/fixtures/students.yml +2 -0
  66. data/test/test_associations.rb +27 -50
  67. data/test/test_attributes.rb +15 -19
  68. data/test/test_composite_arrays.rb +2 -21
  69. data/test/test_create.rb +3 -3
  70. data/test/test_delete.rb +7 -20
  71. data/test/test_exists.rb +3 -7
  72. data/test/test_find.rb +0 -8
  73. data/test/test_ids.rb +3 -17
  74. data/test/test_polymorphic.rb +5 -4
  75. data/test/test_suite.rb +19 -0
  76. data/test/{test_tutorial_examle.rb → test_tutorial_example.rb} +0 -0
  77. metadata +110 -72
  78. data/Manifest.txt +0 -123
  79. data/lib/adapter_helper/base.rb +0 -63
  80. data/lib/adapter_helper/mysql.rb +0 -13
  81. data/lib/adapter_helper/oracle.rb +0 -12
  82. data/lib/adapter_helper/postgresql.rb +0 -13
  83. data/lib/adapter_helper/sqlite3.rb +0 -13
  84. data/lib/composite_primary_keys/migration.rb +0 -20
  85. data/local/database_connections.rb.sample +0 -12
  86. data/local/paths.rb.sample +0 -2
  87. data/local/tasks.rb.sample +0 -2
  88. data/tasks/activerecord_selection.rake +0 -43
  89. data/tasks/databases.rake +0 -12
  90. data/tasks/deployment.rake +0 -22
  91. data/tasks/local_setup.rake +0 -13
  92. data/test/test_dummy.rb +0 -28
  93. data/tmp/test.db +0 -0
  94. data/website/index.html +0 -195
  95. data/website/index.txt +0 -159
  96. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  97. data/website/stylesheets/screen.css +0 -126
  98. data/website/template.js +0 -3
  99. data/website/template.rhtml +0 -53
  100. data/website/version-raw.js +0 -3
  101. data/website/version-raw.txt +0 -2
  102. data/website/version.js +0 -4
  103. data/website/version.txt +0 -3
@@ -1,3 +1,29 @@
1
+ == 3.0.1.b 2010-12-06
2
+
3
+ * Port to Rails 3.0 and Ruby 1.9.2
4
+
5
+ * Backwards compatability issues
6
+ + You can no longer define a composite primary key with a single field. If you
7
+ try, your model will just default to a standard active record model. Removing
8
+ this corner case simplified the code.
9
+ + Removed CompositePrimaryKeys::CompositeKeys. This was done so that the #to_s
10
+ method on composite ids, such as [1,2], returns "[1, 2]". This in turns
11
+ reduces the amount of core Rails code that needs to be overridden.
12
+ + Setting attribute values by string is no longer supported. For example, this
13
+ no longer works:
14
+ my_record[[:main_id, :secondary_id]] = '1,2'
15
+ Instead, do this:
16
+ my_record[[:main_id, :secondary_id]] = [1,2]
17
+ Once again, this was done to reduce the amount of overridden Rails code
18
+ + At the moment, complex finds with nested arrays do not work. For example
19
+ find([[1,2], [3,4]))
20
+ + Count methods no longer work. For example, Tariff.count(:include => :product_tariffs)
21
+ in the TestAssociations#test_count test returns an error. This is because Rails 3.0
22
+ uses Arel to perform such calculations, and its not obvious (at least to me) how to
23
+ hook into this new mechanism to support tables with composite keys.
24
+ + The TestPolymorphic#test_polymorphic_has_many_through is currently failing, but this
25
+ looks like an ActiveRecord 3.0 bug to me.
26
+
1
27
  == 2.3.5.1 2010-02-13
2
28
 
3
29
  * Resolved "warning: already initialized constant HasManyThroughCantAssociateThroughHasManyReflection" [Titi Ala'ilima]
data/README.txt CHANGED
@@ -36,6 +36,6 @@ http://groups.google.com/compositekeys
36
36
 
37
37
  == Author
38
38
 
39
- Written by Dr Nic Williams, drnicwilliams@gmail
39
+ Written by Dr Nic Williams, drnicwilliams@gmail.
40
40
  Contributions by many!
41
41
 
data/Rakefile CHANGED
@@ -5,61 +5,51 @@ require 'rake/testtask'
5
5
  require 'rake/rdoctask'
6
6
  require 'rake/packagetask'
7
7
  require 'rake/gempackagetask'
8
- require 'rake/contrib/rubyforgepublisher'
9
- require 'fileutils'
10
- require 'hoe'
11
- include FileUtils
12
- require File.join(File.dirname(__FILE__), 'lib', 'composite_primary_keys', 'version')
13
8
 
14
- AUTHOR = "Dr Nic Williams"
15
- EMAIL = "drnicwilliams@gmail.com"
16
- DESCRIPTION = "Composite key support for ActiveRecords"
17
- GEM_NAME = "composite_primary_keys" # what ppl will type to install your gem
18
- if File.exists?("~/.rubyforge/user-config.yml")
19
- # TODO this should prob go in a local/ file
20
- config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
21
- RUBYFORGE_USERNAME = config["username"]
22
- end
23
- RUBYFORGE_PROJECT = "compositekeys"
24
- HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ # Set global variable so other tasks can access them
10
+ PROJECT_ROOT = File.expand_path(".")
11
+ GEM_NAME = 'composite_primary_keys'
25
12
 
26
- REV = nil #File.read(".svn/entries")[/committed-rev="(\d+)"/, 1] rescue nil
27
- VERS = ENV['VERSION'] || (CompositePrimaryKeys::VERSION::STRING + (REV ? ".#{REV}" : ""))
28
- CLEAN.include ['**/.*.sw?', '*.gem', '.config','debug.log','*.db','logfile','log/**/*','**/.DS_Store', '.project']
29
- RDOC_OPTS = ['--quiet', '--title', "newgem documentation",
30
- "--opname", "index.html",
31
- "--line-numbers",
32
- "--main", "README",
33
- "--inline-source"]
13
+ # Read the current version
14
+ require File.join(File.dirname(__FILE__), 'lib', 'composite_primary_keys', 'version')
34
15
 
35
- class Hoe
36
- def extra_deps
37
- @extra_deps.reject { |x| Array(x).first == 'hoe' }
38
- end
16
+ # Setup Gem Specs
17
+ spec = Gem::Specification.new do |s|
18
+ s.name = GEM_NAME
19
+ s.version = CompositePrimaryKeys::VERSION::STRING
20
+ s.platform = Gem::Platform::RUBY
21
+ s.authors = ["Dr Nic Williams", "Charlie Savage"]
22
+ s.email = ["drnicwilliams@gmail.com"]
23
+ s.homepage = "http://github.com/cfis/composite_primary_keys"
24
+ s.summary = "Composite key support for ActiveRecord"
25
+ s.rubyforge_project = 'compositekeys'
26
+ s.description = "Composite key support for ActiveRecord 3"
27
+ s.files = FileList['Rakefile',
28
+ '*.txt',
29
+ '*.rb',
30
+ 'lib/**/*',
31
+ 'local/**/*',
32
+ 'scripts/**/*',
33
+ 'tasks/**/*',
34
+ 'test/**/*'].to_a
35
+ s.require_path = 'lib'
36
+ s.test_files = Dir.glob("test/**")
37
+
38
+ s.date = Time.new
39
+ s.has_rdoc = true
40
+
41
+ # Dependencies
42
+ s.required_ruby_version = '>= 1.8.7'
43
+ s.add_dependency('activerecord', '>= 3.0.1')
44
+ s.add_development_dependency "rspec"
39
45
  end
40
46
 
41
- # Generate all the Rake tasks
42
- # Run 'rake -T' to see list of generated tasks (from gem root directory)
43
- hoe = Hoe.new(GEM_NAME, VERS) do |p|
44
- p.author = AUTHOR
45
- p.description = DESCRIPTION
46
- p.email = EMAIL
47
- p.summary = DESCRIPTION
48
- p.url = HOMEPATH
49
- p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
50
- p.test_globs = ["test/**/test*.rb"]
51
- p.clean_globs |= CLEAN #An array of file patterns to delete on clean.
52
-
53
- # == Optional
54
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
55
- p.extra_deps = [['activerecord', '>= 2.3.5']] #An array of rubygem dependencies.
56
- #p.spec_extras - A hash of extra values to set in the gemspec.
47
+ # Rake task to build the default package
48
+ Rake::GemPackageTask.new(spec) do |pkg|
49
+ pkg.need_tar = true
57
50
  end
58
51
 
59
- CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\n\n")
60
- PATH = RUBYFORGE_PROJECT
61
- hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
62
-
63
- PROJECT_ROOT = File.expand_path(".")
64
-
65
- require 'loader'
52
+ # Load task files
53
+ Dir.glob('tasks/**/*.rake').each do |rake_file|
54
+ load File.join(File.dirname(__FILE__), rake_file)
55
+ end
@@ -29,27 +29,39 @@ unless defined?(ActiveRecord)
29
29
  require 'active_record'
30
30
  rescue LoadError
31
31
  require 'rubygems'
32
- require_gem 'activerecord'
32
+ gem 'active_record'
33
33
  end
34
34
  end
35
35
 
36
36
  require 'active_record/associations.rb'
37
+ require 'active_record/associations/association_proxy.rb'
38
+ require 'active_record/associations/association_collection'
39
+ require 'active_record/associations/association_proxy'
40
+ require 'active_record/associations/belongs_to_association'
41
+ require 'active_record/associations/belongs_to_polymorphic_association'
42
+ require 'active_record/associations/has_and_belongs_to_many_association'
43
+ require 'active_record/associations/has_many_association'
44
+ require 'active_record/associations/has_one_association'
45
+ require 'active_record/associations/has_one_through_association'
46
+ require 'active_record/associations/through_association_scope'
37
47
 
38
48
  require 'composite_primary_keys/fixtures'
39
49
  require 'composite_primary_keys/composite_arrays'
40
50
  require 'composite_primary_keys/associations'
51
+ require 'composite_primary_keys/associations/association_proxy'
52
+ require 'composite_primary_keys/associations/has_one_association'
53
+ require 'composite_primary_keys/associations/has_many_association'
54
+ require 'composite_primary_keys/associations/has_and_belongs_to_many_association'
55
+ require 'composite_primary_keys/associations/through_association_scope'
41
56
  require 'composite_primary_keys/association_preload'
42
57
  require 'composite_primary_keys/reflection'
58
+ require 'composite_primary_keys/relation'
59
+ require 'composite_primary_keys/read'
60
+ require 'composite_primary_keys/finder_methods'
43
61
  require 'composite_primary_keys/base'
44
62
  require 'composite_primary_keys/calculations'
45
- require 'composite_primary_keys/migration'
46
- require 'composite_primary_keys/attribute_methods'
47
63
  require 'composite_primary_keys/validations/uniqueness'
48
64
 
49
- ActiveRecord::Base.class_eval do
50
- include CompositePrimaryKeys::ActiveRecord::Base
51
- end
52
-
53
65
  Dir[File.dirname(__FILE__) + '/composite_primary_keys/connection_adapters/*.rb'].each do |adapter|
54
66
  begin
55
67
  require adapter.gsub('.rb','')
@@ -10,101 +10,60 @@ module CompositePrimaryKeys
10
10
  module ClassMethods
11
11
  def preload_has_and_belongs_to_many_association(records, reflection, preload_options={})
12
12
  table_name = reflection.klass.quoted_table_name
13
- id_to_record_map, ids = construct_id_map_for_composite(records)
13
+ id_to_record_map, ids = construct_id_map(records)
14
14
  records.each {|record| record.send(reflection.name).loaded}
15
15
  options = reflection.options
16
16
 
17
17
  if composite?
18
- primary_key = reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP)
19
18
  where = (primary_key * ids.size).in_groups_of(primary_key.size).map do |keys|
20
19
  "(" + keys.map{|key| "t0.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
21
20
  end.join(" OR ")
22
21
 
23
22
  conditions = [where, ids].flatten
24
23
  joins = "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{full_composite_join_clause(reflection, reflection.klass.table_name, reflection.klass.primary_key, 't0', reflection.association_foreign_key)}"
25
- parent_primary_keys = reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP).map{|k| "t0.#{connection.quote_column_name(k)}"}
26
- parent_record_id = connection.concat(*parent_primary_keys.zip(["','"] * (parent_primary_keys.size - 1)).flatten.compact)
24
+ parent_primary_keys = reflection.cpk_primary_key.map{|k| "t0.#{connection.quote_column_name(k)}"}
25
+ concat_arr = parent_primary_keys.zip(["'#{CompositePrimaryKeys::ID_SEP}'"] * (parent_primary_keys.size - 1)).flatten.compact
26
+ parent_record_id = connection.concat(*concat_arr)
27
27
  else
28
- conditions = ["t0.#{connection.quote_column_name(reflection.primary_key_name)} IN (?)", ids]
29
- joins = "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{connection.quote_column_name(reflection.klass.primary_key)} = t0.#{connection.quote_column_name(reflection.association_foreign_key)})"
28
+ conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
29
+ conditions << append_conditions(reflection, preload_options)
30
+ conditions = [conditions, ids]
31
+ joins = "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}"
30
32
  parent_record_id = reflection.primary_key_name
31
- end
32
-
33
- conditions.first << append_conditions(reflection, preload_options)
34
-
35
- associated_records = reflection.klass.find(:all,
36
- :conditions => conditions,
37
- :include => options[:include],
38
- :joins => joins,
39
- :select => "#{options[:select] || table_name+'.*'}, #{parent_record_id} as parent_record_id_",
40
- :order => options[:order])
41
-
42
- set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'parent_record_id_')
43
- end
33
+ end
44
34
 
45
- def preload_has_many_association(records, reflection, preload_options={})
46
- id_to_record_map, ids = construct_id_map_for_composite(records)
47
- records.each {|record| record.send(reflection.name).loaded}
48
- options = reflection.options
35
+ associated_records = reflection.klass.unscoped.where(conditions).
36
+ includes(options[:include]).
37
+ joins(joins).
38
+ select("#{options[:select] || table_name+'.*'}, #{parent_record_id} as the_parent_record_id").
39
+ order(options[:order]).to_a
49
40
 
50
- if options[:through]
51
- through_records = preload_through_records(records, reflection, options[:through])
52
- through_reflection = reflections[options[:through]]
53
- through_primary_key = through_reflection.primary_key_name
54
-
55
- unless through_records.empty?
56
- source = reflection.source_reflection.name
57
- #add conditions from reflection!
58
- through_records.first.class.preload_associations(through_records, source, reflection.options)
59
- through_records.each do |through_record|
60
- key = through_primary_key.to_s.split(CompositePrimaryKeys::ID_SEP).map{|k| through_record.send(k)}.join(CompositePrimaryKeys::ID_SEP)
61
- add_preloaded_records_to_collection(id_to_record_map[key], reflection.name, through_record.send(source))
62
- end
63
- end
64
- else
65
- associated_records = find_associated_records(ids, reflection, preload_options)
66
- set_association_collection_records(id_to_record_map, reflection.name, associated_records, reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP))
67
- end
68
- end
69
-
70
- def preload_through_records(records, reflection, through_association)
71
- through_reflection = reflections[through_association]
72
- through_primary_key = through_reflection.primary_key_name
73
-
74
- if reflection.options[:source_type]
75
- interface = reflection.source_reflection.options[:foreign_type]
76
- preload_options = {:conditions => ["#{connection.quote_column_name interface} = ?", reflection.options[:source_type]]}
77
-
78
- records.compact!
79
- records.first.class.preload_associations(records, through_association, preload_options)
80
-
81
- # Dont cache the association - we would only be caching a subset
82
- through_records = []
83
- records.each do |record|
84
- proxy = record.send(through_association)
85
-
86
- if proxy.respond_to?(:target)
87
- through_records << proxy.target
88
- proxy.reset
89
- else # this is a has_one :through reflection
90
- through_records << proxy if proxy
91
- end
92
- end
93
- through_records.flatten!
94
- else
95
- records.first.class.preload_associations(records, through_association)
96
- through_records = records.map {|record| record.send(through_association)}.flatten
97
- end
98
-
99
- through_records.compact!
100
- through_records
41
+ set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id')
101
42
  end
102
43
 
103
44
  def preload_belongs_to_association(records, reflection, preload_options={})
45
+ return if records.first.send("loaded_#{reflection.name}?")
104
46
  options = reflection.options
105
- primary_key_name = reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP)
106
-
47
+
48
+ ids = Array.new
49
+
107
50
  if options[:polymorphic]
51
+ # CPK
52
+ #polymorph_type = options[:foreign_type]
53
+ #klasses_and_ids = {}
54
+
55
+ # Construct a mapping from klass to a list of ids to load and a mapping of those ids back to their parent_records
56
+ #records.each do |record|
57
+ # if klass = record.send(polymorph_type)
58
+ # klass_id = record.send(primary_key_name)
59
+ # if klass_id
60
+ # id_map = klasses_and_ids[klass] ||= {}
61
+ # id_list_for_klass_id = (id_map[klass_id.to_s] ||= [])
62
+ # id_list_for_klass_id << record
63
+ # end
64
+ # end
65
+ #end
66
+ #klasses_and_ids = klasses_and_ids.to_a
108
67
  raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
109
68
  else
110
69
  # I need to keep the original ids for each record (as opposed to the stringified) so
@@ -114,128 +73,74 @@ module CompositePrimaryKeys
114
73
  id_map = {}
115
74
 
116
75
  records.each do |record|
117
- key = primary_key_name.map{|k| record.send(k)}
118
- key_as_string = key.join(CompositePrimaryKeys::ID_SEP)
76
+ keys = record[reflection.cpk_primary_key]
77
+ ids << keys
119
78
 
120
- if key_as_string
121
- mapped_records = (id_map[key_as_string] ||= {:id => key, :records => []})
122
- mapped_records[:records] << record
123
- end
79
+ mapped_records = (id_map[keys.to_s] ||= [])
80
+ mapped_records << record
124
81
  end
125
82
 
126
-
127
83
  klasses_and_ids = [[reflection.klass.name, id_map]]
128
84
  end
129
85
 
130
86
  klasses_and_ids.each do |klass_and_id|
131
87
  klass_name, id_map = *klass_and_id
88
+ next if id_map.empty?
132
89
  klass = klass_name.constantize
133
- table_name = klass.quoted_table_name
134
- connection = reflection.active_record.connection
135
90
 
136
- if composite?
137
- primary_key = klass.primary_key.to_s.split(CompositePrimaryKeys::ID_SEP)
138
- ids = id_map.keys.uniq.map {|id| id_map[id][:id]}
91
+ table_name = klass.quoted_table_name
92
+ primary_key = [reflection.options[:primary_key] || klass.primary_key].flatten
139
93
 
140
- where = (primary_key * ids.size).in_groups_of(primary_key.size).map do |keys|
141
- "(" + keys.map{|key| "#{table_name}.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
142
- end.join(" OR ")
94
+ # CPK
95
+ conditions = id_map.map do |key, value|
96
+ "(" +
97
+ primary_key.map do |key|
98
+ "#{table_name}.#{connection.quote_column_name(key)} = ?"
99
+ end.join(' AND ') + ")"
100
+ end.join(' OR ')
143
101
 
144
- conditions = [where, ids].flatten
145
- else
146
- conditions = ["#{table_name}.#{connection.quote_column_name(primary_key)} IN (?)", id_map.keys.uniq]
147
- end
102
+ conditions << append_conditions(reflection, preload_options)
148
103
 
149
- conditions.first << append_conditions(reflection, preload_options)
104
+ conditions = [conditions] + ids.uniq.flatten
150
105
 
151
- associated_records = klass.find(:all,
152
- :conditions => conditions,
153
- :include => options[:include],
154
- :select => options[:select],
155
- :joins => options[:joins],
156
- :order => options[:order])
106
+ associated_records = klass.unscoped.where(conditions).apply_finder_options(options.slice(:include, :select, :joins, :order)).to_a
157
107
 
158
108
  set_association_single_records(id_map, reflection.name, associated_records, primary_key)
159
109
  end
160
110
  end
161
-
162
- def set_association_collection_records(id_to_record_map, reflection_name, associated_records, key)
163
- associated_records.each do |associated_record|
164
- associated_record_key = associated_record[key]
165
- associated_record_key = associated_record_key.is_a?(Array) ? associated_record_key.join(CompositePrimaryKeys::ID_SEP) : associated_record_key.to_s
166
- mapped_records = id_to_record_map[associated_record_key]
167
- add_preloaded_records_to_collection(mapped_records, reflection_name, associated_record)
168
- end
169
- end
170
-
171
- def set_association_single_records(id_to_record_map, reflection_name, associated_records, key)
172
- seen_keys = {}
173
- associated_records.each do |associated_record|
174
- associated_record_key = associated_record[key]
175
- associated_record_key = associated_record_key.is_a?(Array) ? associated_record_key.join(CompositePrimaryKeys::ID_SEP) : associated_record_key.to_s
176
-
177
- #this is a has_one or belongs_to: there should only be one record.
178
- #Unfortunately we can't (in portable way) ask the database for 'all records where foo_id in (x,y,z), but please
179
- # only one row per distinct foo_id' so this where we enforce that
180
- next if seen_keys[associated_record_key]
181
- seen_keys[associated_record_key] = true
182
- mapped_records = id_to_record_map[associated_record_key][:records]
183
- mapped_records.each do |mapped_record|
184
- mapped_record.send("set_#{reflection_name}_target", associated_record)
185
- end
186
- end
187
- end
188
-
111
+
189
112
  def find_associated_records(ids, reflection, preload_options)
190
113
  options = reflection.options
191
114
  table_name = reflection.klass.quoted_table_name
192
115
 
193
116
  if interface = reflection.options[:as]
194
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
117
+ conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
195
118
  else
196
- connection = reflection.active_record.connection
197
- foreign_key = reflection.primary_key_name
198
- conditions = ["#{table_name}.#{connection.quote_column_name(foreign_key)} IN (?)", ids]
199
-
200
- if composite?
201
- foreign_keys = foreign_key.to_s.split(CompositePrimaryKeys::ID_SEP)
202
-
203
- where = (foreign_keys * ids.size).in_groups_of(foreign_keys.size).map do |keys|
204
- "(" + keys.map{|key| "#{table_name}.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
205
- end.join(" OR ")
206
-
207
- conditions = [where, ids].flatten
208
- end
119
+ foreign_key = reflection.cpk_primary_key
120
+
121
+ where = (foreign_key * ids.size).in_groups_of(foreign_key.size).map do |keys|
122
+ "(" + keys.map{|key| "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
123
+ end.join(" OR ")
124
+
125
+ conditions = [where, ids].flatten
209
126
  end
210
127
 
211
- conditions.first << append_conditions(reflection, preload_options)
128
+ conditions[0] << append_conditions(reflection, preload_options)
212
129
 
213
- reflection.klass.find(:all,
214
- :select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
215
- :include => preload_options[:include] || options[:include],
130
+ find_options = {
131
+ :select => preload_options[:select] || options[:select] || "#{table_name}.*",
132
+ :include => preload_options[:include] || options[:include],
133
+ # CPK
134
+ # :conditions => [conditions, ids],
216
135
  :conditions => conditions,
217
- :joins => options[:joins],
218
- :group => preload_options[:group] || options[:group],
219
- :order => preload_options[:order] || options[:order])
220
- end
221
-
222
- # Given a collection of ActiveRecord objects, constructs a Hash which maps
223
- # the objects' IDs to the relevant objects. Returns a 2-tuple
224
- # <tt>(id_to_record_map, ids)</tt> where +id_to_record_map+ is the Hash,
225
- # and +ids+ is an Array of record IDs.
226
- def construct_id_map_for_composite(records)
227
- id_to_record_map = {}
228
- ids = []
229
- records.each do |record|
230
- primary_key ||= record.class.primary_key
231
- ids << record.id
232
- mapped_records = (id_to_record_map[record.id.to_s] ||= [])
233
- mapped_records << record
234
- end
235
- ids.uniq!
236
- return id_to_record_map, ids
136
+ :joins => options[:joins],
137
+ :group => preload_options[:group] || options[:group],
138
+ :order => preload_options[:order] || options[:order]
139
+ }
140
+
141
+ reflection.klass.unscoped.apply_finder_options(find_options).to_a
237
142
  end
238
-
143
+
239
144
  def full_composite_join_clause(reflection, table1, full_keys1, table2, full_keys2)
240
145
  connection = reflection.active_record.connection
241
146
  full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
@@ -250,4 +155,4 @@ module CompositePrimaryKeys
250
155
  end
251
156
  end
252
157
  end
253
- end
158
+ end