composite_primary_keys 2.3.5.1 → 3.0.0.b2

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 (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