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