activerecord 1.15.6 → 2.0.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 (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -3,7 +3,7 @@ require 'abstract_unit'
3
3
  class TestRecord < ActiveRecord::Base
4
4
  end
5
5
 
6
- class TestUnconnectedAdaptor < Test::Unit::TestCase
6
+ class TestUnconnectedAdapter < Test::Unit::TestCase
7
7
  self.use_transactional_fixtures = false
8
8
 
9
9
  def setup
@@ -1,6 +1,7 @@
1
1
  require 'abstract_unit'
2
2
  require 'fixtures/topic'
3
3
  require 'fixtures/reply'
4
+ require 'fixtures/person'
4
5
  require 'fixtures/developer'
5
6
 
6
7
  # The following methods in Topic are used in test_conditional_validation_*
@@ -14,6 +15,44 @@ class Topic
14
15
  end
15
16
  end
16
17
 
18
+ class ProtectedPerson < ActiveRecord::Base
19
+ set_table_name 'people'
20
+ attr_accessor :addon
21
+ attr_protected :first_name
22
+ end
23
+
24
+ class UniqueReply < Reply
25
+ validates_uniqueness_of :content, :scope => 'parent_id'
26
+ end
27
+
28
+ class PlagiarizedReply < Reply
29
+ validates_acceptance_of :author_name
30
+ end
31
+
32
+ class SillyUniqueReply < UniqueReply
33
+ end
34
+
35
+ class Topic < ActiveRecord::Base
36
+ has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
37
+ has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
38
+ end
39
+
40
+ class Wizard < ActiveRecord::Base
41
+ self.abstract_class = true
42
+
43
+ validates_uniqueness_of :name
44
+ end
45
+
46
+ class IneptWizard < Wizard
47
+ validates_uniqueness_of :city
48
+ end
49
+
50
+ class Conjurer < IneptWizard
51
+ end
52
+
53
+ class Thaumaturgist < IneptWizard
54
+ end
55
+
17
56
  class ValidationsTest < Test::Unit::TestCase
18
57
  fixtures :topics, :developers
19
58
 
@@ -88,12 +127,36 @@ class ValidationsTest < Test::Unit::TestCase
88
127
  end
89
128
  end
90
129
 
130
+ def test_exception_on_create_bang_many
131
+ assert_raises(ActiveRecord::RecordInvalid) do
132
+ Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
133
+ end
134
+ end
135
+
91
136
  def test_scoped_create_without_attributes
92
137
  Reply.with_scope(:create => {}) do
93
138
  assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
94
139
  end
95
140
  end
96
141
 
142
+ def test_create_with_exceptions_using_scope_for_protected_attributes
143
+ assert_nothing_raised do
144
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
145
+ person = ProtectedPerson.create! :addon => "Addon"
146
+ assert_equal person.first_name, "Mary", "scope should ignore attr_protected"
147
+ end
148
+ end
149
+ end
150
+
151
+ def test_create_with_exceptions_using_scope_and_empty_attributes
152
+ assert_nothing_raised do
153
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
154
+ person = ProtectedPerson.create!
155
+ assert_equal person.first_name, "Mary", "should be ok when no attributes are passed to create!"
156
+ end
157
+ end
158
+ end
159
+
97
160
  def test_single_error_per_attr_iteration
98
161
  r = Reply.new
99
162
  r.save
@@ -233,6 +296,11 @@ class ValidationsTest < Test::Unit::TestCase
233
296
  assert t.save
234
297
  end
235
298
 
299
+ def test_validates_acceptance_of_as_database_column
300
+ reply = PlagiarizedReply.create("author_name" => "Dan Brown")
301
+ assert_equal "Dan Brown", reply["author_name"]
302
+ end
303
+
236
304
  def test_validate_presences
237
305
  Topic.validates_presence_of(:title, :content)
238
306
 
@@ -289,6 +357,21 @@ class ValidationsTest < Test::Unit::TestCase
289
357
  assert r3.valid?, "Saving r3"
290
358
  end
291
359
 
360
+ def test_validate_uniqueness_scoped_to_defining_class
361
+ t = Topic.create("title" => "What, me worry?")
362
+
363
+ r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun"
364
+ assert r1.valid?, "Saving r1"
365
+
366
+ r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun"
367
+ assert !r2.valid?, "Saving r2"
368
+
369
+ # Should succeed as validates_uniqueness_of only applies to
370
+ # UniqueReply and its subclasses
371
+ r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun"
372
+ assert r3.valid?, "Saving r3"
373
+ end
374
+
292
375
  def test_validate_uniqueness_with_scope_array
293
376
  Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
294
377
 
@@ -344,6 +427,35 @@ class ValidationsTest < Test::Unit::TestCase
344
427
  assert t2.save, "should save with nil"
345
428
  end
346
429
 
430
+ def test_validate_straight_inheritance_uniqueness
431
+ w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
432
+ assert w1.valid?, "Saving w1"
433
+
434
+ # Should use validation from base class (which is abstract)
435
+ w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
436
+ assert !w2.valid?, "w2 shouldn't be valid"
437
+ assert w2.errors.on(:name), "Should have errors for name"
438
+ assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
439
+
440
+ w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
441
+ assert !w3.valid?, "w3 shouldn't be valid"
442
+ assert w3.errors.on(:name), "Should have errors for name"
443
+ assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
444
+
445
+ w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
446
+ assert w4.valid?, "Saving w4"
447
+
448
+ w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
449
+ assert !w5.valid?, "w5 shouldn't be valid"
450
+ assert w5.errors.on(:name), "Should have errors for name"
451
+ assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
452
+
453
+ w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
454
+ assert !w6.valid?, "w6 shouldn't be valid"
455
+ assert w6.errors.on(:city), "Should have errors for city"
456
+ assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
457
+ end
458
+
347
459
  def test_validate_format
348
460
  Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
349
461
 
@@ -419,6 +531,42 @@ class ValidationsTest < Test::Unit::TestCase
419
531
  assert Topic.create("title" => nil, "content" => "abc").valid?
420
532
  end
421
533
 
534
+ def test_numericality_with_getter_method
535
+ Developer.validates_numericality_of( :salary )
536
+ developer = Developer.new("name" => "michael", "salary" => nil)
537
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
538
+ assert developer.valid?
539
+ end
540
+
541
+ def test_validates_length_of_with_allow_nil
542
+ Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
543
+
544
+ assert !Topic.create("title" => "ab").valid?
545
+ assert !Topic.create("title" => "").valid?
546
+ assert Topic.create("title" => nil).valid?
547
+ assert Topic.create("title" => "abcde").valid?
548
+ end
549
+
550
+ def test_validates_length_of_with_allow_blank
551
+ Topic.validates_length_of( :title, :is => 5, :allow_blank=>true )
552
+
553
+ assert !Topic.create("title" => "ab").valid?
554
+ assert Topic.create("title" => "").valid?
555
+ assert Topic.create("title" => nil).valid?
556
+ assert Topic.create("title" => "abcde").valid?
557
+ end
558
+
559
+ def test_validates_inclusion_of_with_formatted_message
560
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %s is not in the list" )
561
+
562
+ assert Topic.create("title" => "a", "content" => "abc").valid?
563
+
564
+ t = Topic.create("title" => "uhoh", "content" => "abc")
565
+ assert !t.valid?
566
+ assert t.errors.on(:title)
567
+ assert_equal "option uhoh is not in the list", t.errors["title"]
568
+ end
569
+
422
570
  def test_numericality_with_allow_nil_and_getter_method
423
571
  Developer.validates_numericality_of( :salary, :allow_nil => true)
424
572
  developer = Developer.new("name" => "michael", "salary" => nil)
@@ -433,6 +581,17 @@ class ValidationsTest < Test::Unit::TestCase
433
581
  assert !Topic.create("title" => "monkey", "content" => "abc").valid?
434
582
  end
435
583
 
584
+ def test_validates_exclusion_of_with_formatted_message
585
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option %s is restricted" )
586
+
587
+ assert Topic.create("title" => "something", "content" => "abc")
588
+
589
+ t = Topic.create("title" => "monkey")
590
+ assert !t.valid?
591
+ assert t.errors.on(:title)
592
+ assert_equal "option monkey is restricted", t.errors["title"]
593
+ end
594
+
436
595
  def test_validates_length_of_using_minimum
437
596
  Topic.validates_length_of :title, :minimum => 5
438
597
 
@@ -605,7 +764,7 @@ class ValidationsTest < Test::Unit::TestCase
605
764
  end
606
765
  end
607
766
 
608
- def test_validates_length_with_globaly_modified_error_message
767
+ def test_validates_length_with_globally_modified_error_message
609
768
  ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre %d'
610
769
  Topic.validates_length_of :title, :minimum => 10
611
770
  t = Topic.create(:title => 'too short')
@@ -613,25 +772,13 @@ class ValidationsTest < Test::Unit::TestCase
613
772
 
614
773
  assert_equal 'tu est trops petit hombre 10', t.errors['title']
615
774
  end
616
-
617
- def test_add_on_boundary_breaking_is_deprecated
618
- t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
619
- class << t
620
- def validate
621
- errors.add_on_boundary_breaking('title', 1..6)
622
- end
623
- end
624
- assert_deprecated 'add_on_boundary_breaking' do
625
- assert !t.valid?
626
- end
627
- end
628
775
 
629
776
  def test_validates_size_of_association
630
777
  assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
631
778
  t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
632
779
  assert !t.save
633
780
  assert t.errors.on(:replies)
634
- t.replies.build('title' => 'areply', 'content' => 'whateveragain')
781
+ reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
635
782
  assert t.valid?
636
783
  end
637
784
 
@@ -832,19 +979,21 @@ class ValidationsTest < Test::Unit::TestCase
832
979
  def test_validates_associated_many
833
980
  Topic.validates_associated( :replies )
834
981
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
835
- t.replies << [r = Reply.create("title" => "A reply"), r2 = Reply.create("title" => "Another reply")]
982
+ t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
836
983
  assert !t.valid?
837
984
  assert t.errors.on(:replies)
838
985
  assert_equal 1, r.errors.count # make sure all associated objects have been validated
839
- assert_equal 1, r2.errors.count
840
- r.content = r2.content = "non-empty"
986
+ assert_equal 0, r2.errors.count
987
+ assert_equal 1, r3.errors.count
988
+ assert_equal 0, r4.errors.count
989
+ r.content = r3.content = "non-empty"
841
990
  assert t.valid?
842
991
  end
843
992
 
844
993
  def test_validates_associated_one
845
994
  Reply.validates_associated( :topic )
846
995
  Topic.validates_presence_of( :content )
847
- r = Reply.create("title" => "A reply", "content" => "with content!")
996
+ r = Reply.new("title" => "A reply", "content" => "with content!")
848
997
  r.topic = Topic.create("title" => "uhohuhoh")
849
998
  assert !r.valid?
850
999
  assert r.errors.on(:topic)
@@ -877,7 +1026,7 @@ class ValidationsTest < Test::Unit::TestCase
877
1026
  d = Developer.new
878
1027
  d.salary = "0"
879
1028
  assert !d.valid?
880
- assert_equal d.errors.on(:salary).first, "This string contains 'single' and \"double\" quotes"
1029
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
881
1030
  end
882
1031
 
883
1032
  def test_validates_confirmation_of_with_custom_error_using_quotes
@@ -902,7 +1051,7 @@ class ValidationsTest < Test::Unit::TestCase
902
1051
  d = Developer.new
903
1052
  d.salary = "90,000"
904
1053
  assert !d.valid?
905
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).first
1054
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
906
1055
  end
