activerecord 2.3.8 → 2.3.9.pre

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 (64) hide show
  1. data/CHANGELOG +1 -4
  2. data/Rakefile +1 -1
  3. data/examples/performance.rb +2 -0
  4. data/lib/active_record/association_preload.rb +12 -2
  5. data/lib/active_record/associations.rb +1 -1
  6. data/lib/active_record/associations/association_collection.rb +33 -4
  7. data/lib/active_record/attribute_methods.rb +0 -4
  8. data/lib/active_record/autosave_association.rb +2 -2
  9. data/lib/active_record/base.rb +28 -16
  10. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +1 -1
  11. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -0
  12. data/lib/active_record/connection_adapters/abstract_adapter.rb +6 -0
  13. data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -0
  14. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  15. data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -1
  16. data/lib/active_record/dirty.rb +1 -1
  17. data/lib/active_record/locale/en.yml +11 -11
  18. data/lib/active_record/locking/optimistic.rb +1 -0
  19. data/lib/active_record/migration.rb +1 -1
  20. data/lib/active_record/named_scope.rb +14 -9
  21. data/lib/active_record/nested_attributes.rb +11 -8
  22. data/lib/active_record/serialization.rb +1 -1
  23. data/lib/active_record/session_store.rb +9 -1
  24. data/lib/active_record/validations.rb +12 -12
  25. data/lib/active_record/version.rb +1 -1
  26. data/test/cases/adapter_test.rb +7 -8
  27. data/test/cases/associations/eager_load_nested_include_test.rb +7 -7
  28. data/test/cases/associations/eager_load_nested_polymorphic_include.rb +19 -0
  29. data/test/cases/associations/eager_test.rb +7 -0
  30. data/test/cases/associations/has_many_associations_test.rb +68 -3
  31. data/test/cases/associations_test.rb +29 -0
  32. data/test/cases/base_test.rb +35 -75
  33. data/test/cases/connection_test_mysql.rb +1 -0
  34. data/test/cases/counter_cache_test.rb +84 -0
  35. data/test/cases/locking_test.rb +2 -1
  36. data/test/cases/migration_test.rb +4 -0
  37. data/test/cases/named_scope_test.rb +6 -1
  38. data/test/cases/nested_attributes_test.rb +49 -14
  39. data/test/cases/reflection_test.rb +5 -5
  40. data/test/cases/sp_test_mysql.rb +16 -0
  41. data/test/cases/transactions_test.rb +22 -1
  42. data/test/cases/validations_i18n_test.rb +12 -12
  43. data/test/cases/validations_test.rb +42 -30
  44. data/test/fixtures/fixture_database.sqlite3 +0 -0
  45. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  46. data/test/fixtures/polymorphic_designs.yml +19 -0
  47. data/test/fixtures/polymorphic_prices.yml +19 -0
  48. data/test/fixtures/tees.yml +4 -0
  49. data/test/fixtures/ties.yml +4 -0
  50. data/test/models/author.rb +2 -0
  51. data/test/models/event_author.rb +3 -0
  52. data/test/models/pirate.rb +2 -2
  53. data/test/models/polymorphic_design.rb +3 -0
  54. data/test/models/polymorphic_price.rb +3 -0
  55. data/test/models/post.rb +2 -0
  56. data/test/models/tee.rb +4 -0
  57. data/test/models/tie.rb +4 -0
  58. data/test/schema/mysql_specific_schema.rb +7 -0
  59. data/test/schema/schema.rb +16 -0
  60. metadata +24 -13
  61. data/examples/performance.sql +0 -85
  62. data/test/cases/encoding_test.rb +0 -6
  63. data/test/fixtures/fixture_database.sqlite +0 -0
  64. data/test/fixtures/fixture_database_2.sqlite +0 -0
@@ -48,6 +48,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase
48
48
  def test_multi_results
49
49
  rows = ActiveRecord::Base.connection.select_rows('CALL ten();')
50
50
  assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
51
+ assert @connection.active?, "Bad connection use by 'MysqlAdapter.select_rows'"
51
52
  end
52
53
  end
53
54
 
