composite_primary_keys 2.3.5.1 → 3.0.0.b2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. data/History.txt +26 -0
  2. data/README.txt +1 -1
  3. data/Rakefile +41 -51
  4. data/lib/composite_primary_keys.rb +19 -7
  5. data/lib/composite_primary_keys/association_preload.rb +75 -170
  6. data/lib/composite_primary_keys/associations.rb +98 -400
  7. data/lib/composite_primary_keys/associations/association_proxy.rb +32 -0
  8. data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +30 -0
  9. data/lib/composite_primary_keys/associations/has_many_association.rb +72 -0
  10. data/lib/composite_primary_keys/associations/has_one_association.rb +19 -0
  11. data/lib/composite_primary_keys/associations/through_association_scope.rb +103 -0
  12. data/lib/composite_primary_keys/base.rb +148 -305
  13. data/lib/composite_primary_keys/calculations.rb +22 -64
  14. data/lib/composite_primary_keys/composite_arrays.rb +3 -10
  15. data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +9 -0
  16. data/lib/composite_primary_keys/connection_adapters/oracle_enhanced_adapter.rb +17 -0
  17. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +1 -1
  18. data/lib/composite_primary_keys/finder_methods.rb +71 -0
  19. data/lib/composite_primary_keys/fixtures.rb +1 -1
  20. data/lib/composite_primary_keys/read.rb +25 -0
  21. data/lib/composite_primary_keys/reflection.rb +30 -10
  22. data/lib/composite_primary_keys/relation.rb +31 -0
  23. data/lib/composite_primary_keys/validations/uniqueness.rb +106 -66
  24. data/lib/composite_primary_keys/version.rb +4 -4
  25. data/scripts/console.rb +1 -1
  26. data/tasks/Rakefile.rb +13 -0
  27. data/tasks/databases/mysql.rake +11 -15
  28. data/tasks/databases/oracle.rake +10 -11
  29. data/tasks/databases/postgresql.rake +10 -13
  30. data/tasks/databases/sqlite3.rake +9 -9
  31. data/test/README_tests.txt +1 -45
  32. data/test/abstract_unit.rb +17 -14
  33. data/test/connections/connection_spec.rb +19 -0
  34. data/test/connections/databases.example.yml +11 -0
  35. data/test/connections/databases.yml +13 -0
  36. data/test/connections/native_mysql/connection.rb +10 -2
  37. data/test/connections/native_oracle/connection.rb +7 -4
  38. data/test/connections/native_oracle_enhanced/connection.rb +23 -0
  39. data/test/connections/native_postgresql/connection.rb +13 -5
  40. data/test/connections/native_sqlite/connection.rb +7 -3
  41. data/test/fixtures/article_group.rb +4 -0
  42. data/test/fixtures/article_groups.yml +7 -0
  43. data/test/fixtures/db_definitions/postgresql.sql +2 -1
  44. data/test/fixtures/debug.log +133 -0
  45. data/test/fixtures/dorm.rb +3 -0
  46. data/test/fixtures/dorms.yml +2 -0
  47. data/test/fixtures/kitchen_sink.rb +3 -0
  48. data/test/fixtures/kitchen_sinks.yml +5 -0
  49. data/test/fixtures/reference_codes.yml +2 -0
  50. data/test/fixtures/reference_type.rb +1 -1
  51. data/test/fixtures/restaurant.rb +6 -0
  52. data/test/fixtures/restaurants.yml +5 -0
  53. data/test/fixtures/restaurants_suburbs.yml +11 -0
  54. data/test/fixtures/room.rb +10 -0
  55. data/test/fixtures/room_assignment.rb +4 -0
  56. data/test/fixtures/room_assignments.yml +4 -0
  57. data/test/fixtures/room_attribute.rb +3 -0
  58. data/test/fixtures/room_attribute_assignment.rb +5 -0
  59. data/test/fixtures/room_attribute_assignments.yml +4 -0
  60. data/test/fixtures/room_attributes.yml +3 -0
  61. data/test/fixtures/rooms.yml +3 -0
  62. data/test/fixtures/seat.rb +5 -0
  63. data/test/fixtures/seats.yml +4 -0
  64. data/test/fixtures/student.rb +4 -0
  65. data/test/fixtures/students.yml +2 -0
  66. data/test/test_associations.rb +27 -50
  67. data/test/test_attributes.rb +15 -19
  68. data/test/test_composite_arrays.rb +2 -21
  69. data/test/test_create.rb +3 -3
  70. data/test/test_delete.rb +7 -20
  71. data/test/test_exists.rb +3 -7
  72. data/test/test_find.rb +0 -8
  73. data/test/test_ids.rb +3 -17
  74. data/test/test_polymorphic.rb +5 -4
  75. data/test/test_suite.rb +19 -0
  76. data/test/{test_tutorial_examle.rb → test_tutorial_example.rb} +0 -0
  77. metadata +110 -72
  78. data/Manifest.txt +0 -123
  79. data/lib/adapter_helper/base.rb +0 -63
  80. data/lib/adapter_helper/mysql.rb +0 -13
  81. data/lib/adapter_helper/oracle.rb +0 -12
  82. data/lib/adapter_helper/postgresql.rb +0 -13
  83. data/lib/adapter_helper/sqlite3.rb +0 -13
  84. data/lib/composite_primary_keys/migration.rb +0 -20
  85. data/local/database_connections.rb.sample +0 -12
  86. data/local/paths.rb.sample +0 -2
  87. data/local/tasks.rb.sample +0 -2
  88. data/tasks/activerecord_selection.rake +0 -43
  89. data/tasks/databases.rake +0 -12
  90. data/tasks/deployment.rake +0 -22
  91. data/tasks/local_setup.rake +0 -13
  92. data/test/test_dummy.rb +0 -28
  93. data/tmp/test.db +0 -0
  94. data/website/index.html +0 -195
  95. data/website/index.txt +0 -159
  96. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  97. data/website/stylesheets/screen.css +0 -126
  98. data/website/template.js +0 -3
  99. data/website/template.rhtml +0 -53
  100. data/website/version-raw.js +0 -3
  101. data/website/version-raw.txt +0 -2
  102. data/website/version.js +0 -4
  103. data/website/version.txt +0 -3
