activerecord 1.9.1 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (68) hide show
  1. data/CHANGELOG +78 -0
  2. data/README +1 -1
  3. data/install.rb +7 -42
  4. data/lib/active_record.rb +2 -0
  5. data/lib/active_record/acts/list.rb +28 -4
  6. data/lib/active_record/acts/nested_set.rb +212 -0
  7. data/lib/active_record/associations.rb +203 -21
  8. data/lib/active_record/associations/association_proxy.rb +10 -2
  9. data/lib/active_record/associations/belongs_to_association.rb +0 -1
  10. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -9
  11. data/lib/active_record/associations/has_many_association.rb +25 -25
  12. data/lib/active_record/associations/has_one_association.rb +2 -2
  13. data/lib/active_record/base.rb +134 -110
  14. data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -9
  15. data/lib/active_record/connection_adapters/mysql_adapter.rb +4 -0
  16. data/lib/active_record/connection_adapters/oci_adapter.rb +2 -2
  17. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -2
  18. data/lib/active_record/deprecated_associations.rb +1 -19
  19. data/lib/active_record/deprecated_finders.rb +41 -0
  20. data/lib/active_record/fixtures.rb +24 -11
  21. data/lib/active_record/observer.rb +17 -11
  22. data/lib/active_record/reflection.rb +5 -1
  23. data/lib/active_record/transactions.rb +7 -0
  24. data/lib/active_record/validations.rb +32 -33
  25. data/rakefile +30 -6
  26. data/test/associations_go_eager_test.rb +55 -0
  27. data/test/associations_test.rb +72 -15
  28. data/test/base_test.rb +15 -21
  29. data/test/deprecated_associations_test.rb +0 -24
  30. data/test/deprecated_finder_test.rb +147 -0
  31. data/test/finder_test.rb +37 -37
  32. data/test/fixtures/author.rb +3 -0
  33. data/test/fixtures/authors.yml +7 -0
  34. data/test/fixtures/categories.yml +7 -0
  35. data/test/fixtures/categories_posts.yml +11 -0
  36. data/test/fixtures/category.rb +3 -0
  37. data/test/fixtures/comment.rb +5 -0
  38. data/test/fixtures/comments.yml +17 -0
  39. data/test/fixtures/company.rb +3 -0
  40. data/test/fixtures/courses.yml +4 -4
  41. data/test/fixtures/db_definitions/db2.drop.sql +6 -0
  42. data/test/fixtures/db_definitions/db2.sql +46 -0
  43. data/test/fixtures/db_definitions/mysql.drop.sql +6 -1
  44. data/test/fixtures/db_definitions/mysql.sql +60 -12
  45. data/test/fixtures/db_definitions/mysql2.sql +1 -1
  46. data/test/fixtures/db_definitions/oci.drop.sql +5 -0
  47. data/test/fixtures/db_definitions/oci.sql +45 -0
  48. data/test/fixtures/db_definitions/postgresql.drop.sql +6 -0
  49. data/test/fixtures/db_definitions/postgresql.sql +45 -0
  50. data/test/fixtures/db_definitions/sqlite.drop.sql +6 -1
  51. data/test/fixtures/db_definitions/sqlite.sql +46 -0
  52. data/test/fixtures/db_definitions/sqlserver.drop.sql +7 -1
  53. data/test/fixtures/db_definitions/sqlserver.sql +46 -0
  54. data/test/fixtures/fk_test_has_fk.yml +3 -0
  55. data/test/fixtures/fk_test_has_pk.yml +2 -0
  56. data/test/fixtures/mixin.rb +18 -0
  57. data/test/fixtures/mixins.yml +30 -0
  58. data/test/fixtures/post.rb +8 -0
  59. data/test/fixtures/posts.yml +20 -0
  60. data/test/fixtures/task.rb +3 -0
  61. data/test/fixtures/tasks.yml +7 -0
  62. data/test/fixtures_test.rb +34 -2
  63. data/test/mixin_nested_set_test.rb +184 -0
  64. data/test/mixin_test.rb +28 -3
  65. data/test/validations_test.rb +16 -0
  66. metadata +21 -5
  67. data/test/fixtures/db_definitions/drop_oracle_tables.sql +0 -35
  68. data/test/fixtures/db_definitions/drop_oracle_tables2.sql +0 -3