@@ -0,0 +1,84 @@
1
+ require 'cases/helper'
2
+ require 'models/topic'
3
+ require 'models/reply'
4
+ require 'models/category'
5
+ require 'models/categorization'
6
+
7
+ class CounterCacheTest < ActiveRecord::TestCase
8
+ fixtures :topics, :categories, :categorizations
9
+
10
+ class SpecialTopic < ::Topic
11
+ has_many :special_replies, :foreign_key => 'parent_id'
12
+ end
13
+
14
+ class SpecialReply < ::Reply
15
+ belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count'
16
+ end
17
+
18
+ test "increment counter" do
19
+ topic = Topic.find(1)
20
+ assert_difference 'topic.reload.replies_count' do
21
+ Topic.increment_counter(:replies_count, topic.id)
22
+ end
23
+ end
24
+
25
+ test "decrement counter" do
26
+ topic = Topic.find(1)
27
+ assert_difference 'topic.reload.replies_count', -1 do
28
+ Topic.decrement_counter(:replies_count, topic.id)
29
+ end
30
+ end
31
+
32
+ test "reset counters" do
33
+ topic = Topic.find(1)
34
+ # throw the count off by 1
35
+ Topic.increment_counter(:replies_count, topic.id)
36
+
37
+ # check that it gets reset
38
+ assert_difference 'topic.reload.replies_count', -1 do
39
+ Topic.reset_counters(topic.id, :replies)
40
+ end
41
+ end
42
+
43
+ test "reset counters with string argument" do
44
+ topic = Topic.find(1)
45
+ Topic.increment_counter('replies_count', topic.id)
46
+
47
+ assert_difference 'topic.reload.replies_count', -1 do
48
+ Topic.reset_counters(topic.id, 'replies')
49
+ end
50
+ end
51
+
52
+ test "reset counters with modularized and camelized classnames" do
53
+ special = SpecialTopic.create!(:title => 'Special')
54
+ SpecialTopic.increment_counter(:replies_count, special.id)
55
+
56
+ assert_difference 'special.reload.replies_count', -1 do
57
+ SpecialTopic.reset_counters(special.id, :special_replies)
58
+ end
59
+ end
60
+
61
+ test "update counter with initial null value" do
62
+ category = categories(:general)
63
+ assert_equal 2, category.categorizations.count
64
+ assert_nil category.categorizations_count
65
+
66
+ Category.update_counters(category.id, :categorizations_count => category.categorizations.count)
67
+ assert_equal 2, category.reload.categorizations_count
68
+ end
69
+
70
+ test "update counter for decrement" do
71
+ topic = Topic.find(1)
72
+ assert_difference 'topic.reload.replies_count', -3 do
73
+ Topic.update_counters(topic.id, :replies_count => -3)
74
+ end
75
+ end
76
+
77
+ test "update counters of multiple records" do
78
+ t1, t2 = topics(:first, :second)
79
+
80
+ assert_difference ['t1.reload.replies_count', 't2.reload.replies_count'], 2 do
81
+ Topic.update_counters([t1.id, t2.id], :replies_count => 2)
82
+ end
83
+ end
84
+ end
@@ -53,7 +53,8 @@ class OptimisticLockingTest < ActiveRecord::TestCase
53
53
  assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
54
54
 
55
55
  assert p1.destroy
56
- assert_equal true, p1.frozen?
56
+ assert p1.frozen?
57
+ assert p1.destroyed?
57
58
  assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
58
59
  end
59
60
 
@@ -739,6 +739,10 @@ if ActiveRecord::Base.connection.supports_migrations?
739
739
  ActiveRecord::Base.connection.drop_table(:hats)
740
740
  end
741
741
 
742
+ def test_remove_column_no_second_parameter_raises_exception
743
+ assert_raise(ArgumentError) { Person.connection.remove_column("funny") }
744
+ end
745
+
742
746
  def test_change_type_of_not_null_column
743
747
  assert_nothing_raised do
744
748
  Topic.connection.change_column "topics", "written_on", :datetime, :null => false
@@ -9,6 +9,11 @@ require 'models/developer'
9
9
  class NamedScopeTest < ActiveRecord::TestCase
10
10
  fixtures :posts, :authors, :topics, :comments, :author_addresses
11
11
 
12
+ def test_named_scope_with_STI
13
+ assert_equal 5,Post.with_type_self.count
14
+ assert_equal 1,SpecialPost.with_type_self.count
15
+ end
16
+
12
17
  def test_implements_enumerable
13
18
  assert !Topic.find(:all).empty?
14
19
 
@@ -265,7 +270,7 @@ class NamedScopeTest < ActiveRecord::TestCase
265
270
  end
266
271
 
267
272
  def test_rand_should_select_a_random_object_from_proxy
268
- assert Topic.approved.random_element.is_a?(Topic)
273
+ assert Topic.approved.sample.is_a?(Topic)
269
274
  end
270
275
 
271
276
  def test_should_use_where_in_query_for_named_scope
@@ -175,12 +175,6 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
175
175
  assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
