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.
- data/History.txt +26 -0
- data/README.txt +1 -1
- data/Rakefile +41 -51
- data/lib/composite_primary_keys.rb +19 -7
- data/lib/composite_primary_keys/association_preload.rb +75 -170
- data/lib/composite_primary_keys/associations.rb +98 -400
- data/lib/composite_primary_keys/associations/association_proxy.rb +32 -0
- data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +30 -0
- data/lib/composite_primary_keys/associations/has_many_association.rb +72 -0
- data/lib/composite_primary_keys/associations/has_one_association.rb +19 -0
- data/lib/composite_primary_keys/associations/through_association_scope.rb +103 -0
- data/lib/composite_primary_keys/base.rb +148 -305
- data/lib/composite_primary_keys/calculations.rb +22 -64
- data/lib/composite_primary_keys/composite_arrays.rb +3 -10
- data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +9 -0
- data/lib/composite_primary_keys/connection_adapters/oracle_enhanced_adapter.rb +17 -0
- data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +1 -1
- data/lib/composite_primary_keys/finder_methods.rb +71 -0
- data/lib/composite_primary_keys/fixtures.rb +1 -1
- data/lib/composite_primary_keys/read.rb +25 -0
- data/lib/composite_primary_keys/reflection.rb +30 -10
- data/lib/composite_primary_keys/relation.rb +31 -0
- data/lib/composite_primary_keys/validations/uniqueness.rb +106 -66
- data/lib/composite_primary_keys/version.rb +4 -4
- data/scripts/console.rb +1 -1
- data/tasks/Rakefile.rb +13 -0
- data/tasks/databases/mysql.rake +11 -15
- data/tasks/databases/oracle.rake +10 -11
- data/tasks/databases/postgresql.rake +10 -13
- data/tasks/databases/sqlite3.rake +9 -9
- data/test/README_tests.txt +1 -45
- data/test/abstract_unit.rb +17 -14
- data/test/connections/connection_spec.rb +19 -0
- data/test/connections/databases.example.yml +11 -0
- data/test/connections/databases.yml +13 -0
- data/test/connections/native_mysql/connection.rb +10 -2
- data/test/connections/native_oracle/connection.rb +7 -4
- data/test/connections/native_oracle_enhanced/connection.rb +23 -0
- data/test/connections/native_postgresql/connection.rb +13 -5
- data/test/connections/native_sqlite/connection.rb +7 -3
- data/test/fixtures/article_group.rb +4 -0
- data/test/fixtures/article_groups.yml +7 -0
- data/test/fixtures/db_definitions/postgresql.sql +2 -1
- data/test/fixtures/debug.log +133 -0
- data/test/fixtures/dorm.rb +3 -0
- data/test/fixtures/dorms.yml +2 -0
- data/test/fixtures/kitchen_sink.rb +3 -0
- data/test/fixtures/kitchen_sinks.yml +5 -0
- data/test/fixtures/reference_codes.yml +2 -0
- data/test/fixtures/reference_type.rb +1 -1
- data/test/fixtures/restaurant.rb +6 -0
- data/test/fixtures/restaurants.yml +5 -0
- data/test/fixtures/restaurants_suburbs.yml +11 -0
- data/test/fixtures/room.rb +10 -0
- data/test/fixtures/room_assignment.rb +4 -0
- data/test/fixtures/room_assignments.yml +4 -0
- data/test/fixtures/room_attribute.rb +3 -0
- data/test/fixtures/room_attribute_assignment.rb +5 -0
- data/test/fixtures/room_attribute_assignments.yml +4 -0
- data/test/fixtures/room_attributes.yml +3 -0
- data/test/fixtures/rooms.yml +3 -0
- data/test/fixtures/seat.rb +5 -0
- data/test/fixtures/seats.yml +4 -0
- data/test/fixtures/student.rb +4 -0
- data/test/fixtures/students.yml +2 -0
- data/test/test_associations.rb +27 -50
- data/test/test_attributes.rb +15 -19
- data/test/test_composite_arrays.rb +2 -21
- data/test/test_create.rb +3 -3
- data/test/test_delete.rb +7 -20
- data/test/test_exists.rb +3 -7
- data/test/test_find.rb +0 -8
- data/test/test_ids.rb +3 -17
- data/test/test_polymorphic.rb +5 -4
- data/test/test_suite.rb +19 -0
- data/test/{test_tutorial_examle.rb → test_tutorial_example.rb} +0 -0
- metadata +110 -72
- data/Manifest.txt +0 -123
- data/lib/adapter_helper/base.rb +0 -63
- data/lib/adapter_helper/mysql.rb +0 -13
- data/lib/adapter_helper/oracle.rb +0 -12
- data/lib/adapter_helper/postgresql.rb +0 -13
- data/lib/adapter_helper/sqlite3.rb +0 -13
- data/lib/composite_primary_keys/migration.rb +0 -20
- data/local/database_connections.rb.sample +0 -12
- data/local/paths.rb.sample +0 -2
- data/local/tasks.rb.sample +0 -2
- data/tasks/activerecord_selection.rake +0 -43
- data/tasks/databases.rake +0 -12
- data/tasks/deployment.rake +0 -22
- data/tasks/local_setup.rake +0 -13
- data/test/test_dummy.rb +0 -28
- data/tmp/test.db +0 -0
- data/website/index.html +0 -195
- data/website/index.txt +0 -159
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -126
- data/website/template.js +0 -3
- data/website/template.rhtml +0 -53
- data/website/version-raw.js +0 -3
- data/website/version-raw.txt +0 -2
- data/website/version.js +0 -4
- data/website/version.txt +0 -3
data/History.txt
CHANGED
@@ -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
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
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
27
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
#
|
42
|
-
|
43
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
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 =
|
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.
|
26
|
-
|
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 =
|
29
|
-
|
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
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
118
|
-
|
76
|
+
keys = record[reflection.cpk_primary_key]
|
77
|
+
ids << keys
|
119
78
|
|
120
|
-
|
121
|
-
|
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
|
-
|
137
|
-
|
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
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
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
|
104
|
+
conditions = [conditions] + ids.uniq.flatten
|
150
105
|
|
151
|
-
associated_records = klass.
|
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
|
-
|
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
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
128
|
+
conditions[0] << append_conditions(reflection, preload_options)
|
212
129
|
|
213
|
-
|
214
|
-
:select
|
215
|
-
: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
|
218
|
-
:group
|
219
|
-
:order
|
220
|
-
|
221
|
-
|
222
|
-
|
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
|