globe-composite_primary_keys 3.0.1

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