data/rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
8
8
 
9
9
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
10
  PKG_NAME = 'activerecord'
11
- PKG_VERSION = '1.9.1' + PKG_BUILD
11
+ PKG_VERSION = '1.10.0' + PKG_BUILD
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  RELEASE_NAME = "REL #{PKG_VERSION}"
@@ -38,7 +38,7 @@ Rake::TestTask.new("test_mysql_ruby") { |t|
38
38
  t.verbose = true
39
39
  }
40
40
 
41
- for adapter in %( postgresql sqlite sqlite3 sqlserver db2 oci )
41
+ for adapter in %w( postgresql sqlite sqlite3 sqlserver db2 oci )
42
42
  Rake::TestTask.new("test_#{adapter}") { |t|
43
43
  t.libs << "test" << "test/connections/native_#{adapter}"
44
44
  t.pattern = 'test/*_test.rb'
@@ -76,7 +76,7 @@ spec = Gem::Specification.new do |s|
76
76
  s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
77
77
  end
78
78
 
79
- s.add_dependency('activesupport', '= 1.0.3' + PKG_BUILD)
79
+ s.add_dependency('activesupport', '= 1.0.4' + PKG_BUILD)
80
80
 
81
81
  s.files.delete "test/fixtures/fixture_database.sqlite"
82
82
  s.files.delete "test/fixtures/fixture_database_2.sqlite"
@@ -101,18 +101,42 @@ Rake::GemPackageTask.new(spec) do |p|
101
101
  p.need_zip = true
102
102
  end
103
103
 
104
+ task :lines do
105
+ lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
106
+
107
+ for file_name in FileList["lib/active_record/**/*.rb"]
108
+ next if file_name =~ /vendor/
109
+ f = File.open(file_name)
110
+
111
+ while line = f.gets
112
+ lines += 1
113
+ next if line =~ /^\s*$/
114
+ next if line =~ /^\s*#/
115
+ codelines += 1
116
+ end
117
+ puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
118
+
119
+ total_lines += lines
120
+ total_codelines += codelines
121
+
122
+ lines, codelines = 0, 0
123
+ end
124
+
125
+ puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
126
+ end
127
+
104
128
 
105
129
  # Publishing ------------------------------------------------------
106
130
 
107
131
  desc "Publish the beta gem"
108
132
  task :pgem => [:package] do
109
- Rake::SshFilePublisher.new("davidhh@comox.textdrive.com", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
110
- `ssh davidhh@comox.textdrive.com './gemupdate.sh'`
133
+ Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.com", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
134
+ `ssh davidhh@wrath.rubyonrails.com './gemupdate.sh'`
111
135
  end
112
136
 
113
137
  desc "Publish the API documentation"
114
138
  task :pdoc => [:rdoc] do
115
- Rake::SshDirPublisher.new("davidhh@comox.textdrive.com", "public_html/ar", "doc").upload
139
+ Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.com", "public_html/ar", "doc").upload
116
140
  end
117
141
 
118
142
  desc "Publish the release files to RubyForge."
@@ -0,0 +1,55 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/post'
3
+ require 'fixtures/comment'
4
+ require 'fixtures/author'
5
+ require 'fixtures/category'
6
+
7
+ class EagerAssociationTest < Test::Unit::TestCase
8
+ fixtures :posts, :comments, :authors, :categories, :categories_posts
9
+
10
+ def test_loading_with_one_association
11
+ posts = Post.find(:all, :include => :comments)
12
+ assert_equal 2, posts.first.comments.size
13
+ assert posts.first.comments.include?(@greetings)
14
+
15
+ post = Post.find(:first, :include => :comments, :conditions => "posts.title = 'Welcome to the weblog'")
16
+ assert_equal 2, post.comments.size
17
+ assert post.comments.include?(@greetings)
18
+ end
19
+
20
+ def test_loading_with_multiple_associations
21
+ posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
22
+ assert_equal 2, posts.first.comments.size
23
+ assert_equal 2, posts.first.categories.size
24
+ assert posts.first.comments.include?(@greetings)
25
+ end
26
+
27
+ def test_loading_from_an_association
28
+ posts = @david.posts.find(:all, :include => :comments)
29
+ assert_equal 2, posts.first.comments.size
30
+ end
31
+
32
+ def test_loading_with_no_associations
33
+ assert_nil Post.find(@authorless.id, :include => :author).author
34
+ end
35
+
36
+ def test_eager_association_loading_with_belongs_to
37
+ comments = Comment.find(:all, :include => :post)
38
+ assert_equal @welcome.title, comments.first.post.title
39
+ assert_equal @thinking.title, comments.last.post.title
40
+ end
41
+
42
+ def test_eager_association_loading_with_habtm
43
+ posts = Post.find(:all, :include => :categories, :order => "posts.id")
44
+ assert_equal 2, posts[0].categories.size
45
+ assert_equal 1, posts[1].categories.size
46
+ assert_equal 0, posts[2].categories.size
47
+ assert posts[0].categories.include?(@technology)
48
+ assert posts[1].categories.include?(@general)
49
+ end
50
+
51
+ def test_eager_with_inheritance
52
+ posts = SpecialPost.find(:all, :include => [ :comments ])
53
+ end
54
+ end
55
+
@@ -105,6 +105,30 @@ class HasOneAssociationsTest < Test::Unit::TestCase
105
105
  assert_equal 1, Account.find_all.length
106
106
  end
107
107
 
108
+ def test_succesful_build_association
109
+ firm = Firm.new("name" => "GlobalMegaCorp")
110
+ firm.save
111
+
112
+ account = firm.build_account("credit_limit" => 1000)
113
+ assert account.save
114
+ assert_equal account, firm.account
115
+ end
116
+
117
+ def test_failing_build_association
118
+ firm = Firm.new("name" => "GlobalMegaCorp")
119
+ firm.save
120
+
121
+ account = firm.build_account
122
+ assert !account.save
123
+ assert_equal "can't be empty", account.errors.on("credit_limit")
124
+ end
125
+
126
+ def test_create_association
127
+ firm = Firm.new("name" => "GlobalMegaCorp")
128
+ firm.save
129
+ assert_equal firm.create_account("credit_limit" => 1000), firm.account
130
+ end
131
+
108
132
  def test_build
109
133
  firm = Firm.new("name" => "GlobalMegaCorp")
110
134
  firm.save
@@ -203,8 +227,9 @@ end
203
227
 
204
228
 
205
229
  class HasManyAssociationsTest < Test::Unit::TestCase
230
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects, :topics
231
+
206
232
  def setup
207
- create_fixtures "accounts", "companies", "developers", "projects", "developers_projects", "topics"
208
233
  @signals37 = Firm.find(1)
209
234
  end
210
235
 
@@ -249,6 +274,10 @@ class HasManyAssociationsTest < Test::Unit::TestCase
249
274
  assert_equal 1, Firm.find_first.clients_using_counter_sql.size
250
275
  assert_equal 0, Firm.find_first.clients_using_zero_counter_sql.size
251
276
  end
277
+
278
+ def test_counting_non_existant_items_using_sql
279
+ assert_equal 0, Firm.find_first.no_clients_using_counter_sql.size
280
+ end
252
281
 
253
282
  def test_belongs_to_sanity
254
283
  c = Client.new
@@ -288,8 +317,8 @@ class HasManyAssociationsTest < Test::Unit::TestCase
288
317
  def test_find_all
289
318
  firm = Firm.find_first
290
319
  assert_equal firm.clients, firm.clients.find_all
291
- assert_equal 2, firm.clients.find_all("type = 'Client'").length
292
- assert_equal 1, firm.clients.find_all("name = 'Summit'").length
320
+ assert_equal 2, firm.clients.find(:all, :conditions => "type = 'Client'").length
321
+ assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
293
322
  end
294
323
 
295
324
  def test_find_all_sanitized
@@ -481,9 +510,21 @@ class HasManyAssociationsTest < Test::Unit::TestCase
481
510
  end
482
511
 
483
512
  def test_dependence
484
- assert_equal 2, Client.find_all.length
513
+ assert_equal 2, Client.find_all.size
485
514
  Firm.find_first.destroy
486
- assert_equal 0, Client.find_all.length
515
+ assert Client.find_all.empty?
516
+ end
517
+
518
+ def test_destroy_dependent_when_deleted_from_association
519
+ firm = Firm.find_first
520
+ assert_equal 2, firm.clients.size
521
+
522
+ client = firm.clients.first
523
+ firm.clients.delete(client)
524
+
525
+ assert_raise(ActiveRecord::RecordNotFound) { Client.find(client.id) }
526
+ assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(client.id) }
527
+ assert_equal 1, firm.clients.size
487
528
  end
488
529
 
489
530
  def test_three_levels_of_dependence
@@ -546,6 +587,19 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
546
587
  assert_equal apple.id, citibank.firm_id
547
588
  end
548
589
 
590
+ def test_creating_the_belonging_object
591
+ citibank = Account.create("credit_limit" => 10)
592
+ apple = citibank.create_firm("name" => "Apple")
593
+ assert_equal apple, citibank.firm
594
+ end
595
+
596
+ def test_building_the_belonging_object
597
+ citibank = Account.create("credit_limit" => 10)
598
+ apple = citibank.build_firm("name" => "Apple")
599
+ citibank.save
600
+ assert_equal apple.id, citibank.firm_id
601
+ end
602
+
549
603
  def test_natural_assignment_to_nil
550
604
  client = Client.find(3)
551
605
  client.firm = nil
@@ -615,7 +669,7 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
615
669
 
616
670
  def test_new_record_with_foreign_key_but_no_object
617
671
  c = Client.new("firm_id" => 1)
618
- assert_equal @first_firm, c.firm_with_basic_id
672
+ assert_equal Firm.find_first, c.firm_with_basic_id
619
673
  end
620
674
 
621
675
  def test_forgetting_the_load_when_foreign_key_enters_late
@@ -623,12 +677,12 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
623
677
  assert_nil c.firm_with_basic_id
624
678
 
625
679
  c.firm_id = 1
626
- assert_equal @first_firm, c.firm_with_basic_id
680
+ assert_equal Firm.find_first, c.firm_with_basic_id
627
681
  end
628
682
 
629
683
  def test_field_name_same_as_foreign_key
630
684
  computer = Computer.find 1
631
- assert_not_nil computer.developer, ":foreign key == attribute didn't lock up"
685
+ assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
632
686
  end
633
687
 
634
688
  def xtest_counter_cache
@@ -651,12 +705,7 @@ end
651
705
 
652
706
 
653
707
  class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
654
- def setup
655
- @accounts, @companies, @developers, @projects, @developers_projects =
656
- create_fixtures "accounts", "companies", "developers", "projects", "developers_projects"
657
-
658
- @signals37 = Firm.find(1)
659
- end
708
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects
660
709
 
661
710
  def test_has_and_belongs_to_many
662
711
  david = Developer.find(1)
@@ -860,4 +909,12 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
860
909
  @active_record.developers.reload
861
910
  assert_equal @developers["david"].find, @active_record.developers.find(@developers["david"]["id"]), "Ruby find"
862
911
  end
863
- end
912
+
913
+ def xtest_find_in_association_with_options
914
+ developers = @active_record.developers.find(:all)
915
+ assert_equal 2, developers.size
916
+
917
+ assert_equal @david, @active_record.developers.find(:first, :conditions => "salary < 10000")
918
+ assert_equal @jamis, @active_record.developers.find(:first, :order => "salary DESC")
919
+ end
920
+ end
@@ -103,15 +103,14 @@ class BasicsTest < Test::Unit::TestCase
103
103
  end
104
104
 
105
105
  def test_attributes_hash
106
- assert_equal @projects["action_controller"].to_hash, @action_controller.attributes
106
+ assert_equal @projects['active_record'].to_hash, Project.find_first.attributes
107
107
  end
108
-
108
+
109
109
  def test_create
110
110
  topic = Topic.new
111
111
  topic.title = "New Topic"
112
112
  topic.save
113
- id = topic.id
114
- topicReloaded = Topic.find(id)
113
+ topicReloaded = Topic.find(topic.id)
115
114
  assert_equal("New Topic", topicReloaded.title)
116
115
  end
117
116
 
@@ -139,16 +138,13 @@ class BasicsTest < Test::Unit::TestCase
139
138
  topic.title = "Another New Topic"
140
139
  topic.written_on = "2003-12-12 23:23:00"
141
140
  topic.save
142
- id = topic.id
143
- assert_equal(id, topic.id)
144
-
145
- topicReloaded = Topic.find(id)
141
+ topicReloaded = Topic.find(topic.id)
146
142
  assert_equal("Another New Topic", topicReloaded.title)
147
143
 
148
144
  topicReloaded.title = "Updated topic"
149
145
  topicReloaded.save
150
146
 
151
- topicReloadedAgain = Topic.find(id)
147
+ topicReloadedAgain = Topic.find(topic.id)
152
148
 
153
149
  assert_equal("Updated topic", topicReloadedAgain.title)
154
150
  end
@@ -157,9 +153,8 @@ class BasicsTest < Test::Unit::TestCase
157
153
  topic = Topic.new
158
154
  topic.title = "Still another topic"
159
155
  topic.save
160
- id = topic.id
161
156
 
162
- topicReloaded = Topic.find(id)
157
+ topicReloaded = Topic.find(topic.id)
163
158
  topicReloaded.title = "A New Topic"
164
159
  topicReloaded.send :write_attribute, 'does_not_exist', 'test'
165
160
  assert_nothing_raised { topicReloaded.save }
@@ -198,10 +193,9 @@ class BasicsTest < Test::Unit::TestCase
198
193
  topic.title = "Yet Another New Topic"
199
194
  topic.written_on = "2003-12-12 23:23:00"
200
195
  topic.save
201
- id = topic.id
202
196
  topic.destroy
203
-
204
- assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(id) }
197
+ assert_raise(TypeError) { topic.save }
198
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
205
199
  end