176
176
  end
177
177
 
178
- def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
179
- assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
180
- @pirate.ship_attributes = { :id => 1234567890 }
181
- end
182
- end
183
-
184
178
  def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
185
179
  @pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
186
180
 
@@ -330,10 +324,13 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
330
324
  assert_equal 'Arr', @ship.pirate.catchphrase
331
325
  end
332
326
 
333
- def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
334
- assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}" do
335
- @ship.pirate_attributes = { :id => 1234567890 }
336
- end
327
+ def test_should_associate_with_record_if_parent_record_is_not_saved
328
+ @ship.destroy
329
+ @pirate = Pirate.create(:catchphrase => 'Arr')
330
+ @ship = Ship.new(:name => 'Nights Dirty Lightning', :pirate_attributes => { :id => @pirate.id, :catchphrase => @pirate.catchphrase})
331
+
332
+ assert_equal @ship.name, 'Nights Dirty Lightning'
333
+ assert_equal @pirate, @ship.pirate
337
334
  end
338
335
 
339
336
  def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
@@ -437,6 +434,11 @@ module NestedAttributesOnACollectionAssociationTests
437
434
  assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
438
435
  end
439
436
 
437
+ def test_should_assign_existing_children_if_parent_is_new
438
+ @pirate = Pirate.new({:catchphrase => "Don' botharr talkin' like one, savvy?"}.merge(@alternate_params))
439
+ assert_equal ['Grace OMalley', 'Privateers Greed'], [@pirate.send(@association_name)[0].name, @pirate.send(@association_name)[1].name]
440
+ end
441
+
440
442
  def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
441
443
  @pirate.send(association_setter, @alternate_params[association_getter].values)
442
444
  @pirate.save
@@ -465,6 +467,33 @@ module NestedAttributesOnACollectionAssociationTests
465
467
  assert_equal 'Grace OMalley', @child_1.reload.name
466
468
  end
467
469
 
470
+ def test_should_not_overwrite_unsaved_updates_when_loading_association
471
+ @pirate.reload
472
+ @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
473
+ assert_equal 'Grace OMalley', @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.name
474
+ end
475
+
476
+ def test_should_preserve_order_when_not_overwriting_unsaved_updates
477
+ @pirate.reload
478
+ @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
479
+ assert_equal @child_1.id, @pirate.send(@association_name).send(:load_target).first.id
480
+ end
481
+
482
+ def test_should_refresh_saved_records_when_not_overwriting_unsaved_updates
483
+ @pirate.reload
484
+ record = @pirate.class.reflect_on_association(@association_name).klass.new(:name => 'Grace OMalley')
485
+ @pirate.send(@association_name) << record
486
+ record.save!
487
+ @pirate.send(@association_name).last.update_attributes!(:name => 'Polly')
488
+ assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name
489
+ end
490
+
491
+ def test_should_not_remove_scheduled_destroys_when_loading_association
492
+ @pirate.reload
493
+ @pirate.send(association_setter, [{ :id => @child_1.id, :_destroy => '1' }])
494
+ assert @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.marked_for_destruction?
495
+ end
496
+
468
497
  def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
469
498
  @child_1.stubs(:id).returns('ABC1X')
470
499
  @child_2.stubs(:id).returns('ABC2X')
@@ -479,8 +508,8 @@ module NestedAttributesOnACollectionAssociationTests
479
508
  assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
480
509
  end
481
510
 
482
- def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
483
- assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
511
+ def test_should_not_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
512
+ assert_nothing_raised ActiveRecord::RecordNotFound do
484
513
  @pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
485
514
  end
486
515
  end
@@ -781,7 +810,13 @@ class TestHasManyAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveR
781
810
  @part = @ship.parts.create!(:name => "Mast")
782
811
  @trinket = @part.trinkets.create!(:name => "Necklace")
783
812
  end
784
-
813
+
814
+ test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do
815
+ @ship.parts_attributes=[{:id => @part.id,:name =>'Deck'}]
816
+ assert_equal 1, @ship.parts.proxy_target.size
817
+ assert_equal 'Deck', @ship.parts[0].name
818
+ end
819
+
785
820
  test "when grandchild changed in memory, saving parent should save grandchild" do
786
821
  @trinket.name = "changed"
787
822
  @ship.save
@@ -810,4 +845,4 @@ class TestHasManyAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveR
810
845
  ShipPart.create!(:ship => @ship, :name => "Stern")
811
846
  assert_no_queries { @ship.valid? }
812
847
  end
