ackbar 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ ###############################################################################
2
+ # DB Schema Migration for testing ActiveKirby
3
+ require 'active_record/migration'
4
+
5
+ class SchemaMigrationTest < ActiveRecord::Migration
6
+ def self.up
7
+ create_table "pages", :force => true do |t|
8
+ t.column "book_id", :integer
9
+ t.column "page_num", :integer
10
+ t.column "content", :text
11
+ end
12
+
13
+ remove_index(:delete_me_in_migration, :junk)
14
+ rename_column(:delete_me_in_migration, :more_junk, :less_junk)
15
+ change_column(:delete_me_in_migration, :junk, :string)
16
+ remove_column(:delete_me_in_migration, :junk_yard)
17
+
18
+ drop_table(:delete_me_in_migration)
19
+
20
+ add_column(:publishers, :address, :string)
21
+ add_index(:publishers, :name, :unique)
22
+
23
+ # test belong_to (book) and has_one (book)
24
+ create_table 'errata', :force => true do |t|
25
+ t.column 'book_id', :integer
26
+ t.column 'contents', :string
27
+ end
28
+ end
29
+
30
+ def self.down
31
+ end
32
+ end
@@ -0,0 +1,415 @@
1
+ require 'test_helper'
2
+
3
+ @@AR_PATH = Gem.latest_load_paths.grep(/activerecord/)[0]
4
+ @@AR_TESTS_PATH = File.expand_path(File.join(@@AR_PATH, '../test'))
5
+ $LOAD_PATH << @@AR_TESTS_PATH
6
+
7
+ ################################################################################
8
+ ### Start requiring ActiveRecords test files ###################################
9
+ # There are 663 tests, 2188 assertions in the AR test suite (sqlite)
10
+
11
+ ################################################################################
12
+ # ActiveRecord::Base basics tests
13
+ require File.join(@@AR_TESTS_PATH, 'base_test.rb')
14
+ class BasicsTest
15
+ # too much SQL
16
+ remove_method :test_count_with_join
17
+ end
18
+
19
+ ################################################################################
20
+ # ActiveRecord::Associations tests
21
+ require File.join(@@AR_TESTS_PATH, 'associations_test.rb')
22
+
23
+ class AssociationsTest
24
+ # We use code blocks (procs) which can't be dumped with Marshal
25
+ remove_method :test_storing_in_pstore
26
+ end
27
+
28
+ class HasAndBelongsToManyAssociationsTest
29
+ # Joins not supported
30
+ remove_method :test_adding_uses_default_values_on_join_table
31
+ remove_method :test_adding_uses_explicit_values_on_join_table
32
+ remove_method :test_additional_columns_from_join_table
33
+ end
34
+
35
+ class HasManyAssociationsTest
36
+ # :group option (GROUP BY statement) not supported
37
+ remove_method :test_find_grouped
38
+ # transactions not supported
39
+ remove_method :test_dependence_with_transaction_support_on_failure
40
+ end
41
+
42
+ class HasAndBelongsToManyAssociationsTest
43
+ # just changes the last select count to work through KB select blocks
44
+ def test_update_attributes_after_push_without_duplicate_join_table_rows
45
+ developer = Developer.new("name" => "Kano")
46
+ project = SpecialProject.create("name" => "Special Project")
47
+ assert developer.save
48
+ developer.projects << project
49
+ developer.update_attribute("name", "Bruza")
50
+ # assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
51
+ # SELECT count(*) FROM developers_projects
52
+ # WHERE project_id = #{project.id}
53
+ # AND developer_id = #{developer.id}
54
+ # end_sql
55
+ num_rows = Developer.connection.db.get_table(:developers_projects).select do |rec|
56
+ rec.project_id == project.id and rec.developer_id == developer.id
57
+ end.size
58
+ assert_equal 1, num_rows
59
+ end
60
+
61
+ def test_removing_associations_on_destroy
62
+ david = DeveloperWithBeforeDestroyRaise.find(1)
63
+ assert !david.projects.empty?
64
+ assert_nothing_raised { david.destroy }
65
+ assert david.projects.empty?
66
+ assert DeveloperWithBeforeDestroyRaise.connection.db.get_table(:developers_projects).select{|rec| rec.developer_id == 1}.empty?
67
+ end
68
+
69
+ end
70
+
71
+ require File.join(@@AR_TESTS_PATH, 'associations_extensions_test.rb')
72
+
73
+ class AssociationsExtensionsTest
74
+ # Procs can't be marshalled
75
+ remove_method :test_marshalling_extensions
76
+ remove_method :test_marshalling_named_extensions
77
+ end
78
+
79
+ require File.join(@@AR_TESTS_PATH, 'deprecated_associations_test.rb')
80
+
81
+ class DeprecatedAssociationsTest
82
+ remove_method :test_has_many_dependence_with_transaction_support_on_failure
83
+ remove_method :test_storing_in_pstore
84
+ end
85
+
86
+ ################################################################################
87
+ # Finder tests
88
+ require File.join(@@AR_TESTS_PATH, 'finder_test.rb')
89
+
90
+ class FinderTest
91
+ # we don't allow full SQL, but might as well check the block format
92
+ def test_count_by_sql
93
+ assert_raises(ActiveRecord::StatementInvalid) { Entrant.count_by_sql("SELECT COUNT(*) FROM entrant") }
94
+ assert_equal 0, Entrant.count(lambda{|rec| rec.recno > 3})
95
+ # this is just too wierd: assert_equal 1, Entrant.count([lambda{|rec| rec.recno > 2}])
96
+ assert_equal 2, Entrant.count{|rec| rec.recno > 1}
97
+ end
98
+ remove_method :test_find_with_entire_select_statement
99
+ remove_method :test_find_with_prepared_select_statement
100
+ remove_method :test_select_value
101
+ remove_method :test_select_values
102
+ remove_method :test_find_all_with_join
103
+ end
104
+
105
+ require File.join(@@AR_TESTS_PATH, 'deprecated_finder_test.rb')
106
+
107
+ class DeprecatedFinderTest
108
+ remove_method :test_count_by_sql
109
+ end
110
+
111
+ ################################################################################
112
+ # Schema & Migrations tests
113
+
114
+ require File.join(@@AR_TESTS_PATH, 'ar_schema_test.rb')
115
+
116
+ class ActiveRecordSchemaTest
117
+ def test_schema_define
118
+ ActiveRecord::Schema.define(:version => 7) do
119
+ create_table :fruits do |t|
120
+ t.column :color, :string
121
+ t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle
122
+ t.column :texture, :string
123
+ t.column :flavor, :string
124
+ end
125
+ end
126
+
127
+ assert_nothing_raised { @connection.get_table(:fruits).select }
128
+ assert_nothing_raised { @connection.get_table(:schema_info).select }
129
+ assert_equal 7, @connection.get_table(:schema_info).select[0].version
130
+ end
131
+ end
132
+
133
+ require File.join(@@AR_TESTS_PATH, 'migration_test.rb')
134
+
135
+ class MigrationTest
136
+ def teardown
137
+ ActiveRecord::Base.connection.initialize_schema_information
138
+ ActiveRecord::Base.connection.get_table(:schema_info).update_all {|rec| rec.version = 0}
139
+
140
+ Reminder.connection.drop_table("reminders") rescue nil
141
+ Reminder.connection.drop_table("people_reminders") rescue nil
142
+ Reminder.connection.drop_table("prefix_reminders_suffix") rescue nil
143
+ Reminder.reset_column_information
144
+
145
+ Person.connection.remove_column("people", "last_name") rescue nil
146
+ Person.connection.remove_column("people", "bio") rescue nil
147
+ Person.connection.remove_column("people", "age") rescue nil
148
+ Person.connection.remove_column("people", "height") rescue nil
149
+ Person.connection.remove_column("people", "birthday") rescue nil
150
+ Person.connection.remove_column("people", "favorite_day") rescue nil
151
+ Person.connection.remove_column("people", "male") rescue nil
152
+ Person.connection.remove_column("people", "administrator") rescue nil
153
+ Person.reset_column_information
154
+ end
155
+
156
+ def test_create_table_with_not_null_column
157
+ Person.connection.create_table :testings do |t|
158
+ t.column :foo, :string, :null => false
159
+ end
160
+
161
+ # ArgumentError and not ActiveRecord::StatementInvalid because we're inserting directly to the db.
162
+ # Still showns that this field is required
163
+ assert_raises(ArgumentError) do
164
+ Person.connection.get_table(:testings).insert :foo => nil
165
+ end
166
+ ensure
167
+ Person.connection.drop_table :testings rescue nil
168
+ end
169
+
170
+ def test_add_column_not_null_with_default
171
+ Person.connection.create_table :testings do |t|
172
+ t.column :foo, :string
173
+ end
174
+ Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default"
175
+
176
+ # changed from ActiveRecord::StatementInvalid as we're operating directly on
177
+ # the database, and that is what KB spits out. Still, it validates that the
178
+ # field is now required (not null)
179
+ assert_raises(ArgumentError) do
180
+ Person.connection.get_table(:testings).insert :foo => 'hello', :bar => nil
181
+ end
182
+ ensure
183
+ Person.connection.drop_table :testings rescue nil
184
+ end
185
+
186
+ def test_add_column_not_null_without_default
187
+ Person.connection.create_table :testings do |t|
188
+ t.column :foo, :string
189
+ end
190
+ Person.connection.add_column :testings, :bar, :string, :null => false
191
+
192
+ assert_raises(ArgumentError) do
193
+ Person.connection.get_table(:testings).insert :foo => 'hello', :bar => nil
194
+ end
195
+ ensure
196
+ Person.connection.drop_table :testings rescue nil
197
+ end
198
+
199
+ # KirbyBase only supports one index per column, so created new column
200
+ # (middle_name) for those tests. Also, KirbyBase does not support named
201
+ # indexes, so those tests were disabled.
202
+ def test_add_index
203
+ Person.connection.add_column "people", "last_name", :string
204
+ Person.connection.add_column "people", "middle_name", :string
205
+ Person.connection.add_column "people", "administrator", :boolean
206
+
207
+ assert_nothing_raised { Person.connection.add_index("people", "last_name") }
208
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
209
+
210
+ assert_nothing_raised { Person.connection.add_index("people", ["middle_name", "first_name"]) }
211
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
212
+
213
+ # assert_nothing_raised { Person.connection.add_index("people", %w(last_name middle_name administrator), :name => "named_admin") }
214
+ # assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
215
+ end
216
+
217
+ def test_rename_table
218
+ begin
219
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
220
+ t.column :url, :string
221
+ end
222
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
223
+
224
+ assert_nothing_raised do
225
+ ActiveRecord::Base.connection.get_table(:octopi).insert :url => 'http://www.foreverflying.com/octopus-black7.jpg'
226
+ end
227
+
228
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg',
229
+ ActiveRecord::Base.connection.get_table(:octopi).select{|r|r.recno == 1}.first.url
230
+
231
+ ensure
232
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
233
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
234
+ end
235
+ end
236
+
237
+ # Since this test uses aboolean field with a default, we need to override the
238
+ # change_column statement to use FalseClass rather than 0.
239
+ # In real life migrations, this should be guarded with an if current_adapter ...
240
+ def test_change_column_with_new_default
241
+ Person.connection.add_column "people", "administrator", :boolean, :default => true
242
+ Person.reset_column_information
243
+ assert Person.new.administrator?
244
+
245
+ assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
246
+ Person.reset_column_information
247
+ assert !Person.new.administrator?
248
+ end
249
+ end
250
+
251
+ ################################################################################
252
+ # Misc tests
253
+
254
+ require File.join(@@AR_TESTS_PATH, 'inheritance_test.rb')
255
+
256
+ class InheritanceTest
257
+ # not supporting non-integer primary keys just yet
258
+ remove_method :test_inheritance_without_mapping
259
+
260
+ def test_a_bad_type_column
261
+ recno = Company.table.insert :mame => 'bad_class!', :type => 'Not happening'
262
+ assert_raises(ActiveRecord::SubclassNotFound) { Company.find(recno) }
263
+ end
264
+ end
265
+
266
+ require File.join(@@AR_TESTS_PATH, 'method_scoping_test.rb')
267
+
268
+ class MethodScopingTest
269
+ # changed the LIKE clause to ruby block
270
+ def test_scoped_count
271
+ Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
272
+ assert_equal 1, Developer.count
273
+ end
274
+
275
+ Developer.with_scope(:find => { :conditions => 'salary = 100000' }) do
276
+ assert_equal 8, Developer.count
277
+ # assert_equal 1, Developer.count("name LIKE 'fixture_1%'")
278
+ assert_equal 1, Developer.count(lambda{|rec| rec.name =~ /fixture_1.*/})
279
+ end
280
+ end
281
+ end
282
+
283
+ class HasAndBelongsToManyScopingTest
284
+ # we don't use the nested scopes
285
+ remove_method :test_raise_on_nested_scope
286
+ end
287
+
288
+ require File.join(@@AR_TESTS_PATH, 'pk_test.rb')
289
+
290
+ # Strings not supported for primary keys
291
+ class PrimaryKeysTest
292
+ remove_method :test_string_key
293
+ remove_method :test_find_with_more_than_one_string_key
294
+ end
295
+
296
+ require File.join(@@AR_TESTS_PATH, 'reflection_test.rb')
297
+
298
+ # We don't keep limits on Strings (and others). Using plain Ruby types.
299
+ class ReflectionTest
300
+ remove_method :test_column_string_type_and_limit
301
+ end
302
+
303
+
304
+ ################################################################################
305
+ # Require all other test files not specifically handled above
306
+
307
+ ar_test_files = Dir[@@AR_TESTS_PATH + '/*_test.rb']
308
+ # Remove things we don't support
309
+ %w{
310
+ aaa_create_tables_test.rb
311
+ transactions_test.rb
312
+ associations_go_eager_test.rb
313
+ mixin_test.rb
314
+ mixin_nested_set_test.rb
315
+ }.each {|test_file| ar_test_files.delete File.join(@@AR_TESTS_PATH, test_file)}
316
+ ar_test_files.each {|test_file| require test_file}
317
+
318
+ class BinaryTest
319
+ def setup
320
+ Binary.table.clear
321
+ @data = File.read(BINARY_FIXTURE_PATH).freeze
322
+ end
323
+ end
324
+
325
+ class FixturesTest
326
+ def test_inserts
327
+ topics = create_fixtures("topics")
328
+ # firstRow = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'David'")
329
+ firstRow = ActiveRecord::Base.connection.db.get_table(:topics).select{|rec| rec.author_name == 'David'}.first
330
+ assert_equal("The First Topic", firstRow["title"])
331
+
332
+ # secondRow = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'Mary'")
333
+ secondRow = ActiveRecord::Base.connection.db.get_table(:topics).select{|rec| rec.author_name == 'Mary'}.first
334
+ assert_nil(secondRow["author_email_address"])
335
+ end
336
+
337
+ def test_inserts_with_pre_and_suffix
338
+ ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
339
+ t.column :title, :string
340
+ t.column :author_name, :string
341
+ t.column :author_email_address, :string
342
+ t.column :written_on, :datetime
343
+ t.column :bonus_time, :time
344
+ t.column :last_read, :date
345
+ t.column :content, :text
346
+ t.column :approved, :boolean, :default => 1
347
+ t.column :replies_count, :integer, :default => 0
348
+ t.column :parent_id, :integer
349
+ t.column :type, :string, :limit => 50
350
+ end
351
+
352
+ # Store existing prefix/suffix
353
+ old_prefix = ActiveRecord::Base.table_name_prefix
354
+ old_suffix = ActiveRecord::Base.table_name_suffix
355
+
356
+ # Set a prefix/suffix we can test against
357
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
358
+ ActiveRecord::Base.table_name_suffix = '_suffix'
359
+
360
+ topics = create_fixtures("topics")
361
+
362
+ # Restore prefix/suffix to its previous values
363
+ ActiveRecord::Base.table_name_prefix = old_prefix
364
+ ActiveRecord::Base.table_name_suffix = old_suffix
365
+
366
+ # firstRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
367
+ firstRow = ActiveRecord::Base.connection.db.get_table(:prefix_topics_suffix).select{|rec| rec.author_name == 'David'}.first
368
+ assert_equal("The First Topic", firstRow["title"])
369
+
370
+ # secondRow = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
371
+ secondRow = ActiveRecord::Base.connection.db.get_table(:prefix_topics_suffix).select{|rec| rec.author_name == 'Mary'}.first
372
+ assert_nil(secondRow["author_email_address"])
373
+ ensure
374
+ ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
375
+ end
376
+ end
377
+
378
+ # Too SQL specific
379
+ class TestColumnAlias
380
+ # can't remove_method, because it's the only one in the textcase
381
+ def test_column_alias() end
382
+ end
383
+
384
+ ################################################################################
385
+ # Introduce my adaptation of the model classes (override SQL with blocks)
386
+ require 'ar_model_adaptation'
387
+
388
+ # schema_test and schema_dumper_test require from relative URLs, which means they
389
+ # override my changes. So the changes are reintroduced here.
390
+ module ActiveRecord #:nodoc:
391
+ class Schema
392
+ def self.define(info={}, &block)
393
+ instance_eval(&block)
394
+
395
+ unless info.empty?
396
+ initialize_schema_information
397
+ ActiveRecord::Base.connection.get_table(ActiveRecord::Migrator.schema_info_table_name.to_sym).update_all(info)
398
+ end
399
+ end
400
+ end
401
+
402
+ class SchemaDumper
403
+ def initialize(connection)
404
+ @connection = connection
405
+ @types = @connection.native_database_types
406
+ @info = @connection.get_table(:schema_info).select[0] rescue nil
407
+ end
408
+ end
409
+ end
410
+
411
+ # This is required as KirbyBase does not support transactions.
412
+ ObjectSpace.each_object(Class) do |test|
413
+ test.use_transactional_fixtures = false if test < Test::Unit::TestCase
414
+ end
415
+
@@ -0,0 +1,98 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ module ClassMethods
4
+ def remove_association *names
5
+ if names.length == 1
6
+ assocs = @inheritable_attributes[:associations].select{ |a| a.name == names[0] }
7
+ assocs.each {|assoc| @inheritable_attributes[:associations].delete assoc }
8
+ else
9
+ names.each {|assoc| remove_association(assoc)}
10
+ end
11
+ end
12
+ def has_and_belongs_to_many_without_method_redefinition(association_id, options = {}, &extension)
13
+ options.assert_valid_keys(
14
+ :class_name, :table_name, :foreign_key, :association_foreign_key, :conditions, :include,
15
+ :join_table, :finder_sql, :delete_sql, :insert_sql, :order, :uniq, :before_add, :after_add,
16
+ :before_remove, :after_remove, :extend
17
+ )
18
+
19
+ options[:extend] = create_extension_module(association_id, extension) if block_given?
20
+
21
+ association_name, association_class_name, association_class_primary_key_name =
22
+ associate_identification(association_id, options[:class_name], options[:foreign_key])
23
+
24
+ require_association_class(association_class_name)
25
+
26
+ options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(association_class_name))
27
+
28
+ add_multiple_associated_save_callbacks(association_name)
29
+
30
+ collection_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasAndBelongsToManyAssociation)
31
+
32
+ add_association_callbacks(association_name, options)
33
+
34
+ # deprecated api
35
+ deprecated_collection_count_method(association_name)
36
+ deprecated_add_association_relation(association_name)
37
+ deprecated_remove_association_relation(association_name)
38
+ deprecated_has_collection_method(association_name)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ class Company < ActiveRecord::Base
45
+ set_sequence_name nil
46
+ end
47
+
48
+ class Firm
49
+ remove_association :clients, :clients_of_firm, :clients_using_sql, :clients_using_counter_sql,
50
+ :clients_using_zero_counter_sql, :no_clients_using_counter_sql
51
+
52
+ has_many :clients, :order => "id", :dependent => true, :counter_sql =>
53
+ lambda {|rec, firm| rec.firm_id == 1 and ['Client', 'SpecialClient', 'VerySpecialClient'].include?(rec.type) }
54
+ # "SELECT COUNT(*) FROM companies WHERE firm_id = 1 " +
55
+ # "AND (#{QUOTED_TYPE} = 'Client' OR #{QUOTED_TYPE} = 'SpecialClient' OR #{QUOTED_TYPE} = 'VerySpecialClient' )"
56
+
57
+ has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
58
+ has_many :clients_using_sql, :class_name => "Client",
59
+ :finder_sql => lambda{|rec, firm| rec.client_of == firm.id} # 'SELECT * FROM companies WHERE client_of = #{id}'
60
+ has_many :clients_using_counter_sql, :class_name => "Client",
61
+ :finder_sql => lambda{|rec, firm| rec.client_of == firm.id}, # 'SELECT * FROM companies WHERE client_of = #{id}',
62
+ :counter_sql => lambda{|rec, firm| rec.client_of == firm.id} # 'SELECT COUNT(*) FROM companies WHERE client_of = #{id}'
63
+ has_many :clients_using_zero_counter_sql, :class_name => "Client",
64
+ :finder_sql => lambda{|rec, firm| rec.client_of == id}, # 'SELECT * FROM companies WHERE client_of = #{id}'
65
+ :counter_sql => lambda{|rec, firm| false } # 'SELECT 0 FROM companies WHERE client_of = #{id}'
66
+ has_many :no_clients_using_counter_sql, :class_name => "Client",
67
+ :finder_sql => lambda{|rec, firm| rec.client_of == 1000}, # 'SELECT * FROM companies WHERE client_of = 1000'
68
+ :counter_sql => lambda{|rec, firm| rec.client_of == 1000} # 'SELECT COUNT(*) FROM companies WHERE client_of = 1000'
69
+ end
70
+
71
+ class Client < Company
72
+ belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => "1 = 1"
73
+ end
74
+
75
+ # module MyApplication
76
+ # module Business
77
+ # class Firm
78
+ # has_many :clients_using_sql, :class_name => "Client",
79
+ # :finder_sql => lambda{|rec, firm| rec.client_of == firm.id} # 'SELECT * FROM companies WHERE client_of = #{id}'
80
+ # end
81
+ # end
82
+ # end
83
+
84
+ class Project
85
+ remove_association :developers_with_finder_sql, :developers_by_sql
86
+
87
+ ## Unfortunately calling habtm with the same name again will cause some method
88
+ # redefinition loop (see associations.rb for the dynamic method redefinition).
89
+ # So we have to go through loops to redefine the exact methods.
90
+ has_and_belongs_to_many_without_method_redefinition :developers_with_finder_sql, :class_name => "Developer",
91
+ # :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id}'
92
+ :finder_sql => lambda{|join, project| join.project_id == project.id } # this is run on the join table
93
+
94
+ has_and_belongs_to_many_without_method_redefinition :developers_by_sql, :class_name => "Developer",
95
+ # :delete_sql => "DELETE FROM developers_projects WHERE project_id = \#{id} AND developer_id = \#{record.id}"
96
+ :delete_sql => lambda{|join, project, developer| join.project_id == project.id and join.developer_id == developer.id}
97
+ end
98
+