@@ -1,429 +1,127 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module Associations
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
-
12
- def construct_counter_sql_with_included_associations(options, join_dependency)
13
- scope = scope(:find)
14
- sql = "SELECT COUNT(DISTINCT #{quoted_table_columns(primary_key)})"
15
-
16
- # A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
17
- if !self.connection.supports_count_distinct?
18
- sql = "SELECT COUNT(*) FROM (SELECT DISTINCT #{quoted_table_columns(primary_key)}"
19
- end
20
-
21
- sql << " FROM #{quoted_table_name} "
22
- sql << join_dependency.join_associations.collect{|join| join.association_join }.join
23
-
24
- add_joins!(sql, options[:joins], scope)
25
- add_conditions!(sql, options[:conditions], scope)
26
- add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
27
-
28
- add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
29
-
30
- if !self.connection.supports_count_distinct?
31
- sql << ")"
1
+ module ActiveRecord
2
+ module Associations
3
+ module ClassMethods
4
+ class JoinDependency
5
+ class JoinBase
6
+ def column_names_with_alias
7
+ unless defined?(@column_names_with_alias)
8
+ @column_names_with_alias = []
9
+ keys = active_record.composite? ? primary_key.map(&:to_s) : [primary_key]
10
+ (keys + (column_names - keys)).each_with_index do |column_name, i|
11
+ @column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
12
+ end
13
+ end
14
+ @column_names_with_alias
32
15
  end
33
-
34
- return sanitize_sql(sql)
35
16
  end