813
- end
848
+ end
@@ -24,25 +24,25 @@ class ReflectionTest < ActiveRecord::TestCase
24
24
 
25
25
  def test_read_attribute_names
26
26
  assert_equal(
27
- %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id parent_title type ).sort,
27
+ %w( id title author_name author_email_address bonus_time written_on last_read content group approved replies_count parent_id parent_title type ).sort,
28
28
  @first.attribute_names
29
29
  )
30
30
  end
31
31
 
32
32
  def test_columns
33
- assert_equal 13, Topic.columns.length
33
+ assert_equal 14, Topic.columns.length
34
34
  end
35
35
 
36
36
  def test_columns_are_returned_in_the_order_they_were_declared
37
37
  column_names = Topic.columns.map { |column| column.name }
38
- assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type), column_names
38
+ assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type group), column_names
39
39
  end
40
40
 
41
41
  def test_content_columns
42
42
  content_columns = Topic.content_columns
43
43
  content_column_names = content_columns.map {|column| column.name}
44
- assert_equal 9, content_columns.length
45
- assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved parent_title).sort, content_column_names.sort
44
+ assert_equal 10, content_columns.length
45
+ assert_equal %w(title author_name author_email_address written_on bonus_time last_read content group approved parent_title).sort, content_column_names.sort
46
46
  end
47
47
 
48
48
  def test_column_string_type_and_limit
@@ -0,0 +1,16 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+ require 'models/minimalistic'
4
+
5
+ class StoredProcedureTest < ActiveRecord::TestCase
6
+ fixtures :topics
7
+
8
+ # Test that MySQL allows multiple results for stored procedures
9
+ if Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
10
+ def test_multi_results_from_find_by_sql
11
+ topics = Topic.find_by_sql 'CALL topics();'
12
+ assert_equal 1, topics.size
13
+ assert ActiveRecord::Base.connection.active?, "Bad connection use by 'MysqlAdapter.select'"
14
+ end
15
+ end
16
+ end
@@ -3,10 +3,12 @@ require 'models/topic'
3
3
  require 'models/reply'
4
4
  require 'models/developer'
5
5
  require 'models/book'
6
+ require 'models/author'
7
+ require 'models/post'
6
8
 
7
9
  class TransactionTest < ActiveRecord::TestCase
8
10
  self.use_transactional_fixtures = false
9
- fixtures :topics, :developers
11
+ fixtures :topics, :developers, :authors, :posts
10
12
 
11
13
  def setup
12
14
  @first, @second = Topic.find(1, 2).sort_by { |t| t.id }
@@ -34,6 +36,25 @@ class TransactionTest < ActiveRecord::TestCase
34
36
  end
35
37
  end
36
38
 
39
+ def test_update_attributes_should_rollback_on_failure
40
+ author = Author.find(1)
41
+ posts_count = author.posts.size
42
+ assert posts_count > 0
43
+ status = author.update_attributes(:name => nil, :post_ids => [])
44
+ assert !status
45
+ assert_equal posts_count, author.posts(true).size
46
+ end
47
+
48
+ def test_update_attributes_should_rollback_on_failure!
49
+ author = Author.find(1)
50
+ posts_count = author.posts.size
51
+ assert posts_count > 0
52
+ assert_raise(ActiveRecord::RecordInvalid) do
53
+ author.update_attributes!(:name => nil, :post_ids => [])
54
+ end
55
+ assert_equal posts_count, author.posts(true).size
56
+ end
57
+
37
58
  def test_successful_with_return
38
59
  class << Topic.connection
39
60
  alias :real_commit_db_transaction :commit_db_transaction
@@ -486,20 +486,20 @@ class ActiveRecordErrorI18nTests < ActiveSupport::TestCase
486
486
  end
487
487
 
488
488
  test ":default is only given to message if a symbol is supplied" do
489
- store_translations(:errors => { :messages => { :"foo bar" => "You fooed: {{value}}." } })
489
+ store_translations(:errors => { :messages => { :"foo bar" => "You fooed: %{value}." } })
490
490
  @reply.errors.add(:title, :inexistent, :default => "foo bar")
491
491
  assert_equal "foo bar", @reply.errors[:title]
492
492
  end
493
493
 
494
494
  test "#generate_message passes the model attribute value for interpolation" do
495
- store_translations(:errors => { :messages => { :foo => "You fooed: {{value}}." } })
495
+ store_translations(:errors => { :messages => { :foo => "You fooed: %{value}." } })
496
496
  @reply.title = "da title"
497
497
  assert_error_message 'You fooed: da title.', :title, :foo
498
498
  end
499
499
 