206
200
 
207
201
  def test_record_not_found_exception
@@ -353,7 +347,7 @@ class BasicsTest < Test::Unit::TestCase
353
347
  def test_attribute_keys_on_new_instance
354
348
  t = Topic.new
355
349
  assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
356
- assert_raises(NoMethodError) { t.title2 }
350
+ assert_raise(NoMethodError) { t.title2 }
357
351
  end
358
352
 
359
353
  def test_class_name
@@ -644,7 +638,7 @@ class BasicsTest < Test::Unit::TestCase
644
638
  topic = Topic.create("content" => myobj)
645
639
  Topic.serialize(:content, Hash)
646
640
 
647
- assert_raises(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
641
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
648
642
 
649
643
  settings = { "color" => "blue" }
650
644
  Topic.find(topic.id).update_attribute("content", settings)
@@ -660,19 +654,19 @@ class BasicsTest < Test::Unit::TestCase
660
654
 
661
655
  def test_class_level_destroy
662
656
  should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
663
- @first.replies << should_be_destroyed_reply
657
+ Topic.find(1).replies << should_be_destroyed_reply
664
658
 
665
659
  Topic.destroy(1)
666
- assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1) }
667
- assert_raises(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
660
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
661
+ assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
668
662
  end
669
663
 
670
664
  def test_class_level_delete
671
665
  should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
672
- @first.replies << should_be_destroyed_reply
666
+ Topic.find(1).replies << should_be_destroyed_reply
673
667
 
674
668
  Topic.delete(1)
675
- assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1) }
669
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
676
670
  assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