36
17
 
37
- def construct_finder_sql_with_included_associations(options, join_dependency)
38
- scope = scope(:find)
39
- sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
40
- sql << join_dependency.join_associations.collect{|join| join.association_join }.join
41
-
42
- add_joins!(sql, options[:joins], scope)
43
- add_conditions!(sql, options[:conditions], scope)
44
- add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && options[:limit]
45
-
46
- sql << "ORDER BY #{options[:order]} " if options[:order]
47
-
48
- add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
49
-
50
- return sanitize_sql(sql)
51
- end
52
-
53
- def table_columns(columns)
54
- columns.collect {|column| "#{self.quoted_table_name}.#{connection.quote_column_name(column)}"}
55
- end
56
-
57
- def quoted_table_columns(columns)
58
- table_columns(columns).join(ID_SEP)
59
- end
60
-
61
- end
62
-
63
- end
64
- end
65
- end
66
-
67
- module ActiveRecord::Associations::ClassMethods
68
- class JoinDependency
69
- def construct_association(record, join, row)
70
- case join.reflection.macro
71
- when :has_many, :has_and_belongs_to_many
72
- collection = record.send(join.reflection.name)
73
- collection.loaded
18
+ class JoinAssociation
19
+ # Ugly to include this twice, but I couldn't figure out how to make this
20
+ # work via a module
21
+ def composite_join_predicates(table1, keys1, table2, keys2)
22
+ attributes1 = [keys1].flatten.map do |key|
23
+ table1[key]
24
+ end
74
25
 
75
- join_aliased_primary_keys = join.active_record.composite? ?
76
- join.aliased_primary_key : [join.aliased_primary_key]
77
- return nil if
78
- record.id.to_s != join.parent.record_id(row).to_s or not
79
- join_aliased_primary_keys.select {|key| row[key].nil?}.blank?
80
- association = join.instantiate(row)
81
- collection.target.push(association) unless collection.target.include?(association)
82
- when :has_one, :belongs_to
83
- return if record.id.to_s != join.parent.record_id(row).to_s or
84
- [*join.aliased_primary_key].any? { |key| row[key].nil? }
85
- association = join.instantiate(row)
86
- record.send("set_#{join.reflection.name}_target", association)
87
- else
88
- raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
89
- end
90
- return association
91
- end
26
+ attributes2 = [keys2].flatten.map do |key|
27
+ table2[key]
28
+ end
92
29
 
93
- class JoinBase
94
- def aliased_primary_key
95
- active_record.composite? ?
96
- primary_key.inject([]) {|aliased_keys, key| aliased_keys << "#{ aliased_prefix }_r#{aliased_keys.length}"} :
97
- "#{ aliased_prefix }_r0"
98
- end
30
+ [attributes1, attributes2].transpose.map do |attribute1, attribute2|
31
+ attribute1.eq(attribute2)
32
+ end
33
+ end
99
34
 
100
- def record_id(row)
101
- active_record.composite? ?
102
- aliased_primary_key.map {|key| row[key]}.to_composite_ids :
103
- row[aliased_primary_key]
104
- end
35
+ def association_join
36
+ return @join if @join
105
37
 
106
- def column_names_with_alias
107
- unless @column_names_with_alias
108
- @column_names_with_alias = []
109
- keys = active_record.composite? ? primary_key.map(&:to_s) : [primary_key]
110
- (keys + (column_names - keys)).each_with_index do |column_name, i|
111
- @column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
112
- end
113
- end
114
- return @column_names_with_alias
115
- end
116
- end
38
+ aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
39
+ parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine)
117
40
 
118
- class JoinAssociation < JoinBase
119
- alias single_association_join association_join
120
- def association_join
121
- reflection.active_record.composite? ? composite_association_join : single_association_join
122
- end
41
+ @join = case reflection.macro
42
+ when :has_and_belongs_to_many
43
+ join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine)
44
+ fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
45
+ klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key
123
46
 