500
500
  test "#generate_message passes the human_name of the model for interpolation" do
501
501
  store_translations(
502
- :errors => { :messages => { :foo => "You fooed: {{model}}." } },
502
+ :errors => { :messages => { :foo => "You fooed: %{model}." } },
503
503
  :models => { :topic => 'da topic' }
504
504
  )
505
505
  assert_error_message 'You fooed: da topic.', :title, :foo
@@ -507,7 +507,7 @@ class ActiveRecordErrorI18nTests < ActiveSupport::TestCase
507
507
 
508
508
  test "#generate_message passes the human_name of the attribute for interpolation" do
509
509
  store_translations(
510
- :errors => { :messages => { :foo => "You fooed: {{attribute}}." } },
510
+ :errors => { :messages => { :foo => "You fooed: %{attribute}." } },
511
511
  :attributes => { :topic => { :title => 'da topic title' } }
512
512
  )
513
513
  assert_error_message 'You fooed: da topic title.', :title, :foo
@@ -607,17 +607,17 @@ class ActiveRecordErrorI18nTests < ActiveSupport::TestCase
607
607
  end
608
608
 
609
609
  test "#full_message with a format present" do
610
- store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :format => '{{attribute}}: {{message}}' } })
610
+ store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :format => '%{attribute}: %{message}' } })
611
611
  assert_full_message 'Title: is kaputt', :title, :kaputt
612
612
  end
613
613
 
614
614
  test "#full_message with a type specific format present" do
615
- store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :kaputt => '{{attribute}} {{message}}!' } })
615
+ store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :kaputt => '%{attribute} %{message}!' } })
616
616
  assert_full_message 'Title is kaputt!', :title, :kaputt
617
617
  end
618
618
 
619
619
  test "#full_message with class-level specified custom message" do
620
- store_translations(:errors => { :messages => { :broken => 'is kaputt' }, :full_messages => { :broken => '{{attribute}} {{message}}?!' } })
620
+ store_translations(:errors => { :messages => { :broken => 'is kaputt' }, :full_messages => { :broken => '%{attribute} %{message}?!' } })
621
621
  assert_full_message 'Title is kaputt?!', :title, :kaputt, :message => :broken
622
622
  end
623
623
 
@@ -625,7 +625,7 @@ class ActiveRecordErrorI18nTests < ActiveSupport::TestCase
625
625
  store_translations(:my_errors => { :messages => { :kaputt => 'is kaputt' } })
626
626
  assert_full_message 'Title is kaputt', :title, :kaputt, :scope => [:activerecord, :my_errors]
627
627
 
628
- store_translations(:my_errors => { :full_messages => { :kaputt => '{{attribute}} {{message}}!' } })
628
+ store_translations(:my_errors => { :full_messages => { :kaputt => '%{attribute} %{message}!' } })
629
629
  assert_full_message 'Title is kaputt!', :title, :kaputt, :scope => [:activerecord, :my_errors]
630
630
  end
631
631
 
@@ -763,7 +763,7 @@ class ActiveRecordDefaultErrorMessagesI18nTests < ActiveSupport::TestCase
763
763
  end
764
764
 
765
765
  test "custom message string interpolation" do
766
- assert_equal 'custom message title', error_message(:invalid, :default => 'custom message {{value}}', :value => 'title')
766
+ assert_equal 'custom message title', error_message(:invalid, :default => 'custom message %{value}', :value => 'title')
767
767
  end
768
768
  end
769
769
 
@@ -897,14 +897,14 @@ class ActiveRecordValidationsI18nFullMessagesFullStackTests < ActiveSupport::Tes
897
897
 
898
898
  test "full_message format stored per custom error message key" do
899
899
  assert_full_message("Name is broken!") do
900
- store_translations :errors => { :messages => { :broken => 'is broken' }, :full_messages => { :broken => '{{attribute}} {{message}}!' } }
900
+ store_translations :errors => { :messages => { :broken => 'is broken' }, :full_messages => { :broken => '%{attribute} %{message}!' } }
901
901
  I18nPerson.validates_presence_of :name, :message => :broken
902
902
  end
903
903
  end
904
904
 
905
905
  test "full_message format stored per error type" do
906
906
  assert_full_message("Name can't be blank!") do
907
- store_translations :errors => { :full_messages => { :blank => '{{attribute}} {{message}}!' } }
907
+ store_translations :errors => { :full_messages => { :blank => '%{attribute} %{message}!' } }
908
908
  I18nPerson.validates_presence_of :name
909
909
  end
910
910
  end