907
1056
 
908
1057
  def test_validates_length_of_with_custom_too_long_using_quotes
@@ -910,7 +1059,7 @@ class ValidationsTest < Test::Unit::TestCase
910
1059
  d = Developer.new
911
1060
  d.name = "Jeffrey"
912
1061
  assert !d.valid?
913
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
1062
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
914
1063
  end
915
1064
 
916
1065
  def test_validates_length_of_with_custom_too_short_using_quotes
@@ -918,7 +1067,7 @@ class ValidationsTest < Test::Unit::TestCase
918
1067
  d = Developer.new
919
1068
  d.name = "Joe"
920
1069
  assert !d.valid?
921
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
1070
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
922
1071
  end
923
1072
 
924
1073
  def test_validates_length_of_with_custom_message_using_quotes
@@ -926,7 +1075,7 @@ class ValidationsTest < Test::Unit::TestCase
926
1075
  d = Developer.new
927
1076
  d.name = "Joe"
928
1077
  assert !d.valid?
929
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
1078
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
930
1079
  end
931
1080
 
932
1081
  def test_validates_presence_of_with_custom_message_using_quotes
@@ -942,7 +1091,7 @@ class ValidationsTest < Test::Unit::TestCase
942
1091
  d = Developer.new
943
1092
  d.name = "David"
