globe-composite_primary_keys 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/History.txt +203 -0
  2. data/Manifest.txt +121 -0
  3. data/README.txt +41 -0
  4. data/README_DB2.txt +33 -0
  5. data/Rakefile +30 -0
  6. data/composite_primary_keys.gemspec +17 -0
  7. data/lib/adapter_helper/base.rb +63 -0
  8. data/lib/adapter_helper/mysql.rb +13 -0
  9. data/lib/adapter_helper/oracle.rb +12 -0
  10. data/lib/adapter_helper/oracle_enhanced.rb +12 -0
  11. data/lib/adapter_helper/postgresql.rb +13 -0
  12. data/lib/adapter_helper/sqlite3.rb +13 -0
  13. data/lib/composite_primary_keys.rb +63 -0
  14. data/lib/composite_primary_keys/association_preload.rb +162 -0
  15. data/lib/composite_primary_keys/associations.rb +159 -0
  16. data/lib/composite_primary_keys/attribute_methods.rb +84 -0
  17. data/lib/composite_primary_keys/base.rb +200 -0
  18. data/lib/composite_primary_keys/composite_arrays.rb +29 -0
  19. data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +9 -0
  20. data/lib/composite_primary_keys/connection_adapters/ibm_db_adapter.rb +21 -0
  21. data/lib/composite_primary_keys/connection_adapters/oracle_adapter.rb +15 -0
  22. data/lib/composite_primary_keys/connection_adapters/oracle_enhanced_adapter.rb +17 -0
  23. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +53 -0
  24. data/lib/composite_primary_keys/connection_adapters/sqlite3_adapter.rb +15 -0
  25. data/lib/composite_primary_keys/finder_methods.rb +68 -0
  26. data/lib/composite_primary_keys/fixtures.rb +8 -0
  27. data/lib/composite_primary_keys/read.rb +25 -0
  28. data/lib/composite_primary_keys/reflection.rb +39 -0
  29. data/lib/composite_primary_keys/relation.rb +31 -0
  30. data/lib/composite_primary_keys/through_association_scope.rb +212 -0
  31. data/lib/composite_primary_keys/validations/uniqueness.rb +118 -0
  32. data/lib/composite_primary_keys/version.rb +9 -0
  33. data/loader.rb +24 -0
  34. data/local/database_connections.rb.sample +12 -0
  35. data/local/paths.rb.sample +2 -0
  36. data/local/tasks.rb.sample +2 -0
  37. data/scripts/console.rb +48 -0
  38. data/scripts/txt2html +67 -0
  39. data/scripts/txt2js +59 -0
  40. data/tasks/activerecord_selection.rake +43 -0
  41. data/tasks/databases.rake +12 -0
  42. data/tasks/databases/mysql.rake +30 -0
  43. data/tasks/databases/oracle.rake +25 -0
  44. data/tasks/databases/postgresql.rake +25 -0
  45. data/tasks/databases/sqlite3.rake +28 -0
  46. data/tasks/deployment.rake +22 -0
  47. data/tasks/local_setup.rake +13 -0
  48. data/tasks/website.rake +18 -0
  49. data/test/README_tests.txt +67 -0
  50. data/test/abstract_unit.rb +103 -0
  51. data/test/connections/native_ibm_db/connection.rb +23 -0
  52. data/test/connections/native_mysql/connection.rb +13 -0
  53. data/test/connections/native_oracle/connection.rb +14 -0
  54. data/test/connections/native_oracle_enhanced/connection.rb +20 -0
  55. data/test/connections/native_postgresql/connection.rb +8 -0
  56. data/test/connections/native_sqlite/connection.rb +9 -0
  57. data/test/fixtures/article.rb +5 -0
  58. data/test/fixtures/article_group.rb +4 -0
  59. data/test/fixtures/article_groups.yml +7 -0
  60. data/test/fixtures/articles.yml +6 -0
  61. data/test/fixtures/comment.rb +6 -0
  62. data/test/fixtures/comments.yml +16 -0
  63. data/test/fixtures/db_definitions/db2-create-tables.sql +113 -0
  64. data/test/fixtures/db_definitions/db2-drop-tables.sql +16 -0
  65. data/test/fixtures/db_definitions/mysql.sql +181 -0
  66. data/test/fixtures/db_definitions/oracle.drop.sql +39 -0
  67. data/test/fixtures/db_definitions/oracle.sql +188 -0
  68. data/test/fixtures/db_definitions/postgresql.sql +206 -0
  69. data/test/fixtures/db_definitions/sqlite.sql +166 -0
  70. data/test/fixtures/department.rb +5 -0
  71. data/test/fixtures/departments.yml +3 -0
  72. data/test/fixtures/dorm.rb +3 -0
  73. data/test/fixtures/dorms.yml +2 -0
  74. data/test/fixtures/employee.rb +4 -0
  75. data/test/fixtures/employees.yml +9 -0
  76. data/test/fixtures/group.rb +3 -0
  77. data/test/fixtures/groups.yml +3 -0
  78. data/test/fixtures/hack.rb +6 -0
  79. data/test/fixtures/hacks.yml +2 -0
  80. data/test/fixtures/kitchen_sink.rb +3 -0
  81. data/test/fixtures/kitchen_sinks.yml +5 -0
  82. data/test/fixtures/membership.rb +10 -0
  83. data/test/fixtures/membership_status.rb +3 -0
  84. data/test/fixtures/membership_statuses.yml +10 -0
  85. data/test/fixtures/memberships.yml +6 -0
  86. data/test/fixtures/product.rb +7 -0
  87. data/test/fixtures/product_tariff.rb +5 -0
  88. data/test/fixtures/product_tariffs.yml +12 -0
  89. data/test/fixtures/products.yml +6 -0
  90. data/test/fixtures/reading.rb +4 -0
  91. data/test/fixtures/readings.yml +10 -0
  92. data/test/fixtures/reference_code.rb +7 -0
  93. data/test/fixtures/reference_codes.yml +28 -0
  94. data/test/fixtures/reference_type.rb +7 -0
  95. data/test/fixtures/reference_types.yml +9 -0
  96. data/test/fixtures/restaurant.rb +6 -0
  97. data/test/fixtures/restaurants.yml +5 -0
  98. data/test/fixtures/restaurants_suburbs.yml +11 -0
  99. data/test/fixtures/room.rb +10 -0
  100. data/test/fixtures/room_assignment.rb +4 -0
  101. data/test/fixtures/room_assignments.yml +4 -0
  102. data/test/fixtures/room_attribute.rb +3 -0
  103. data/test/fixtures/room_attribute_assignment.rb +5 -0
  104. data/test/fixtures/room_attribute_assignments.yml +4 -0
  105. data/test/fixtures/room_attributes.yml +3 -0
  106. data/test/fixtures/rooms.yml +3 -0
  107. data/test/fixtures/seat.rb +5 -0
  108. data/test/fixtures/seats.yml +4 -0
  109. data/test/fixtures/street.rb +3 -0
  110. data/test/fixtures/streets.yml +15 -0
  111. data/test/fixtures/student.rb +4 -0
  112. data/test/fixtures/students.yml +2 -0
  113. data/test/fixtures/suburb.rb +6 -0
  114. data/test/fixtures/suburbs.yml +9 -0
  115. data/test/fixtures/tariff.rb +6 -0
  116. data/test/fixtures/tariffs.yml +13 -0
  117. data/test/fixtures/user.rb +10 -0
  118. data/test/fixtures/users.yml +6 -0
  119. data/test/hash_tricks.rb +34 -0
  120. data/test/plugins/pagination.rb +405 -0
  121. data/test/plugins/pagination_helper.rb +135 -0
  122. data/test/test_associations.rb +178 -0
  123. data/test/test_attribute_methods.rb +22 -0
  124. data/test/test_attributes.rb +80 -0
  125. data/test/test_clone.rb +34 -0
  126. data/test/test_composite_arrays.rb +32 -0
  127. data/test/test_create.rb +68 -0
  128. data/test/test_delete.rb +83 -0
  129. data/test/test_exists.rb +25 -0
  130. data/test/test_find.rb +73 -0
  131. data/test/test_ids.rb +90 -0
  132. data/test/test_miscellaneous.rb +39 -0
  133. data/test/test_pagination.rb +38 -0
  134. data/test/test_polymorphic.rb +32 -0
  135. data/test/test_santiago.rb +27 -0
  136. data/test/test_suite.rb +19 -0
  137. data/test/test_tutorial_example.rb +26 -0
  138. data/test/test_update.rb +40 -0
  139. data/test/test_validations.rb +11 -0
  140. data/website/index.html +195 -0
  141. data/website/index.txt +159 -0
  142. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  143. data/website/stylesheets/screen.css +126 -0
  144. data/website/template.js +3 -0
  145. data/website/template.rhtml +53 -0
  146. data/website/version-raw.js +3 -0
  147. data/website/version-raw.txt +2 -0
  148. data/website/version.js +4 -0
  149. data/website/version.txt +3 -0
  150. metadata +339 -0