124
- def composite_association_join
125
- join = case reflection.macro
126
- when :has_and_belongs_to_many
127
- " LEFT OUTER JOIN %s ON %s " % [
128
- table_alias_for(options[:join_table], aliased_join_table_name),
129
- composite_join_clause(
130
- full_keys(aliased_join_table_name, options[:foreign_key] || reflection.active_record.to_s.classify.foreign_key),
131
- full_keys(reflection.active_record.table_name, reflection.active_record.primary_key)
132
- )
133
- ] +
134
- " LEFT OUTER JOIN %s ON %s " % [
135
- table_name_and_alias,
136
- composite_join_clause(
137
- full_keys(aliased_table_name, klass.primary_key),
138
- full_keys(aliased_join_table_name, options[:association_foreign_key] || klass.table_name.classify.foreign_key)
139
- )
47
+ [
48
+ join_table[fk].eq(parent_table[reflection.active_record.primary_key]),
49
+ aliased_table[klass.primary_key].eq(join_table[klass_fk])
140
50
  ]
141
- when :has_many, :has_one
142
- case
143
- when reflection.macro == :has_many && reflection.options[:through]
144
- through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
51
+ when :has_many, :has_one
52
+ if reflection.options[:through]
53
+ join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine)
54
+ jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
55
+ first_key = second_key = as_extra = nil
56
+
145
57
  if through_reflection.options[:as] # has_many :through against a polymorphic join
146
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
58
+ jt_foreign_key = through_reflection.options[:as].to_s + '_id'
59
+ jt_as_extra = join_table[through_reflection.options[:as].to_s + '_type'].eq(parent.active_record.base_class.name)
147
60
  else
148
- if source_reflection.macro == :has_many && source_reflection.options[:as]
149
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
61
+ jt_foreign_key = through_reflection.primary_key_name
62
+ end
63
+
64
+ case source_reflection.macro
65
+ when :has_many
66
+ if source_reflection.options[:as]
67
+ first_key = "#{source_reflection.options[:as]}_id"
68
+ second_key = options[:foreign_key] || primary_key
69
+ as_extra = aliased_table["#{source_reflection.options[:as]}_type"].eq(source_reflection.active_record.base_class.name)
150
70
  else
151
- case source_reflection.macro
152
- when :belongs_to
153
- first_key = primary_key
154
- second_key = options[:foreign_key] || klass.to_s.classify.foreign_key
155
- when :has_many
156
- first_key = through_reflection.klass.to_s.classify.foreign_key
157
- second_key = options[:foreign_key] || primary_key
158
- end
71
+ first_key = through_reflection.klass.base_class.to_s.foreign_key
72
+ second_key = options[:foreign_key] || primary_key
73
+ end
159
74
 
160
- " LEFT OUTER JOIN %s ON %s " % [
161
- table_alias_for(through_reflection.klass.table_name, aliased_join_table_name),
162
- composite_join_clause(
163
- full_keys(aliased_join_table_name, through_reflection.primary_key_name),
164
- full_keys(parent.aliased_table_name, parent.primary_key)
165
- )
166
- ] +
167
- " LEFT OUTER JOIN %s ON %s " % [
168
- table_name_and_alias,
169
- composite_join_clause(
170
- full_keys(aliased_table_name, first_key),
171
- full_keys(aliased_join_table_name, second_key)
172
- )
173
- ]
75
+ unless through_reflection.klass.descends_from_active_record?
76
+ jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name)
77
+ end
78
+ when :belongs_to
79
+ first_key = primary_key
80
+ if reflection.options[:source_type]
81
+ second_key = source_reflection.association_foreign_key
82
+ jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type])
83
+ else
84
+ second_key = source_reflection.primary_key_name
174
85
  end
175
86
  end
176
87
 