677
671
  end
678
672
 
@@ -174,30 +174,6 @@ class DeprecatedAssociationsTest < Test::Unit::TestCase
174
174
  assert_equal @signals37.create_in_clients_of_firm("name" => "Another Client"), @signals37.clients_of_firm(true).last
175
175
  end
176
176
 
177
- def test_succesful_build_association
178
- firm = Firm.new("name" => "GlobalMegaCorp")
179
- firm.save
180
-
181
- account = firm.build_account("credit_limit" => 1000)
182
- assert account.save
183
- assert_equal account, firm.account
184
- end
185
-
186
- def test_failing_build_association
187
- firm = Firm.new("name" => "GlobalMegaCorp")
188
- firm.save
189
-
190
- account = firm.build_account
191
- assert !account.save
192
- assert_equal "can't be empty", account.errors.on("credit_limit")
193
- end
194
-
195
- def test_create_association
196
- firm = Firm.new("name" => "GlobalMegaCorp")
197
- firm.save
198
- assert_equal firm.create_account("credit_limit" => 1000), firm.account
199
- end
200
-
201
177
  def test_has_and_belongs_to_many
202
178
  david = Developer.find(1)
203
179
  assert david.has_projects?
@@ -0,0 +1,147 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/company'
3
+ require 'fixtures/topic'
4
+ require 'fixtures/entrant'
5
+ require 'fixtures/developer'
6
+
7
+ class FinderTest < Test::Unit::TestCase
8
+ fixtures :companies, :topics, :entrants, :developers
9
+
10
+ def test_find_all_with_limit
11
+ entrants = Entrant.find_all nil, "id ASC", 2
12
+
13
+ assert_equal(2, entrants.size)
14
+ assert_equal(@entrants["first"]["name"], entrants.first.name)
15
+ end
16
+
17
+ def test_find_all_with_prepared_limit_and_offset
18
+ if ActiveRecord::ConnectionAdapters.const_defined? :OracleAdapter
19
+ if ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::OracleAdapter)
20
+ assert_raises(ArgumentError) { Entrant.find_all nil, "id ASC", [2, 1] }
21
+ end
22
+ else
23
+ entrants = Entrant.find_all nil, "id ASC", [2, 1]
24
+
25
+ assert_equal(2, entrants.size)
26
+ assert_equal(@entrants["second"]["name"], entrants.first.name)
27
+ end
28
+ end
29
+
30
+ def test_find_first
31
+ first = Topic.find_first "title = 'The First Topic'"
32
+ assert_equal(@topics["first"]["title"], first.title)
33
+ end
34
+
35
+ def test_find_first_failing
36
+ first = Topic.find_first "title = 'The First Topic!'"
37
+ assert_nil(first)
38
+ end
39
+
40
+ def test_deprecated_find_on_conditions
41
+ assert Topic.find_on_conditions(1, "approved = 0")
42
+ assert_raises(ActiveRecord::RecordNotFound) { Topic.find_on_conditions(1, "approved = 1") }
43
+ end
44
+
45
+ def test_condition_interpolation
46
+ assert_kind_of Firm, Company.find_first(["name = '%s'", "37signals"])
47
+ assert_nil Company.find_first(["name = '%s'", "37signals!"])
48
+ assert_nil Company.find_first(["name = '%s'", "37signals!' OR 1=1"])
49
+ assert_kind_of Time, Topic.find_first(["id = %d", 1]).written_on
50
+ end
51
+
52
+ def test_bind_variables
53
+ assert_kind_of Firm, Company.find_first(["name = ?", "37signals"])
54
+ assert_nil Company.find_first(["name = ?", "37signals!"])
55
+ assert_nil Company.find_first(["name = ?", "37signals!' OR 1=1"])
56
+ assert_kind_of Time, Topic.find_first(["id = ?", 1]).written_on
57
+ assert_raises(ActiveRecord::PreparedStatementInvalid) {
58
+ Company.find_first(["id=? AND name = ?", 2])
59
+ }
60
+ assert_raises(ActiveRecord::PreparedStatementInvalid) {
61
+ Company.find_first(["id=?", 2, 3, 4])
62
+ }
63
+ end
64
+
65
+ def test_bind_variables_with_quotes
66
+ Company.create("name" => "37signals' go'es agains")
67
+ assert Company.find_first(["name = ?", "37signals' go'es agains"])
68
+ end
69
+
70
+ def test_named_bind_variables_with_quotes
71
+ Company.create("name" => "37signals' go'es agains")
72
+ assert Company.find_first(["name = :name", {:name => "37signals' go'es agains"}])
73
+ end
74
+
75
+ def test_named_bind_variables
76
+ assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
77
+ assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
78
+
79
+ assert_kind_of Firm, Company.find_first(["name = :name", { :name => "37signals" }])
80
+ assert_nil Company.find_first(["name = :name", { :name => "37signals!" }])
81
+ assert_nil Company.find_first(["name = :name", { :name => "37signals!' OR 1=1" }])
82
+ assert_kind_of Time, Topic.find_first(["id = :id", { :id => 1 }]).written_on
83
+ assert_raises(ActiveRecord::PreparedStatementInvalid) {
84
+ Company.find_first(["id=:id and name=:name", { :id=>3 }])
85
+ }
86
+ assert_raises(ActiveRecord::PreparedStatementInvalid) {
87
+ Company.find_first(["id=:id", { :id=>3, :name=>"37signals!" }])
88
+ }
89
+ end
90
+
91
+ def test_count
92
+ assert_equal(0, Entrant.count("id > 3"))
93
+ assert_equal(1, Entrant.count(["id > ?", 2]))
94
+ assert_equal(2, Entrant.count(["id > ?", 1]))
95
+ end
96
+
97
+ def test_count_by_sql
98
+ assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
99
+ assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
100
+ assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
101
+ end
102
+
103
+ def test_find_all_with_limit
104
+ first_five_developers = Developer.find_all nil, 'id ASC', 5
105
+ assert_equal 5, first_five_developers.length
106
+ assert_equal 'David', first_five_developers.first.name
107
+ assert_equal 'fixture_5', first_five_developers.last.name
108
+
109
+ no_developers = Developer.find_all nil, 'id ASC', 0
110
+ assert_equal 0, no_developers.length
111
+
112
+ assert_equal first_five_developers, Developer.find_all(nil, 'id ASC', [5])
113
+ assert_equal no_developers, Developer.find_all(nil, 'id ASC', [0])
114
+ end
115
+
116
+ def test_find_all_with_limit_and_offset
117
+ first_three_developers = Developer.find_all nil, 'id ASC', [3, 0]
118
+ second_three_developers = Developer.find_all nil, 'id ASC', [3, 3]
119
+ last_two_developers = Developer.find_all nil, 'id ASC', [2, 8]
120
+
121
+ assert_equal 3, first_three_developers.length
122
+ assert_equal 3, second_three_developers.length
123
+ assert_equal 2, last_two_developers.length
124
+
125
+ assert_equal 'David', first_three_developers.first.name
126
+ assert_equal 'fixture_4', second_three_developers.first.name
127
+ assert_equal 'fixture_9', last_two_developers.first.name
128
+ end
129
+
130
+ def test_find_all_by_one_attribute_with_options
131
+ topics = Topic.find_all_by_content("Have a nice day", nil, "id DESC")
132
+ assert @topics["first"].find, topics.last
133
+
134
+ topics = Topic.find_all_by_content("Have a nice day", nil, "id DESC")
135
+ assert @topics["first"].find, topics.first
136
+ end
137
+
138
+
139
+ protected
140
+ def bind(statement, *vars)
141
+ if vars.first.is_a?(Hash)
142
+ ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
143
+ else
144
+ ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
145
+ end
146
+ end
147
+ end