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.
- data/CHANGELOG +2454 -34
- data/README +1 -1
- data/RUNNING_UNIT_TESTS +3 -34
- data/Rakefile +98 -77
- data/install.rb +1 -1
- data/lib/active_record.rb +13 -22
- data/lib/active_record/aggregations.rb +38 -49
- data/lib/active_record/associations.rb +452 -333
- data/lib/active_record/associations/association_collection.rb +66 -20
- data/lib/active_record/associations/association_proxy.rb +9 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +38 -18
- data/lib/active_record/associations/has_one_association.rb +30 -14
- data/lib/active_record/attribute_methods.rb +253 -0
- data/lib/active_record/base.rb +719 -494
- data/lib/active_record/calculations.rb +62 -63
- data/lib/active_record/callbacks.rb +57 -83
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
- data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
- data/lib/active_record/fixtures.rb +503 -113
- data/lib/active_record/locking/optimistic.rb +72 -34
- data/lib/active_record/migration.rb +80 -57
- data/lib/active_record/observer.rb +13 -10
- data/lib/active_record/query_cache.rb +16 -57
- data/lib/active_record/reflection.rb +35 -38
- data/lib/active_record/schema.rb +5 -5
- data/lib/active_record/schema_dumper.rb +35 -13
- data/lib/active_record/serialization.rb +98 -0
- data/lib/active_record/serializers/json_serializer.rb +71 -0
- data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
- data/lib/active_record/timestamp.rb +20 -21
- data/lib/active_record/transactions.rb +39 -43
- data/lib/active_record/validations.rb +256 -107
- data/lib/active_record/version.rb +3 -3
- data/lib/activerecord.rb +1 -0
- data/test/aaa_create_tables_test.rb +15 -2
- data/test/abstract_unit.rb +24 -17
- data/test/active_schema_test_mysql.rb +20 -8
- data/test/adapter_test.rb +23 -5
- data/test/adapter_test_sqlserver.rb +15 -1
- data/test/aggregations_test.rb +16 -1
- data/test/all.sh +2 -2
- data/test/associations/ar_joins_test.rb +0 -0
- data/test/associations/callbacks_test.rb +51 -30
- data/test/associations/cascaded_eager_loading_test.rb +1 -29
- data/test/associations/eager_singularization_test.rb +145 -0
- data/test/associations/eager_test.rb +42 -6
- data/test/associations/extension_test.rb +6 -1
- data/test/associations/inner_join_association_test.rb +88 -0
- data/test/associations/join_model_test.rb +47 -16
- data/test/associations_test.rb +449 -226
- data/test/attribute_methods_test.rb +97 -0
- data/test/base_test.rb +251 -105
- data/test/binary_test.rb +22 -27
- data/test/calculations_test.rb +37 -5
- data/test/callbacks_test.rb +23 -0
- data/test/connection_test_firebird.rb +2 -2
- data/test/connection_test_mysql.rb +30 -0
- data/test/connections/native_mysql/connection.rb +3 -0
- data/test/connections/native_sqlite/connection.rb +5 -14
- data/test/connections/native_sqlite3/connection.rb +5 -14
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
- data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
- data/test/datatype_test_postgresql.rb +178 -27
- data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
- data/test/defaults_test.rb +8 -1
- data/test/deprecated_finder_test.rb +7 -128
- data/test/finder_test.rb +192 -54
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author.rb +12 -5
- data/test/fixtures/binaries.yml +130 -435
- data/test/fixtures/category.rb +6 -0
- data/test/fixtures/company.rb +8 -1
- data/test/fixtures/computer.rb +1 -0
- data/test/fixtures/contact.rb +16 -0
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +4 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
- data/test/fixtures/db_definitions/firebird.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase.sql +5 -0
- data/test/fixtures/db_definitions/openbase.sql +41 -25
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +5 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
- data/test/fixtures/db_definitions/postgresql.sql +87 -58
- data/test/fixtures/db_definitions/postgresql2.sql +1 -2
- data/test/fixtures/db_definitions/schema.rb +280 -0
- data/test/fixtures/db_definitions/schema2.rb +11 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +4 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
- data/test/fixtures/db_definitions/sybase.sql +4 -0
- data/test/fixtures/developer.rb +10 -0
- data/test/fixtures/example.log +1 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/item.rb +7 -0
- data/test/fixtures/items.yml +4 -0
- data/test/fixtures/joke.rb +0 -3
- data/test/fixtures/matey.rb +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/minimalistic.rb +2 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/mixins.yml +2 -100
- data/test/fixtures/parrot.rb +13 -0
- data/test/fixtures/parrots.yml +27 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/pirate.rb +5 -0
- data/test/fixtures/pirates.yml +9 -0
- data/test/fixtures/post.rb +1 -0
- data/test/fixtures/project.rb +3 -2
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ship.rb +3 -0
- data/test/fixtures/ships.yml +5 -0
- data/test/fixtures/tagging.rb +4 -0
- data/test/fixtures/taggings.yml +8 -1
- data/test/fixtures/topic.rb +13 -1
- data/test/fixtures/treasure.rb +4 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures_test.rb +205 -24
- data/test/inheritance_test.rb +7 -1
- data/test/json_serialization_test.rb +180 -0
- data/test/lifecycle_test.rb +1 -1
- data/test/locking_test.rb +85 -2
- data/test/migration_test.rb +206 -40
- data/test/mixin_test.rb +13 -515
- data/test/pk_test.rb +3 -6
- data/test/query_cache_test.rb +104 -0
- data/test/reflection_test.rb +16 -0
- data/test/reserved_word_test_mysql.rb +177 -0
- data/test/schema_dumper_test.rb +38 -3
- data/test/serialization_test.rb +47 -0
- data/test/transactions_test.rb +74 -23
- data/test/unconnected_test.rb +1 -1
- data/test/validations_test.rb +322 -32
- data/test/xml_serialization_test.rb +121 -44
- metadata +48 -41
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -85
- data/lib/active_record/acts/list.rb +0 -256
- data/lib/active_record/acts/nested_set.rb +0 -211
- data/lib/active_record/acts/tree.rb +0 -96
- data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
- data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
- data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
- data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
- data/lib/active_record/deprecated_associations.rb +0 -104
- data/lib/active_record/deprecated_finders.rb +0 -44
- data/lib/active_record/vendor/simple.rb +0 -693
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -58
- data/test/connections/native_sqlserver/connection.rb +0 -23
- data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
- data/test/deprecated_associations_test.rb +0 -396
- data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
- data/test/fixtures/db_definitions/mysql.sql +0 -234
- data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
- data/test/fixtures/db_definitions/mysql2.sql +0 -5
- data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
- data/test/fixtures/db_definitions/sqlserver.sql +0 -243
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
- data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
- data/test/fixtures/mixin.rb +0 -63
- data/test/mixin_nested_set_test.rb +0 -196
data/test/unconnected_test.rb
CHANGED
data/test/validations_test.rb
CHANGED
@@ -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
|
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.
|
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
|
840
|
-
|
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.
|
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
|
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).
|
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).
|
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).
|
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).
|
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).
|
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).
|
1103
|
+
assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
|
955
1104
|
end
|
956
1105
|
|
957
|
-
def
|
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
|
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
|
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
|
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
|
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
|
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
|
|