177
- when reflection.macro == :has_many && reflection.options[:as]
178
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
179
- when reflection.macro == :has_one && reflection.options[:as]
180
- raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
88
+ [
89
+ [parent_table[parent.primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].reject{|x| x.blank? },
90
+ aliased_table[first_key].eq(join_table[second_key])
91
+ ]
92
+ elsif reflection.options[:as]
93
+ id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key])
94
+ type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name)
95
+ [id_rel, type_rel]
181
96
  else
182
97
  foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
183
- primary_key = options[:primary_key] || parent.primary_key
184
- " LEFT OUTER JOIN %s ON %s " % [
185
- table_name_and_alias,
186
- composite_join_clause(
187
- full_keys(aliased_table_name, foreign_key),
188
- full_keys(parent.aliased_table_name, primary_key)),
189
- ]
98
+ # CPK
99
+ # [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
100
+ composite_join_predicates(aliased_table, foreign_key,
101
+ parent_table, reflection.options[:primary_key] || parent.primary_key)
102
+ end
103
+ when :belongs_to
104
+ [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.cpk_primary_key])]
190
105
  end
191
- when :belongs_to
192
- " LEFT OUTER JOIN %s ON %s " % [
193
- table_name_and_alias,
194
- composite_join_clause(
195
- full_keys(aliased_table_name, reflection.klass.primary_key),
196
- full_keys(parent.aliased_table_name, options[:foreign_key] || klass.to_s.foreign_key)),
197
- ]
198
- else
199
- ""
200
- end || ''
201
- join << %(AND %s.%s = %s ) % [
202
- aliased_table_name,
203
- reflection.active_record.connection.quote_column_name(reflection.active_record.inheritance_column),
204
- klass.connection.quote(klass.name)] unless klass.descends_from_active_record?
205
- join << "AND #{interpolate_sql(sanitize_sql(reflection.options[:conditions]))} " if reflection.options[:conditions]
206
- join
207
- end
208
-
209
- def full_keys(table_name, keys)
210
- connection = reflection.active_record.connection
211
- quoted_table_name = connection.quote_table_name(table_name)
212
- if keys.is_a?(Array)
213
- keys.collect {|key| "#{quoted_table_name}.#{connection.quote_column_name(key)}"}.join(CompositePrimaryKeys::ID_SEP)
214
- else
215
- "#{quoted_table_name}.#{connection.quote_column_name(keys)}"
216
- end
217
- end
218
-
219
- def composite_join_clause(full_keys1, full_keys2)
220
- full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
221
- full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
222
- where_clause = [full_keys1, full_keys2].transpose.map do |key1, key2|
223
- "#{key1}=#{key2}"
224
- end.join(" AND ")
225
- "(#{where_clause})"
226
- end
227
- end
228
- end
229
- end
230
-
231
- module ActiveRecord::Associations
232
- class AssociationProxy #:nodoc:
233
-
234
- def composite_where_clause(full_keys, ids)
235
- full_keys = full_keys.split(CompositePrimaryKeys::ID_SEP) if full_keys.is_a?(String)
236
-
237
- if ids.is_a?(String)
238
- ids = [[ids]]
239
- elsif not ids.first.is_a?(Array) # if single comp key passed, turn into an array of 1
240
- ids = [ids.to_composite_ids]
241
- end
242
-
243
- where_clause = ids.map do |id_set|
244
- transposed = id_set.size == 1 ? [[full_keys, id_set.first]] : [full_keys, id_set].transpose
245
- transposed.map do |full_key, id|
246
- "#{full_key.to_s}=#{@reflection.klass.sanitize(id)}"
247
- end.join(" AND ")
248
- end.join(") OR (")
249
-
250
- "(#{where_clause})"
251
- end
252
-
253
- def composite_join_clause(full_keys1, full_keys2)
254
- full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
255
- full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
256
-
257
- where_clause = [full_keys1, full_keys2].transpose.map do |key1, key2|
258
- "#{key1}=#{key2}"
259
- end.join(" AND ")
260
-
261
- "(#{where_clause})"
262
- end
263
-
264
- def full_composite_join_clause(table1, full_keys1, table2, full_keys2)
265
- connection = @reflection.active_record.connection
266
- full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
267
- full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
268
106
 