944
1093
  assert !d.valid?
945
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
1094
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
946
1095
  end
947
1096
 
948
1097
  def test_validates_associated_with_custom_message_using_quotes
@@ -951,10 +1100,10 @@ class ValidationsTest < Test::Unit::TestCase
951
1100
  r = Reply.create("title" => "A reply", "content" => "with content!")
952
1101
  r.topic = Topic.create("title" => "uhohuhoh")
953
1102
  assert !r.valid?
954
- assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).first
1103
+ assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
955
1104
  end
956
1105
 
957
- def test_conditional_validation_using_method_true
1106
+ def test_if_validation_using_method_true
958
1107
  # When the method returns true
959
1108
  Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true )
960
1109
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -963,7 +1112,15 @@ class ValidationsTest < Test::Unit::TestCase
963
1112
  assert_equal "hoo 5", t.errors["title"]
964
1113
  end
965
1114
 
966
- def test_conditional_validation_using_method_false
1115
+ def test_unless_validation_using_method_true
1116
+ # When the method returns true
1117
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true )
1118
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1119
+ assert t.valid?
1120
+ assert !t.errors.on(:title)
1121
+ end
1122
+
1123
+ def test_if_validation_using_method_false
967
1124
  # When the method returns false
968
1125
  Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true_but_its_not )