@@ -912,7 +912,7 @@ class ActiveRecordValidationsI18nFullMessagesFullStackTests < ActiveSupport::Tes
912
912
 
913
913
  test "full_message format stored as default" do
914
914
  assert_full_message("Name: can't be blank") do
915
- store_translations :errors => { :full_messages => { :format => '{{attribute}}: {{message}}' } }
915
+ store_translations :errors => { :full_messages => { :format => '%{attribute}: %{message}' } }
916
916
  I18nPerson.validates_presence_of :name
917
917
  end
918
918
  end
@@ -434,6 +434,18 @@ class ValidationsTest < ActiveRecord::TestCase
434
434
  end
435
435
  end
436
436
 
437
+ def test_validate_uniqueness_with_reserved_word_as_scope
438
+ repair_validations(Reply) do
439
+ Topic.validates_uniqueness_of(:content, :scope => "group")
440
+
441
+ t1 = Topic.create "title" => "t1", "content" => "hello world2"
442
+ assert t1.valid?
443
+
444
+ t2 = Topic.create "title" => "t2", "content" => "hello world2"
445
+ assert !t2.valid?
446
+ end
447
+ end
448
+
437
449
  def test_validate_uniqueness_scoped_to_defining_class
438
450
  t = Topic.create("title" => "What, me worry?")
439
451
 
@@ -678,7 +690,7 @@ class ValidationsTest < ActiveRecord::TestCase
678
690
  end
679
691
 
680
692
  def test_validate_format_with_formatted_message
681
- Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be {{value}}")
693
+ Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be %{value}")
682
694
  t = Topic.create(:title => 'Invalid title')
683
695
  assert_equal "can't be Invalid title", t.errors.on(:title)
684
696
  end
@@ -741,7 +753,7 @@ class ValidationsTest < ActiveRecord::TestCase
741
753
  end
742
754
 
743
755
  def test_validates_inclusion_of_with_formatted_message
744
- Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option {{value}} is not in the list" )
756
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %{value} is not in the list" )
745
757
 
746
758
  assert Topic.create("title" => "a", "content" => "abc").valid?
747
759
 
@@ -768,7 +780,7 @@ class ValidationsTest < ActiveRecord::TestCase
768
780
  end
769
781
 
770
782
  def test_validates_exclusion_of_with_formatted_message
771
- Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option {{value}} is restricted" )
783
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option %{value} is restricted" )
772
784
 
773
785
  assert Topic.create("title" => "something", "content" => "abc")
774
786
 
@@ -868,7 +880,7 @@ class ValidationsTest < ActiveRecord::TestCase
868
880
  end
869
881
 
870
882
  def test_optionally_validates_length_of_using_within_on_create
871
- Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: {{count}}"
883
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: %{count}"
872
884
 
873
885
  t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
874
886
  assert !t.save
@@ -889,7 +901,7 @@ class ValidationsTest < ActiveRecord::TestCase
889
901
  end
890
902
 
891
903
  def test_optionally_validates_length_of_using_within_on_update
892
- Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: {{count}}"
904
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: %{count}"
893
905
 
894
906
  t = Topic.create("title" => "vali", "content" => "whatever")
895
907
  assert !t.save
@@ -953,7 +965,7 @@ class ValidationsTest < ActiveRecord::TestCase
953
965
  def test_validates_length_with_globally_modified_error_message
954
966
  defaults = ActiveSupport::Deprecation.silence { ActiveRecord::Errors.default_error_messages }
955
967
  original_message = defaults[:too_short]
956
- defaults[:too_short] = 'tu est trops petit hombre {{count}}'
968
+ defaults[:too_short] = 'tu est trops petit hombre %{count}'
957
969
 
958
970
  Topic.validates_length_of :title, :minimum => 10
959
971
  t = Topic.create(:title => 'too short')
@@ -1004,7 +1016,7 @@ class ValidationsTest < ActiveRecord::TestCase
1004
1016
  end
1005
1017
 
1006
1018
  def test_validates_length_of_custom_errors_for_minimum_with_message
1007
- Topic.validates_length_of( :title, :minimum=>5, :message=>"boo {{count}}" )
1019
+ Topic.validates_length_of( :title, :minimum=>5, :message=>"boo %{count}" )
1008
1020
  t = Topic.create("title" => "uhoh", "content" => "whatever")
1009
1021
  assert !t.valid?
1010
1022
  assert t.errors.on(:title)
@@ -1012,7 +1024,7 @@ class ValidationsTest < ActiveRecord::TestCase
1012
1024
  end
1013
1025
 
1014
1026
  def test_validates_length_of_custom_errors_for_minimum_with_too_short