269
- quoted1 = connection.quote_table_name(table1)
270
- quoted2 = connection.quote_table_name(table2)
271
-
272
- where_clause = [full_keys1, full_keys2].transpose.map do |key_pair|
273
- "#{quoted1}.#{connection.quote_column_name(key_pair.first)}=#{quoted2}.#{connection.quote_column_name(key_pair.last)}"
274
- end.join(" AND ")
107
+ unless klass.descends_from_active_record?
108
+ sti_column = aliased_table[klass.inheritance_column]
109
+ sti_condition = sti_column.eq(klass.sti_name)
110
+ klass.send(:subclasses).each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
275
111
 
276
- "(#{where_clause})"
277
- end
278
-
279
- def full_keys(table_name, keys)
280
- connection = @reflection.active_record.connection
281
- quoted_table_name = connection.quote_table_name(table_name)
282
- keys = keys.split(CompositePrimaryKeys::ID_SEP) if keys.is_a?(String)
283
- if keys.is_a?(Array)
284
- keys.collect {|key| "#{quoted_table_name}.#{connection.quote_column_name(key)}"}.join(CompositePrimaryKeys::ID_SEP)
285
- else
286
- "#{quoted_table_name}.#{connection.quote_column_name(keys)}"
287
- end
288
- end
289
-
290
- def full_columns_equals(table_name, keys, quoted_ids)
291
- connection = @reflection.active_record.connection
292
- quoted_table_name = connection.quote_table_name(table_name)
293
- if keys.is_a?(Symbol) or (keys.is_a?(String) and keys == keys.to_s.split(CompositePrimaryKeys::ID_SEP))
294
- return "#{quoted_table_name}.#{connection.quote_column_name(keys)} = #{quoted_ids}"
295
- end
296
- keys = keys.split(CompositePrimaryKeys::ID_SEP) if keys.is_a?(String)
297
- quoted_ids = quoted_ids.split(CompositePrimaryKeys::ID_SEP) if quoted_ids.is_a?(String)
298
- keys_ids = [keys, quoted_ids].transpose
299
- keys_ids.collect {|key, id| "(#{quoted_table_name}.#{connection.quote_column_name(key)} = #{id})"}.join(' AND ')
300
- end
301
-
302
- def set_belongs_to_association_for(record)
303
- if @reflection.options[:as]
304
- record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
305
- record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
306
- else
307
- key_values = @reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP).zip([@owner.id].flatten)
308
- key_values.each{|key, value| record[key] = value} unless @owner.new_record?
309
- end
310
- end
311
- end
312
-
313
- class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
314
- def construct_sql
315
- @reflection.options[:finder_sql] &&= interpolate_sql(@reflection.options[:finder_sql])
316
-
317
- if @reflection.options[:finder_sql]
318
- @finder_sql = @reflection.options[:finder_sql]
319
- else
320
- @finder_sql = full_columns_equals(@reflection.options[:join_table], @reflection.primary_key_name, @owner.quoted_id)
321
- @finder_sql << " AND (#{conditions})" if conditions
322
- end
323
-
324
- @join_sql = "INNER JOIN #{@reflection.active_record.connection.quote_table_name(@reflection.options[:join_table])} ON " +
325
- full_composite_join_clause(@reflection.klass.table_name, @reflection.klass.primary_key, @reflection.options[:join_table], @reflection.association_foreign_key)
326
- end
327
- end
328
-
329
- class HasManyAssociation < AssociationCollection #:nodoc:
330
- def construct_sql
331
- case
332
- when @reflection.options[:finder_sql]
333
- @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
334
-
335
- when @reflection.options[:as]
336
- @finder_sql =
337
- "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
338
- "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
339
- @finder_sql << " AND (#{conditions})" if conditions
340
-
341
- else
342
- @finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, owner_quoted_id)
343
- @finder_sql << " AND (#{conditions})" if conditions
344
- end
345
-
346
- if @reflection.options[:counter_sql]
347
- @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
348
- elsif @reflection.options[:finder_sql]
349
- # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
350
- @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
351
- @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
352
- else
353
- @counter_sql = @finder_sql
354
- end
355
- end
356
-
357
- def delete_records(records)
358
- if @reflection.options[:dependent]
359
- records.each { |r| r.destroy }
360
- else
361
- connection = @reflection.active_record.connection
362
- field_names = @reflection.primary_key_name.split(',')
363
- field_names.collect! {|n| connection.quote_column_name(n) + " = NULL"}
364
- records.each do |r|
365
- where_clause = nil
366
-
367
- if r.quoted_id.to_s.include?(CompositePrimaryKeys::ID_SEP)
368
- where_clause_terms = [@reflection.klass.primary_key, r.quoted_id].transpose.map do |pair|
369
- "(#{connection.quote_column_name(pair[0])} = #{pair[1]})"
112
+ @join << sti_condition
370
113
  end