969
1126
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -971,7 +1128,16 @@ class ValidationsTest < Test::Unit::TestCase
971
1128
  assert !t.errors.on(:title)
972
1129
  end
973
1130
 
974
- def test_conditional_validation_using_string_true
1131
+ def test_unless_validation_using_method_false
1132
+ # When the method returns false
1133
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true_but_its_not )
1134
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1135
+ assert !t.valid?
1136
+ assert t.errors.on(:title)
1137
+ assert_equal "hoo 5", t.errors["title"]
1138
+ end
1139
+
1140
+ def test_if_validation_using_string_true
975
1141
  # When the evaluated string returns true
976
1142
  Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "a = 1; a == 1" )
977
1143
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -980,7 +1146,15 @@ class ValidationsTest < Test::Unit::TestCase
980
1146
  assert_equal "hoo 5", t.errors["title"]
981
1147
  end
982
1148
 
983
- def test_conditional_validation_using_string_false
1149
+ def test_unless_validation_using_string_true
1150
+ # When the evaluated string returns true
1151
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "a = 1; a == 1" )
1152
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1153
+ assert t.valid?
1154
+ assert !t.errors.on(:title)
1155
+ end
1156
+
1157
+ def test_if_validation_using_string_false
984
1158
  # When the evaluated string returns false
985
1159
  Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "false")
986
1160
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -988,7 +1162,16 @@ class ValidationsTest < Test::Unit::TestCase
988
1162
  assert !t.errors.on(:title)
989
1163
  end
990
1164
 
991
- def test_conditional_validation_using_block_true
1165
+ def test_unless_validation_using_string_false
1166
+ # When the evaluated string returns false
1167
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "false")
1168
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1169
+ assert !t.valid?
1170
+ assert t.errors.on(:title)
1171
+ assert_equal "hoo 5", t.errors["title"]
1172
+ end
1173
+
1174
+ def test_if_validation_using_block_true
992
1175
  # When the block returns true
993
1176
  Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
994
1177
  :if => Proc.new { |r| r.content.size > 4 } )
@@ -998,7 +1181,16 @@ class ValidationsTest < Test::Unit::TestCase
998
1181
  assert_equal "hoo 5", t.errors["title"]
999
1182
  end
1000
1183
 
1001
- def test_conditional_validation_using_block_false
1184
+ def test_unless_validation_using_block_true
1185
+ # When the block returns true
1186
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1187
+ :unless => Proc.new { |r| r.content.size > 4 } )
1188
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1189
+ assert t.valid?
1190
+ assert !t.errors.on(:title)
1191
+ end
1192
+
1193
+ def test_if_validation_using_block_false
1002
1194
  # When the block returns false
1003
1195
  Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1004
1196
  :if => Proc.new { |r| r.title != "uhohuhoh"} )
@@ -1007,6 +1199,16 @@ class ValidationsTest < Test::Unit::TestCase
1007
1199
  assert !t.errors.on(:title)
1008
1200
  end
1009
1201
 
1202
+ def test_unless_validation_using_block_false
1203
+ # When the block returns false
1204
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1205
+ :unless => Proc.new { |r| r.title != "uhohuhoh"} )
1206
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1207
+ assert !t.valid?
1208
+ assert t.errors.on(:title)
1209
+ assert_equal "hoo 5", t.errors["title"]
1210
+ end
1211
+
1010
1212
  def test_validates_associated_missing