1015
- Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo {{count}}" )
1027
+ Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo %{count}" )
1016
1028
  t = Topic.create("title" => "uhoh", "content" => "whatever")
1017
1029
  assert !t.valid?
1018
1030
  assert t.errors.on(:title)
@@ -1020,7 +1032,7 @@ class ValidationsTest < ActiveRecord::TestCase
1020
1032
  end
1021
1033
 
1022
1034
  def test_validates_length_of_custom_errors_for_maximum_with_message
1023
- Topic.validates_length_of( :title, :maximum=>5, :message=>"boo {{count}}" )
1035
+ Topic.validates_length_of( :title, :maximum=>5, :message=>"boo %{count}" )
1024
1036
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1025
1037
  assert !t.valid?
1026
1038
  assert t.errors.on(:title)
@@ -1028,7 +1040,7 @@ class ValidationsTest < ActiveRecord::TestCase
1028
1040
  end
1029
1041
 
1030
1042
  def test_validates_length_of_custom_errors_for_in
1031
- Topic.validates_length_of(:title, :in => 10..20, :message => "hoo {{count}}")
1043
+ Topic.validates_length_of(:title, :in => 10..20, :message => "hoo %{count}")
1032
1044
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1033
1045
  assert !t.valid?
1034
1046
  assert t.errors.on(:title)
@@ -1041,7 +1053,7 @@ class ValidationsTest < ActiveRecord::TestCase
1041
1053
  end
1042
1054
 
1043
1055
  def test_validates_length_of_custom_errors_for_maximum_with_too_long
1044
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" )
1056
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}" )
1045
1057
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1046
1058
  assert !t.valid?
1047
1059
  assert t.errors.on(:title)
@@ -1049,7 +1061,7 @@ class ValidationsTest < ActiveRecord::TestCase
1049
1061
  end
1050
1062
 
1051
1063
  def test_validates_length_of_custom_errors_for_is_with_message
1052
- Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" )
1064
+ Topic.validates_length_of( :title, :is=>5, :message=>"boo %{count}" )
1053
1065
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1054
1066
  assert !t.valid?
1055
1067
  assert t.errors.on(:title)
@@ -1057,7 +1069,7 @@ class ValidationsTest < ActiveRecord::TestCase
1057
1069
  end
1058
1070
 
1059
1071
  def test_validates_length_of_custom_errors_for_is_with_wrong_length
1060
- Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" )
1072
+ Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo %{count}" )
1061
1073
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1062
1074
  assert !t.valid?
1063
1075
  assert t.errors.on(:title)
@@ -1123,7 +1135,7 @@ class ValidationsTest < ActiveRecord::TestCase
1123
1135
 
1124
1136
  def test_optionally_validates_length_of_using_within_on_create_utf8
1125
1137
  with_kcode('UTF8') do
1126
- Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: {{count}}"
1138
+ Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: %{count}"
1127
1139
 
1128
1140
  t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
1129
1141
  assert !t.save
@@ -1146,7 +1158,7 @@ class ValidationsTest < ActiveRecord::TestCase
1146
1158
 
1147
1159
  def test_optionally_validates_length_of_using_within_on_update_utf8
1148
1160
  with_kcode('UTF8') do
1149
- Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: {{count}}"
1161
+ Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: %{count}"
1150
1162
 
1151
1163
  t = Topic.create("title" => "一二三4", "content" => "whatever")
1152
1164
  assert !t.save
@@ -1181,7 +1193,7 @@ class ValidationsTest < ActiveRecord::TestCase
1181
1193
  end
1182
1194
 
1183
1195
  def test_validates_length_of_with_block
1184
- Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least {{count}} words.",
1196
+ Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least %{count} words.",
1185
1197
  :tokenizer => lambda {|str| str.scan(/\w+/) }
1186
1198
  t = Topic.create!(:content => "this content should be long enough")
1187
1199
  assert t.valid?
@@ -1356,7 +1368,7 @@ class ValidationsTest < ActiveRecord::TestCase
1356
1368
 
1357
1369
  def test_if_validation_using_method_true
1358
1370
  # When the method returns true
1359
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true )
1371
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => :condition_is_true )
1360
1372
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1361
1373
  assert !t.valid?
1362
1374
  assert t.errors.on(:title)
@@ -1365,7 +1377,7 @@ class ValidationsTest < ActiveRecord::TestCase
1365
1377
 
1366
1378
  def test_unless_validation_using_method_true
1367
1379
  # When the method returns true
1368
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true )
1380
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => :condition_is_true )
1369
1381
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1370
1382
  assert t.valid?