371
- where_clause = where_clause_terms.join(" AND ")
372
- else
373
- where_clause = connection.quote_column_name(@reflection.klass.primary_key) + ' = ' + r.quoted_id
374
- end
375
-
376
- @reflection.klass.update_all( field_names.join(',') , where_clause)
377
- end
378
- end
379
- end
380
- end
381
114
 
382
- class HasOneAssociation < BelongsToAssociation #:nodoc:
383
- def construct_sql
384
- case
385
- when @reflection.options[:as]
386
- @finder_sql =
387
- "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
388
- "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
389
- else
390
- @finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, owner_quoted_id)
391
- end
392
-
393
- @finder_sql << " AND (#{conditions})" if conditions
394
- end
395
- end
396
-
397
- class HasManyThroughAssociation < HasManyAssociation #:nodoc:
398
- def construct_conditions_with_composite_keys
399
- if @reflection.through_reflection.options[:as]
400
- construct_conditions_without_composite_keys
401
- else
402
- conditions = full_columns_equals(@reflection.through_reflection.table_name, @reflection.through_reflection.primary_key_name, @owner.quoted_id)
403
- conditions << " AND (#{sql_conditions})" if sql_conditions
404
- conditions
405
- end
406
- end
407
- alias_method_chain :construct_conditions, :composite_keys
115
+ [through_reflection, reflection].each do |ref|
116
+ if ref && ref.options[:conditions]
117
+ @join << interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))
118
+ end
119
+ end
408
120
 
409
- def construct_joins_with_composite_keys(custom_joins = nil)
410
- if @reflection.through_reflection.options[:as] || @reflection.source_reflection.options[:as]
411
- construct_joins_without_composite_keys(custom_joins)
412
- else
413
- if @reflection.source_reflection.macro == :belongs_to
414
- reflection_primary_key = @reflection.klass.primary_key
415
- source_primary_key = @reflection.source_reflection.primary_key_name
416
- else
417
- reflection_primary_key = @reflection.source_reflection.primary_key_name
418
- source_primary_key = @reflection.klass.primary_key
121
+ @join
122
+ end
419
123
  end
420
-
421
- "INNER JOIN %s ON %s #{@reflection.options[:joins]} #{custom_joins}" % [
422
- @reflection.through_reflection.quoted_table_name,
423
- composite_join_clause(full_keys(@reflection.table_name, reflection_primary_key), full_keys(@reflection.through_reflection.table_name, source_primary_key))
424
- ]
425
124
  end
426
125
  end
427
- alias_method_chain :construct_joins, :composite_keys
428
126
  end
429
- end
127
+ end