1011
1213
  Reply.validates_presence_of(:topic)
1012
1214
  r = Reply.create("title" => "A reply", "content" => "with content!")
@@ -1025,6 +1227,37 @@ class ValidationsTest < Test::Unit::TestCase
1025
1227
  assert xml.include?("<error>Title is Wrong Create</error>")
1026
1228
  assert xml.include?("<error>Content Empty</error>")
1027
1229
  end
1230
+
1231
+ def test_validation_order
1232
+ Topic.validates_presence_of :title
1233
+ Topic.validates_length_of :title, :minimum => 2
1234
+
1235
+ t = Topic.new("title" => "")
1236
+ assert !t.valid?
1237
+ assert_equal "can't be blank", t.errors.on("title").first
1238
+ end
1239
+
1240
+ # previous implementation of validates_presence_of eval'd the
1241
+ # string with the wrong binding, this regression test is to
1242
+ # ensure that it works correctly
1243
+ def test_validation_with_if_as_string
1244
+ Topic.validates_presence_of(:title)
1245
+ Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
1246
+
1247
+ t = Topic.new
1248
+ assert !t.valid?, "A topic without a title should not be valid"
1249
+ assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author"
1250
+
1251
+ t.title = "Just a title"
1252
+ assert t.valid?, "A topic with a basic title should be valid"
1253
+
1254
+ t.title = "A very important title"
1255
+ assert !t.valid?, "A topic with an important title, but without an author, should not be valid"
1256
+ assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author"
1257
+
1258
+ t.author_name = "Hubert J. Farnsworth"
1259
+ assert t.valid?, "A topic with an important title and author should be valid"
1260
+ end
1028
1261
  end
1029
1262
 
1030
1263
 
@@ -1073,11 +1306,68 @@ class ValidatesNumericalityTest < Test::Unit::TestCase
1073
1306
  valid!(NIL + INTEGERS)
1074
1307
  end
1075
1308
 
1309
+ def test_validates_numericality_with_greater_than
1310
+ Topic.validates_numericality_of :approved, :greater_than => 10
1311
+
1312
+ invalid!([-10, 10], 'must be greater than 10')
1313
+ valid!([11])
1314
+ end
1315
+
1316
+ def test_validates_numericality_with_greater_than_or_equal
1317
+ Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
1318
+
1319
+ invalid!([-9, 9], 'must be greater than or equal to 10')
1320
+ valid!([10])
1321
+ end
1322
+
1323
+ def test_validates_numericality_with_equal_to
1324
+ Topic.validates_numericality_of :approved, :equal_to => 10
1325
+
1326
+ invalid!([-10, 11], 'must be equal to 10')
1327
+ valid!([10])
1328
+ end
1329
+
1330
+ def test_validates_numericality_with_less_than
1331
+ Topic.validates_numericality_of :approved, :less_than => 10
1332
+
1333
+ invalid!([10], 'must be less than 10')
1334
+ valid!([-9, 9])
1335
+ end
1336
+
1337
+ def test_validates_numericality_with_less_than_or_equal_to
1338
+ Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
1339
+
1340
+ invalid!([11], 'must be less than or equal to 10')
1341
+ valid!([-10, 10])
1342
+ end
1343
+
1344
+ def test_validates_numericality_with_odd
1345
+ Topic.validates_numericality_of :approved, :odd => true
1346
+
1347
+ invalid!([-2, 2], 'must be odd')
1348
+ valid!([-1, 1])
1349
+ end
1350
+
1351
+ def test_validates_numericality_with_even
1352
+ Topic.validates_numericality_of :approved, :even => true
1353
+
1354
+ invalid!([-1, 1], 'must be even')
1355
+ valid!([-2, 2])
1356
+ end
1357
+
1358
+ def test_validates_numericality_with_greater_than_less_than_and_even
1359
+ Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
1360
+
1361
+ invalid!([1, 3, 4])
1362
+ valid!([2])
1363
+ end
1364
+
1076
1365
  private
1077
- def invalid!(values)
1366
+ def invalid!(values, error=nil)
1078
1367
  with_each_topic_approved_value(values) do |topic, value|
1079
1368
  assert !topic.valid?, "#{value.inspect} not rejected as a number"
1080
1369
  assert topic.errors.on(:approved)
1370
+ assert_equal error, topic.errors.on(:approved) if error
1081
1371
  end
1082
1372
  end
1083
1373