1371
1383
  assert !t.errors.on(:title)
@@ -1373,7 +1385,7 @@ class ValidationsTest < ActiveRecord::TestCase
1373
1385
 
1374
1386
  def test_if_validation_using_method_false
1375
1387
  # When the method returns false
1376
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not )
1388
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => :condition_is_true_but_its_not )
1377
1389
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1378
1390
  assert t.valid?
1379
1391
  assert !t.errors.on(:title)
@@ -1381,7 +1393,7 @@ class ValidationsTest < ActiveRecord::TestCase
1381
1393
 
1382
1394
  def test_unless_validation_using_method_false
1383
1395
  # When the method returns false
1384
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not )
1396
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => :condition_is_true_but_its_not )
1385
1397
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1386
1398
  assert !t.valid?
1387
1399
  assert t.errors.on(:title)
@@ -1390,7 +1402,7 @@ class ValidationsTest < ActiveRecord::TestCase
1390
1402
 
1391
1403
  def test_if_validation_using_string_true
1392
1404
  # When the evaluated string returns true
1393
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" )
1405
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => "a = 1; a == 1" )
1394
1406
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1395
1407
  assert !t.valid?
1396
1408
  assert t.errors.on(:title)
@@ -1399,7 +1411,7 @@ class ValidationsTest < ActiveRecord::TestCase
1399
1411
 
1400
1412
  def test_unless_validation_using_string_true
1401
1413
  # When the evaluated string returns true
1402
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" )
1414
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => "a = 1; a == 1" )
1403
1415
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1404
1416
  assert t.valid?
1405
1417
  assert !t.errors.on(:title)
@@ -1407,7 +1419,7 @@ class ValidationsTest < ActiveRecord::TestCase
1407
1419
 
1408
1420
  def test_if_validation_using_string_false
1409
1421
  # When the evaluated string returns false
1410
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false")
1422
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => "false")
1411
1423
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1412
1424
  assert t.valid?
1413
1425
  assert !t.errors.on(:title)
@@ -1415,7 +1427,7 @@ class ValidationsTest < ActiveRecord::TestCase
1415
1427
 
1416
1428
  def test_unless_validation_using_string_false
1417
1429
  # When the evaluated string returns false
1418
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false")
1430
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => "false")
1419
1431
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1420
1432
  assert !t.valid?
1421
1433
  assert t.errors.on(:title)
@@ -1424,7 +1436,7 @@ class ValidationsTest < ActiveRecord::TestCase
1424
1436
 
1425
1437
  def test_if_validation_using_block_true
1426
1438
  # When the block returns true
1427
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1439
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}",
1428
1440
  :if => Proc.new { |r| r.content.size > 4 } )
1429
1441
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1430
1442
  assert !t.valid?
@@ -1434,7 +1446,7 @@ class ValidationsTest < ActiveRecord::TestCase
1434
1446
 
1435
1447
  def test_unless_validation_using_block_true
1436
1448
  # When the block returns true
1437
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1449
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}",
1438
1450
  :unless => Proc.new { |r| r.content.size > 4 } )
1439
1451
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1440
1452
  assert t.valid?
@@ -1443,7 +1455,7 @@ class ValidationsTest < ActiveRecord::TestCase
1443
1455
 
1444
1456
  def test_if_validation_using_block_false
1445
1457
  # When the block returns false
1446
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1458
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}",
1447
1459
  :if => Proc.new { |r| r.title != "uhohuhoh"} )
1448
1460
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1449
1461
  assert t.valid?
@@ -1452,7 +1464,7 @@ class ValidationsTest < ActiveRecord::TestCase
1452
1464
 
1453
1465
  def test_unless_validation_using_block_false
1454
1466
  # When the block returns false
1455
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1467
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}",
1456
1468
  :unless => Proc.new { |r| r.title != "uhohuhoh"} )
1457
1469
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1458
1470
  assert !t.valid?
@@ -1634,13 +1646,13 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
1634
1646
  end
1635
1647
 
1636
1648
  def test_validates_numericality_with_numeric_message
1637
- Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}"
1649
+ Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than %{count}"
1638
1650
  topic = Topic.new("title" => "numeric test", "approved" => 10)
1639
1651
 
1640
1652
  assert !topic.valid?
1641
1653
  assert_equal "smaller than 4", topic.errors.on(:approved)
1642
1654
 
1643
- Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than {{count}}"
1655
+ Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than %{count}"
1644
1656
  topic = Topic.new("title" => "numeric test", "approved" => 1)
1645
1657
 
1646
1658
  assert !topic.valid?