@@ -0,0 +1,63 @@
1
+ module AdapterHelper
2
+ class Base
3
+ class << self
4
+ attr_accessor :adapter
5
+
6
+ def load_connection_from_env(adapter)
7
+ self.adapter = adapter
8
+ unless ENV['cpk_adapters']
9
+ puts error_msg_setup_helper
10
+ exit
11
+ end
12
+
13
+ ActiveRecord::Base.configurations = YAML.load(ENV['cpk_adapters'])
14
+ unless spec = ActiveRecord::Base.configurations[adapter]
15
+ puts error_msg_adapter_helper
16
+ exit
17
+ end
18
+ spec[:adapter] = adapter
19
+ spec
20
+ end
21
+
22
+ def error_msg_setup_helper
23
+ <<-EOS
24
+ Setup Helper:
25
+ CPK now has a place for your individual testing configuration.
26
+ That is, instead of hardcoding it in the Rakefile and test/connections files,
27
+ there is now a local/database_connections.rb file that is NOT in the
28
+ repository. Your personal DB information (username, password etc) can
29
+ be stored here without making it difficult to submit patches etc.
30
+
31
+ Installation:
32
+ i) cp locals/database_connections.rb.sample locals/database_connections.rb
33
+ ii) For #{adapter} connection details see "Adapter Setup Helper" below.
34
+ iii) Rerun this task
35
+
36
+ #{error_msg_adapter_helper}
37
+
38
+ Current ENV:
39
+ #{ENV.inspect}
40
+ EOS
41
+ end
42
+
43
+ def error_msg_adapter_helper
44
+ <<-EOS
45
+ Adapter Setup Helper:
46
+ To run #{adapter} tests, you need to setup your #{adapter} connections.
47
+ In your local/database_connections.rb file, within the ENV['cpk_adapter'] hash, add:
48
+ "#{adapter}" => { adapter settings }
49
+
50
+ That is, it will look like:
51
+ ENV['cpk_adapters'] = {
52
+ "#{adapter}" => {
53
+ :adapter => "#{adapter}",
54
+ :username => "root",
55
+ :password => "root",
56
+ # ...
57
+ }
58
+ }.to_yaml
59
+ EOS
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), 'base')
2
+
3
+ module AdapterHelper
4
+ class MySQL < Base
5
+ class << self
6
+ def load_connection_from_env
7
+ spec = super('mysql')
8
+ spec[:database] ||= 'composite_primary_keys_unittest'
9
+ spec
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ require File.join(File.dirname(__FILE__), 'base')
2
+
3
+ module AdapterHelper
4
+ class Oracle < Base
5
+ class << self
6
+ def load_connection_from_env
7
+ spec = super('oracle')
8
+ spec
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require File.join(File.dirname(__FILE__), 'base')
2
+
3
+ module AdapterHelper
4
+ class OracleEnhanced < Base
5
+ class << self
6
+ def load_connection_from_env
7
+ spec = super('oracle_enhanced')
8
+ spec
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), 'base')
2
+
3
+ module AdapterHelper
4
+ class Postgresql < Base
5
+ class << self
6
+ def load_connection_from_env
7
+ spec = super('postgresql')
8
+ spec[:database] ||= 'composite_primary_keys_unittest'
9
+ spec
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), 'base')
2
+
3
+ module AdapterHelper
4
+ class Sqlite3 < Base
5
+ class << self
6
+ def load_connection_from_env
7
+ spec = super('sqlite3')
8
+ spec[:dbfile] ||= "tmp/test.db"
9
+ spec
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,63 @@
1
+ #--
2
+ # Copyright (c) 2006 Nic Williams
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ $:.unshift(File.dirname(__FILE__)) unless
25
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
26
+
27
+ unless defined?(ActiveRecord)
28
+ begin
29
+ require 'active_record'
30
+ rescue LoadError
31
+ require 'rubygems'
32
+ require_gem 'activerecord'
33
+ end
34
+ end
35
+
36
+ require 'active_record/associations.rb'
37
+ require 'active_record/associations/association_collection'
38
+ require 'active_record/associations/association_proxy'
39
+ require 'active_record/associations/belongs_to_association'
40
+ require 'active_record/associations/belongs_to_polymorphic_association'
41
+ require 'active_record/associations/has_and_belongs_to_many_association'
42
+ require 'active_record/associations/has_many_association'
43
+ require 'active_record/associations/has_one_association'
44
+ require 'active_record/associations/has_one_through_association'
45
+
46
+ require 'composite_primary_keys/fixtures'
47
+ require 'composite_primary_keys/composite_arrays'
48
+ require 'composite_primary_keys/associations'
49
+ require 'composite_primary_keys/through_association_scope'
50
+ require 'composite_primary_keys/association_preload'
51
+ require 'composite_primary_keys/reflection'
52
+ require 'composite_primary_keys/relation'
53
+ require 'composite_primary_keys/read'
54
+ require 'composite_primary_keys/finder_methods'
55
+ require 'composite_primary_keys/base'
56
+ require 'composite_primary_keys/validations/uniqueness'
57
+
58
+ Dir[File.dirname(__FILE__) + '/composite_primary_keys/connection_adapters/*.rb'].each do |adapter|
59
+ begin
60
+ require adapter.gsub('.rb','')
61
+ rescue MissingSourceFile
62
+ end
63
+ end
@@ -0,0 +1,162 @@
1
+ module CompositePrimaryKeys
2
+ module ActiveRecord
3
+ module AssociationPreload
4
+ def self.append_features(base)
5
+ super
6
+ base.send(:extend, ClassMethods)
7
+ end
8
+
9
+ # Composite key versions of Association functions
10
+ module ClassMethods
11
+ def preload_has_and_belongs_to_many_association(records, reflection, preload_options={})
12
+ table_name = reflection.klass.quoted_table_name
13
+ id_to_record_map, ids = construct_id_map(records)
14
+ records.each {|record| record.send(reflection.name).loaded}
15
+ options = reflection.options
16
+
17
+ if composite?
18
+ where = (primary_key * ids.size).in_groups_of(primary_key.size).map do |keys|
19
+ "(" + keys.map{|key| "t0.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
20
+ end.join(" OR ")
21
+
22
+ conditions = [where, ids].flatten
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)}"
24
+ parent_primary_keys = reflection.cpk_primary_key.map{|k| "t0.#{connection.quote_column_name(k)}"}
25
+
26
+ concat_arr = ["'['"] +
27
+ parent_primary_keys.zip(["', '"] * (parent_primary_keys.size - 1)).flatten.compact +
28
+ ["']'"]
29
+
30
+ parent_record_id = connection.concat(*concat_arr)
31
+ else
32
+ conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
33
+ conditions << append_conditions(reflection, preload_options)
34
+ conditions = [conditions, ids]
35
+ 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}"
36
+ parent_record_id = reflection.primary_key_name
37
+ end
38
+
39
+ associated_records = reflection.klass.unscoped.where(conditions).
40
+ includes(options[:include]).
41
+ joins(joins).
42
+ select("#{options[:select] || table_name+'.*'}, #{parent_record_id} as the_parent_record_id").
43
+ order(options[:order]).to_a
44
+
45
+ set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id')
46
+ end
47
+
48
+ def preload_belongs_to_association(records, reflection, preload_options={})
49
+ return if records.first.send("loaded_#{reflection.name}?")
50
+ options = reflection.options
51
+
52
+ ids = Array.new
53
+
54
+ if options[:polymorphic]
55
+ # CPK
56
+ #polymorph_type = options[:foreign_type]
57
+ #klasses_and_ids = {}
58
+
59
+ # Construct a mapping from klass to a list of ids to load and a mapping of those ids back to their parent_records
60
+ #records.each do |record|
61
+ # if klass = record.send(polymorph_type)
62
+ # klass_id = record.send(primary_key_name)
63
+ # if klass_id
64
+ # id_map = klasses_and_ids[klass] ||= {}
65
+ # id_list_for_klass_id = (id_map[klass_id.to_s] ||= [])
66
+ # id_list_for_klass_id << record
67
+ # end
68
+ # end
69
+ #end
70
+ #klasses_and_ids = klasses_and_ids.to_a
71
+ raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
72
+ else
73
+ # I need to keep the original ids for each record (as opposed to the stringified) so
74
+ # that they get properly converted for each db so the id_map ends up looking like:
75
+ #
76
+ # { '1,2' => {:id => [1,2], :records => [...records...]}}
77
+ id_map = {}
78
+
79
+ records.each do |record|
80
+ keys = reflection.cpk_primary_key.map{|k| record.send(k)}
81
+ ids << keys
82
+
83
+ mapped_records = (id_map[keys.to_s] ||= [])
84
+ mapped_records << record
85
+ end
86
+
87
+ klasses_and_ids = [[reflection.klass.name, id_map]]
88
+ end
89
+
90
+ klasses_and_ids.each do |klass_and_id|
91
+ klass_name, id_map = *klass_and_id
92
+ next if id_map.empty?
93
+ klass = klass_name.constantize
94
+
95
+ table_name = klass.quoted_table_name
96
+ primary_key = [reflection.options[:primary_key] || klass.primary_key].flatten
97
+
98
+ # CPK
99
+ conditions = id_map.map do |key, value|
100
+ "(" +
101
+ primary_key.map do |key|
102
+ "#{table_name}.#{connection.quote_column_name(key)} = ?"
103
+ end.join(' AND ') + ")"
104
+ end.join(' OR ')
105
+
106
+ conditions << append_conditions(reflection, preload_options)
107
+
108
+ conditions = [conditions] + ids.uniq.flatten
109
+
110
+ associated_records = klass.unscoped.where(conditions).apply_finder_options(options.slice(:include, :select, :joins, :order)).to_a
111
+
112
+ set_association_single_records(id_map, reflection.name, associated_records, primary_key)
113
+ end
114
+ end
115
+
116
+ def find_associated_records(ids, reflection, preload_options)
117
+ options = reflection.options
118
+ table_name = reflection.klass.quoted_table_name
119
+
120
+ if interface = reflection.options[:as]
121
+ 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}'"
122
+ else
123
+ foreign_key = reflection.cpk_primary_key
124
+
125
+ where = (foreign_key * ids.size).in_groups_of(foreign_key.size).map do |keys|
126
+ "(" + keys.map{|key| "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
127
+ end.join(" OR ")
128
+
129
+ conditions = [where, ids].flatten
130
+ end
131
+
132
+ conditions[0] << append_conditions(reflection, preload_options)
133
+
134
+ find_options = {
135
+ :select => preload_options[:select] || options[:select] || "#{table_name}.*",
136
+ :include => preload_options[:include] || options[:include],
137
+ # CPK
138
+ # :conditions => [conditions, ids],
139
+ :conditions => conditions,
140
+ :joins => options[:joins],
141
+ :group => preload_options[:group] || options[:group],
142
+ :order => preload_options[:order] || options[:order]
143
+ }
144
+
145
+ reflection.klass.unscoped.apply_finder_options(find_options).to_a
146
+ end
147
+
148
+ def full_composite_join_clause(reflection, table1, full_keys1, table2, full_keys2)
149
+ connection = reflection.active_record.connection
150
+ full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
151
+ full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
152
+ where_clause = [full_keys1, full_keys2].transpose.map do |key_pair|
153
+ quoted1 = connection.quote_table_name(table1)
154
+ quoted2 = connection.quote_table_name(table2)
155
+ "#{quoted1}.#{connection.quote_column_name(key_pair.first)}=#{quoted2}.#{connection.quote_column_name(key_pair.last)}"
156
+ end.join(" AND ")
157
+ "(#{where_clause})"
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,159 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class AssociationProxy
4
+ def full_columns_equals(table_name, keys, quoted_ids)
5
+ quoted_table_name = @owner.connection.quote_table_name(table_name)
6
+
7
+ keys = [keys].flatten
8
+ ids = [quoted_ids].flatten
9
+
10
+ [keys,ids].transpose.map do |key, id|
11
+ "(#{quoted_table_name}.#{@owner.connection.quote_column_name(key)} = #{id})"
12
+ end.join(' AND ')
13
+ end
14
+
15
+ def set_belongs_to_association_for(record)
16
+ if @reflection.options[:as]
17
+ record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
18
+ record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
19
+ else
20
+ unless @owner.new_record?
21
+ primary_key = @reflection.options[:primary_key] || :id
22
+ # CPK
23
+ # record[@reflection.primary_key_name] = @owner.send(primary_key)
24
+ values = [@owner.send(primary_key)].flatten
25
+ key_values = @reflection.cpk_primary_key.zip(values)
26
+ key_values.each {|key, value| record[key] = value}
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ class HasAndBelongsToManyAssociation
33
+ def construct_sql
34
+ if @reflection.options[:finder_sql]
35
+ @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
36
+ else
37
+ # CPK
38
+ # @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} "
39
+ @finder_sql = full_columns_equals(@reflection.options[:join_table], @reflection.cpk_primary_key, owner_quoted_id)
40
+ @finder_sql << " AND (#{conditions})" if conditions
41
+ end
42
+
43
+ join_condition = if composite?
44
+ conditions = Array.new
45
+ primary_keys.length.times do |i|
46
+ conditions << "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key[i]} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key[i]}"
47
+ end
48
+ conditions.join(' AND ')
49
+ else
50
+ "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
51
+ end
52
+ #@join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
53
+ @join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON (#{join_condition})"
54
+
55
+ construct_counter_sql
56
+ end
57
+ end
58
+
59
+ class HasManyAssociation
60
+ def construct_sql
61
+ case
62
+ when @reflection.options[:finder_sql]
63
+ @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
64
+
65
+ when @reflection.options[:as]
66
+ @finder_sql =
67
+ "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
68
+ "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
69
+ @finder_sql << " AND (#{conditions})" if conditions
70
+
71
+ else
72
+ # CPK
73
+ # @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
74
+ @finder_sql = full_columns_equals(@reflection.quoted_table_name, @reflection.cpk_primary_key, owner_quoted_id)
75
+ @finder_sql << " AND (#{conditions})" if conditions
76
+ end
77
+
78
+ construct_counter_sql
79
+ end
80
+
81
+ # Deletes the records according to the <tt>:dependent</tt> option.
82
+ def delete_records(records)
83
+ case @reflection.options[:dependent]
84
+ when :destroy
85
+ records.each { |r| r.destroy }
86
+ when :delete_all
87
+ @reflection.klass.delete(records.map { |record| record.id })
88
+ else
89
+ relation = Arel::Table.new(@reflection.table_name)
90
+ # CPK
91
+ # relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
92
+ # and(Arel::Predicates::In.new(relation[@reflection.klass.primary_key], records.map(&:id)))
93
+ # ).update(relation[@reflection.primary_key_name] => nil)
94
+ id_predicate = nil
95
+ owner_key_values = @reflection.cpk_primary_key.zip([@owner.id].flatten)
96
+ owner_key_values.each do |key, value|
97
+ eq = relation[key].eq(value)
98
+ id_predicate = id_predicate ? id_predicate.and(eq) : eq
99
+ end
100
+
101
+ record_predicates = nil
102
+ records.each do |record|
103
+ keys = [@reflection.klass.primary_key].flatten
104
+ values = [record.id].flatten
105
+
106
+ record_predicate = nil
107
+ keys.zip(values).each do |key, value|
108
+ eq = relation[key].eq(value)
109
+ record_predicate = record_predicate ? record_predicate.and(eq) : eq
110
+ end
111
+ record_predicates = record_predicates ? record_predicates.or(record_predicate) : record_predicate
112
+ end
113
+
114
+ relation = relation.where(id_predicate.and(record_predicates))
115
+
116
+ nullify = @reflection.cpk_primary_key.inject(Hash.new) do |hash, key|
117
+ hash[relation[key]] = nil
118
+ hash
119
+ end
120
+
121
+ relation.update(nullify)
122
+
123
+ @owner.class.update_counters(@owner.id, cached_counter_attribute_name => -records.size) if has_cached_counter?
124
+ end
125
+ end
126
+ end
127
+
128
+ class HasOneAssociation
129
+ def construct_sql
130
+ case
131
+ when @reflection.options[:as]
132
+ @finder_sql =
133
+ "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
134
+ "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
135
+ else
136
+ # CPK
137
+ #@finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
138
+ @finder_sql = full_columns_equals(@reflection.quoted_table_name, @reflection.cpk_primary_key, owner_quoted_id)
139
+ end
140
+ @finder_sql << " AND (#{conditions})" if conditions
141
+ end
142
+ end
143
+
144
+ class JoinDependency
145
+ class JoinBase
146
+ def column_names_with_alias
147
+ unless defined?(@column_names_with_alias)
148
+ @column_names_with_alias = []
149
+ keys = active_record.composite? ? primary_key.map(&:to_s) : [primary_key]
150
+ (keys + (column_names - keys)).each_with_index do |column_name, i|
151
+ @column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
152
+ end
153
+ end
154
+ @column_names_with_alias
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end