activerecord 2.1.2 → 2.2.2

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 (110) hide show
  1. data/CHANGELOG +32 -6
  2. data/README +0 -0
  3. data/Rakefile +4 -5
  4. data/lib/active_record.rb +11 -10
  5. data/lib/active_record/aggregations.rb +110 -38
  6. data/lib/active_record/association_preload.rb +104 -15
  7. data/lib/active_record/associations.rb +427 -212
  8. data/lib/active_record/associations/association_collection.rb +101 -16
  9. data/lib/active_record/associations/association_proxy.rb +65 -13
  10. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
  12. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -3
  13. data/lib/active_record/associations/has_many_association.rb +28 -28
  14. data/lib/active_record/associations/has_many_through_association.rb +21 -19
  15. data/lib/active_record/associations/has_one_association.rb +24 -7
  16. data/lib/active_record/associations/has_one_through_association.rb +3 -4
  17. data/lib/active_record/attribute_methods.rb +13 -5
  18. data/lib/active_record/base.rb +435 -212
  19. data/lib/active_record/calculations.rb +12 -5
  20. data/lib/active_record/callbacks.rb +28 -9
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +355 -0
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +42 -215
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -5
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +48 -7
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +10 -4
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -26
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +71 -45
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +155 -84
  30. data/lib/active_record/dirty.rb +25 -7
  31. data/lib/active_record/dynamic_finder_match.rb +41 -0
  32. data/lib/active_record/fixtures.rb +10 -9
  33. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  34. data/lib/active_record/locale/en.yml +54 -0
  35. data/lib/active_record/migration.rb +47 -10
  36. data/lib/active_record/named_scope.rb +29 -16
  37. data/lib/active_record/reflection.rb +118 -54
  38. data/lib/active_record/schema_dumper.rb +13 -7
  39. data/lib/active_record/test_case.rb +18 -5
  40. data/lib/active_record/transactions.rb +89 -34
  41. data/lib/active_record/validations.rb +270 -180
  42. data/lib/active_record/version.rb +1 -1
  43. data/test/cases/active_schema_test_mysql.rb +5 -0
  44. data/test/cases/adapter_test.rb +6 -0
  45. data/test/cases/aggregations_test.rb +39 -0
  46. data/test/cases/associations/belongs_to_associations_test.rb +10 -0
  47. data/test/cases/associations/eager_load_nested_include_test.rb +30 -12
  48. data/test/cases/associations/eager_test.rb +54 -5
  49. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +77 -10
  50. data/test/cases/associations/has_many_associations_test.rb +74 -7
  51. data/test/cases/associations/has_many_through_associations_test.rb +50 -3
  52. data/test/cases/associations/has_one_associations_test.rb +17 -0
  53. data/test/cases/associations/has_one_through_associations_test.rb +49 -1
  54. data/test/cases/associations_test.rb +0 -0
  55. data/test/cases/attribute_methods_test.rb +59 -4
  56. data/test/cases/base_test.rb +93 -21
  57. data/test/cases/binary_test.rb +1 -5
  58. data/test/cases/calculations_test.rb +5 -0
  59. data/test/cases/callbacks_observers_test.rb +38 -0
  60. data/test/cases/connection_test_mysql.rb +1 -1
  61. data/test/cases/defaults_test.rb +32 -1
  62. data/test/cases/deprecated_finder_test.rb +0 -0
  63. data/test/cases/dirty_test.rb +13 -0
  64. data/test/cases/finder_test.rb +162 -12
  65. data/test/cases/fixtures_test.rb +32 -3
  66. data/test/cases/helper.rb +15 -0
  67. data/test/cases/i18n_test.rb +41 -0
  68. data/test/cases/inheritance_test.rb +2 -2
  69. data/test/cases/lifecycle_test.rb +0 -0
  70. data/test/cases/locking_test.rb +4 -9
  71. data/test/cases/method_scoping_test.rb +109 -2
  72. data/test/cases/migration_test.rb +43 -8
  73. data/test/cases/multiple_db_test.rb +25 -0
  74. data/test/cases/named_scope_test.rb +74 -0
  75. data/test/cases/pooled_connections_test.rb +103 -0
  76. data/test/cases/readonly_test.rb +0 -0
  77. data/test/cases/reflection_test.rb +11 -3
  78. data/test/cases/reload_models_test.rb +20 -0
  79. data/test/cases/sanitize_test.rb +25 -0
  80. data/test/cases/schema_authorization_test_postgresql.rb +2 -2
  81. data/test/cases/transactions_test.rb +62 -12
  82. data/test/cases/unconnected_test.rb +0 -0
  83. data/test/cases/validations_i18n_test.rb +921 -0
  84. data/test/cases/validations_test.rb +44 -33
  85. data/test/connections/native_mysql/connection.rb +1 -3
  86. data/test/fixtures/companies.yml +1 -0
  87. data/test/fixtures/customers.yml +10 -1
  88. data/test/fixtures/fixture_database.sqlite3 +0 -0
  89. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  90. data/test/fixtures/organizations.yml +5 -0
  91. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  92. data/test/models/author.rb +3 -0
  93. data/test/models/category.rb +3 -0
  94. data/test/models/club.rb +6 -0
  95. data/test/models/company.rb +25 -1
  96. data/test/models/customer.rb +19 -1
  97. data/test/models/member.rb +2 -0
  98. data/test/models/member_detail.rb +4 -0
  99. data/test/models/organization.rb +4 -0
  100. data/test/models/parrot.rb +1 -0
  101. data/test/models/post.rb +3 -0
  102. data/test/models/reply.rb +0 -0
  103. data/test/models/topic.rb +3 -0
  104. data/test/schema/schema.rb +12 -1
  105. metadata +22 -10
  106. data/lib/active_record/vendor/mysql.rb +0 -1214
  107. data/test/cases/adapter_test_sqlserver.rb +0 -95
  108. data/test/cases/table_name_test_sqlserver.rb +0 -23
  109. data/test/cases/threaded_connections_test.rb +0 -48
  110. data/test/schema/sqlserver_specific_schema.rb +0 -5
@@ -0,0 +1,103 @@
1
+ require "cases/helper"
2
+
3
+ class PooledConnectionsTest < ActiveRecord::TestCase
4
+ def setup
5
+ super
6
+ @connection = ActiveRecord::Base.remove_connection
7
+ end
8
+
9
+ def teardown
10
+ ActiveRecord::Base.clear_all_connections!
11
+ ActiveRecord::Base.establish_connection(@connection)
12
+ super
13
+ end
14
+
15
+ def checkout_connections
16
+ ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3}))
17
+ @connections = []
18
+ @timed_out = 0
19
+
20
+ 4.times do
21
+ Thread.new do
22
+ begin
23
+ @connections << ActiveRecord::Base.connection_pool.checkout
24
+ rescue ActiveRecord::ConnectionTimeoutError
25
+ @timed_out += 1
26
+ end
27
+ end.join
28
+ end
29
+ end
30
+
31
+ # Will deadlock due to lack of Monitor timeouts in 1.9
32
+ if RUBY_VERSION < '1.9'
33
+ def test_pooled_connection_checkout
34
+ checkout_connections
35
+ assert_equal @connections.length, 2
36
+ assert_equal @timed_out, 2
37
+ end
38
+ end
39
+
40
+ def checkout_checkin_connections(pool_size, threads)
41
+ ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5}))
42
+ @connection_count = 0
43
+ @timed_out = 0
44
+ threads.times do
45
+ Thread.new do
46
+ begin
47
+ conn = ActiveRecord::Base.connection_pool.checkout
48
+ sleep 0.1
49
+ ActiveRecord::Base.connection_pool.checkin conn
50
+ @connection_count += 1
51
+ rescue ActiveRecord::ConnectionTimeoutError
52
+ @timed_out += 1
53
+ end
54
+ end.join
55
+ end
56
+ end
57
+
58
+ def test_pooled_connection_checkin_one
59
+ checkout_checkin_connections 1, 2
60
+ assert_equal 2, @connection_count
61
+ assert_equal 0, @timed_out
62
+ end
63
+
64
+ def test_pooled_connection_checkin_two
65
+ checkout_checkin_connections 2, 3
66
+ assert_equal 3, @connection_count
67
+ assert_equal 0, @timed_out
68
+ end
69
+
70
+ def test_pooled_connection_checkout_existing_first
71
+ ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1}))
72
+ conn_pool = ActiveRecord::Base.connection_pool
73
+ conn = conn_pool.checkout
74
+ conn_pool.checkin(conn)
75
+ conn = conn_pool.checkout
76
+ assert ActiveRecord::ConnectionAdapters::AbstractAdapter === conn
77
+ conn_pool.checkin(conn)
78
+ end
79
+
80
+ def test_not_connected_defined_connection_returns_false
81
+ ActiveRecord::Base.establish_connection(@connection)
82
+ assert ! ActiveRecord::Base.connected?
83
+ end
84
+
85
+ def test_undefined_connection_returns_false
86
+ old_handler = ActiveRecord::Base.connection_handler
87
+ ActiveRecord::Base.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
88
+ assert_equal false, ActiveRecord::Base.connected?
89
+ ensure
90
+ ActiveRecord::Base.connection_handler = old_handler
91
+ end
92
+ end unless %w(FrontBase).include? ActiveRecord::Base.connection.adapter_name
93
+
94
+ class AllowConcurrencyDeprecatedTest < ActiveRecord::TestCase
95
+ def test_allow_concurrency_is_deprecated
96
+ assert_deprecated('ActiveRecord::Base.allow_concurrency') do
97
+ ActiveRecord::Base.allow_concurrency
98
+ end
99
+ assert_deprecated('ActiveRecord::Base.allow_concurrency=') do
100
+ ActiveRecord::Base.allow_concurrency = true
101
+ end
102
+ end
103
+ end
File without changes
@@ -160,12 +160,20 @@ class ReflectionTest < ActiveRecord::TestCase
160
160
 
161
161
  def test_reflection_of_all_associations
162
162
  # FIXME these assertions bust a lot
163
- assert_equal 22, Firm.reflect_on_all_associations.size
164
- assert_equal 17, Firm.reflect_on_all_associations(:has_many).size
165
- assert_equal 5, Firm.reflect_on_all_associations(:has_one).size
163
+ assert_equal 26, Firm.reflect_on_all_associations.size
164
+ assert_equal 20, Firm.reflect_on_all_associations(:has_many).size
165
+ assert_equal 6, Firm.reflect_on_all_associations(:has_one).size
166
166
  assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
167
167
  end
168
168
 
169
+ def test_reflection_should_not_raise_error_when_compared_to_other_object
170
+ assert_nothing_raised { Firm.reflections[:clients] == Object.new }
171
+ end
172
+
173
+ def test_has_many_through_reflection
174
+ assert_kind_of ActiveRecord::Reflection::ThroughReflection, Subscriber.reflect_on_association(:books)
175
+ end
176
+
169
177
  private
170
178
  def assert_reflection(klass, association, options)
171
179
  assert reflection = klass.reflect_on_association(association)
@@ -0,0 +1,20 @@
1
+ require "cases/helper"
2
+ require 'models/owner'
3
+ require 'models/pet'
4
+
5
+ class ReloadModelsTest < ActiveRecord::TestCase
6
+ def test_has_one_with_reload
7
+ pet = Pet.find_by_name('parrot')
8
+ pet.owner = Owner.find_by_name('ashley')
9
+
10
+ # Reload the class Owner, simulating auto-reloading of model classes in a
11
+ # development environment. Note that meanwhile the class Pet is not
12
+ # reloaded, simulating a class that is present in a plugin.
13
+ Object.class_eval { remove_const :Owner }
14
+ Kernel.load(File.expand_path(File.join(File.dirname(__FILE__), "../models/owner.rb")))
15
+
16
+ pet = Pet.find_by_name('parrot')
17
+ pet.owner = Owner.find_by_name('ashley')
18
+ assert_equal pet.owner, Owner.find_by_name('ashley')
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ require "cases/helper"
2
+ require 'models/binary'
3
+
4
+ class SanitizeTest < ActiveRecord::TestCase
5
+ def setup
6
+ end
7
+
8
+ def test_sanitize_sql_array_handles_string_interpolation
9
+ quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
10
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi"])
11
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi".mb_chars])
12
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
13
+ assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper"])
14
+ assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper".mb_chars])
15
+ end
16
+
17
+ def test_sanitize_sql_array_handles_bind_variables
18
+ quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
19
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi"])
20
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi".mb_chars])
21
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
22
+ assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper"])
23
+ assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".mb_chars])
24
+ end
25
+ end
@@ -18,8 +18,8 @@ class SchemaAuthorizationTest < ActiveRecord::TestCase
18
18
  @connection.execute "SET search_path TO '$user',public"
19
19
  set_session_auth
20
20
  USERS.each do |u|
21
- @connection.execute "CREATE USER #{u}"
22
- @connection.execute "CREATE SCHEMA AUTHORIZATION #{u}"
21
+ @connection.execute "CREATE USER #{u}" rescue nil
22
+ @connection.execute "CREATE SCHEMA AUTHORIZATION #{u}" rescue nil
23
23
  set_session_auth u
24
24
  @connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
25
25
  @connection.execute "INSERT INTO #{TABLE_NAME} (name) VALUES ('#{u}')"
@@ -2,6 +2,7 @@ require "cases/helper"
2
2
  require 'models/topic'
3
3
  require 'models/reply'
4
4
  require 'models/developer'
5
+ require 'models/book'
5
6
 
6
7
  class TransactionTest < ActiveRecord::TestCase
7
8
  self.use_transactional_fixtures = false
@@ -86,8 +87,7 @@ class TransactionTest < ActiveRecord::TestCase
86
87
  assert Topic.find(2).approved?, "Second should still be approved"
87
88
  end
88
89
 
89
-
90
- def test_callback_rollback_in_save
90
+ def test_raising_exception_in_callback_rollbacks_in_save
91
91
  add_exception_raising_after_save_callback_to_topic
92
92
 
93
93
  begin
@@ -102,6 +102,54 @@ class TransactionTest < ActiveRecord::TestCase
102
102
  end
103
103
  end
104
104
 
105
+ def test_cancellation_from_before_destroy_rollbacks_in_destroy
106
+ add_cancelling_before_destroy_with_db_side_effect_to_topic
107
+ begin
108
+ nbooks_before_destroy = Book.count
109
+ status = @first.destroy
110
+ assert !status
111
+ assert_nothing_raised(ActiveRecord::RecordNotFound) { @first.reload }
112
+ assert_equal nbooks_before_destroy, Book.count
113
+ ensure
114
+ remove_cancelling_before_destroy_with_db_side_effect_to_topic
115
+ end
116
+ end
117
+
118
+ def test_cancellation_from_before_filters_rollbacks_in_save
119
+ %w(validation save).each do |filter|
120
+ send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
121
+ begin
122
+ nbooks_before_save = Book.count
123
+ original_author_name = @first.author_name
124
+ @first.author_name += '_this_should_not_end_up_in_the_db'
125
+ status = @first.save
126
+ assert !status
127
+ assert_equal original_author_name, @first.reload.author_name
128
+ assert_equal nbooks_before_save, Book.count
129
+ ensure
130
+ send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
131
+ end
132
+ end
133
+ end
134
+
135
+ def test_cancellation_from_before_filters_rollbacks_in_save!
136
+ %w(validation save).each do |filter|
137
+ send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
138
+ begin
139
+ nbooks_before_save = Book.count
140
+ original_author_name = @first.author_name
141
+ @first.author_name += '_this_should_not_end_up_in_the_db'
142
+ @first.save!
143
+ flunk
144
+ rescue => e
145
+ assert_equal original_author_name, @first.reload.author_name
146
+ assert_equal nbooks_before_save, Book.count
147
+ ensure
148
+ send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
149
+ end
150
+ end
151
+ end
152
+
105
153
  def test_callback_rollback_in_create
106
154
  new_topic = Topic.new(
107
155
  :title => "A new topic",
@@ -168,6 +216,7 @@ class TransactionTest < ActiveRecord::TestCase
168
216
  uses_mocha 'mocking connection.commit_db_transaction' do
169
217
  def test_rollback_when_commit_raises
170
218
  Topic.connection.expects(:begin_db_transaction)
219
+ Topic.connection.expects(:transaction_active?).returns(true) if current_adapter?(:PostgreSQLAdapter)
171
220
  Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
172
221
  Topic.connection.expects(:rollback_db_transaction)
173
222
 
@@ -221,20 +270,21 @@ class TransactionTest < ActiveRecord::TestCase
221
270
  def remove_exception_raising_after_create_callback_to_topic
222
271
  Topic.class_eval { remove_method :after_create }
223
272
  end
273
+
274
+ %w(validation save destroy).each do |filter|
275
+ define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
276
+ Topic.class_eval "def before_#{filter}() Book.create; false end"
277
+ end
278
+
279
+ define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
280
+ Topic.class_eval "remove_method :before_#{filter}"
281
+ end
282
+ end
224
283
  end
225
284
 
226
285
  if current_adapter?(:PostgreSQLAdapter)
227
286
  class ConcurrentTransactionTest < TransactionTest
228
- def setup
229
- @allow_concurrency = ActiveRecord::Base.allow_concurrency
230
- ActiveRecord::Base.allow_concurrency = true
231
- super
232
- end
233
-
234
- def teardown
235
- super
236
- ActiveRecord::Base.allow_concurrency = @allow_concurrency
237
- end
287
+ use_concurrent_connections
238
288
 
239
289
  # This will cause transactions to overlap and fail unless they are performed on
240
290
  # separate database connections.
File without changes
@@ -0,0 +1,921 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+ require 'models/reply'
4
+
5
+ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase
6
+ def setup
7
+ reset_callbacks Topic
8
+ @topic = Topic.new
9
+ @old_load_path, @old_backend = I18n.load_path, I18n.backend
10
+ I18n.load_path.clear
11
+ I18n.backend = I18n::Backend::Simple.new
12
+ I18n.backend.store_translations('en', :activerecord => {:errors => {:messages => {:custom => nil}}})
13
+ end
14
+
15
+ def teardown
16
+ reset_callbacks Topic
17
+ I18n.load_path.replace @old_load_path
18
+ I18n.backend = @old_backend
19
+ end
20
+
21
+ def unique_topic
22
+ @unique ||= Topic.create :title => 'unique!'
23
+ end
24
+
25
+ def replied_topic
26
+ @replied_topic ||= begin
27
+ topic = Topic.create(:title => "topic")
28
+ topic.replies << Reply.new
29
+ topic
30
+ end
31
+ end
32
+
33
+ def reset_callbacks(*models)
34
+ models.each do |model|
35
+ model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
36
+ model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
37
+ model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
38
+ end
39
+ end
40
+
41
+ def test_default_error_messages_is_deprecated
42
+ assert_deprecated('ActiveRecord::Errors.default_error_messages') do
43
+ ActiveRecord::Errors.default_error_messages
44
+ end
45
+ end
46
+
47
+ def test_percent_s_interpolation_syntax_in_error_messages_still_works
48
+ ActiveSupport::Deprecation.silence do
49
+ result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this'
50
+ assert_equal result, "this interpolation syntax is deprecated"
51
+ end
52
+ end
53
+
54
+ def test_percent_s_interpolation_syntax_in_error_messages_is_deprecated
55
+ assert_deprecated('using %s in messages') do
56
+ I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this'
57
+ end
58
+ end
59
+
60
+ def test_percent_d_interpolation_syntax_in_error_messages_still_works
61
+ ActiveSupport::Deprecation.silence do
62
+ result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2
63
+ assert_equal result, "2 interpolation syntaxes are deprecated"
64
+ end
65
+ end
66
+
67
+ def test_percent_d_interpolation_syntax_in_error_messages_is_deprecated
68
+ assert_deprecated('using %d in messages') do
69
+ I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2
70
+ end
71
+ end
72
+
73
+ # ActiveRecord::Errors
74
+ uses_mocha 'ActiveRecord::Errors' do
75
+
76
+ def test_errors_generate_message_translates_custom_model_attribute_key
77
+
78
+ I18n.expects(:translate).with(
79
+ :topic,
80
+ { :count => 1,
81
+ :default => ['Topic'],
82
+ :scope => [:activerecord, :models]
83
+ }
84
+ ).returns('Topic')
85
+
86
+ I18n.expects(:translate).with(
87
+ :"topic.title",
88
+ { :count => 1,
89
+ :default => ['Title'],
90
+ :scope => [:activerecord, :attributes]
91
+ }
92
+ ).returns('Title')
93
+
94
+ I18n.expects(:translate).with(
95
+ :"models.topic.attributes.title.invalid",
96
+ :value => nil,
97
+ :scope => [:activerecord, :errors],
98
+ :default => [
99
+ :"models.topic.invalid",
100
+ 'default from class def error 1',
101
+ :"messages.invalid"],
102
+ :attribute => "Title",
103
+ :model => "Topic"
104
+ ).returns('default from class def error 1')
105
+
106
+ @topic.errors.generate_message :title, :invalid, :default => 'default from class def error 1'
107
+ end
108
+
109
+ def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti
110
+
111
+ I18n.expects(:translate).with(
112
+ :reply,
113
+ { :count => 1,
114
+ :default => [:topic, 'Reply'],
115
+ :scope => [:activerecord, :models]
116
+ }
117
+ ).returns('Reply')
118
+
119
+ I18n.expects(:translate).with(
120
+ :"reply.title",
121
+ { :count => 1,
122
+ :default => [:'topic.title', 'Title'],
123
+ :scope => [:activerecord, :attributes]
124
+ }
125
+ ).returns('Title')
126
+
127
+ I18n.expects(:translate).with(
128
+ :"models.reply.attributes.title.invalid",
129
+ :value => nil,
130
+ :scope => [:activerecord, :errors],
131
+ :default => [
132
+ :"models.reply.invalid",
133
+ :"models.topic.attributes.title.invalid",
134
+ :"models.topic.invalid",
135
+ 'default from class def',
136
+ :"messages.invalid"],
137
+ :model => 'Reply',
138
+ :attribute => 'Title'
139
+ ).returns("default from class def")
140
+
141
+ Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def'
142
+
143
+ end
144
+
145
+ def test_errors_add_on_empty_generates_message
146
+ @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil})
147
+ @topic.errors.add_on_empty :title
148
+ end
149
+
150
+ def test_errors_add_on_empty_generates_message_with_custom_default_message
151
+ @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'})
152
+ @topic.errors.add_on_empty :title, 'custom'
153
+ end
154
+
155
+ def test_errors_add_on_blank_generates_message
156
+ @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil})
157
+ @topic.errors.add_on_blank :title
158
+ end
159
+
160
+ def test_errors_add_on_blank_generates_message_with_custom_default_message
161
+ @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'})
162
+ @topic.errors.add_on_blank :title, 'custom'
163
+ end
164
+
165
+ def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
166
+ @topic.errors.instance_variable_set :@errors, { 'title' => ['empty'] }
167
+ I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title')
168
+ @topic.errors.full_messages :locale => 'en'
169
+ end
170
+ end
171
+
172
+ # ActiveRecord::Validations
173
+ uses_mocha 'ActiveRecord::Validations' do
174
+ # validates_confirmation_of w/ mocha
175
+
176
+ def test_validates_confirmation_of_generates_message
177
+ Topic.validates_confirmation_of :title
178
+ @topic.title_confirmation = 'foo'
179
+ @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil})
180
+ @topic.valid?
181
+ end
182
+
183
+ def test_validates_confirmation_of_generates_message_with_custom_default_message
184
+ Topic.validates_confirmation_of :title, :message => 'custom'
185
+ @topic.title_confirmation = 'foo'
186
+ @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'})
187
+ @topic.valid?
188
+ end
189
+
190
+ # validates_acceptance_of w/ mocha
191
+
192
+ def test_validates_acceptance_of_generates_message
193
+ Topic.validates_acceptance_of :title, :allow_nil => false
194
+ @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => nil})
195
+ @topic.valid?
196
+ end
197
+
198
+ def test_validates_acceptance_of_generates_message_with_custom_default_message
199
+ Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false
200
+ @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'})
201
+ @topic.valid?
202
+ end
203
+
204
+ # validates_presence_of w/ mocha
205
+
206
+ def test_validates_presence_of_generates_message
207
+ Topic.validates_presence_of :title
208
+ @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil})
209
+ @topic.valid?
210
+ end
211
+
212
+ def test_validates_presence_of_generates_message_with_custom_default_message
213
+ Topic.validates_presence_of :title, :message => 'custom'
214
+ @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'})
215
+ @topic.valid?
216
+ end
217
+
218
+ def test_validates_length_of_within_generates_message_with_title_too_short
219
+ Topic.validates_length_of :title, :within => 3..5
220
+ @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil})
221
+ @topic.valid?
222
+ end
223
+
224
+ def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message
225
+ Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom'
226
+ @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'})
227
+ @topic.valid?
228
+ end
229
+
230
+ def test_validates_length_of_within_generates_message_with_title_too_long
231
+ Topic.validates_length_of :title, :within => 3..5
232
+ @topic.title = 'this title is too long'
233
+ @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil})
234
+ @topic.valid?
235
+ end
236
+
237
+ def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message
238
+ Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom'
239
+ @topic.title = 'this title is too long'
240
+ @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'})
241
+ @topic.valid?
242
+ end
243
+
244
+ # validates_length_of :within w/ mocha
245
+
246
+ def test_validates_length_of_within_generates_message_with_title_too_short
247
+ Topic.validates_length_of :title, :within => 3..5
248
+ @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil})
249
+ @topic.valid?
250
+ end
251
+
252
+ def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message
253
+ Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom'
254
+ @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'})
255
+ @topic.valid?
256
+ end
257
+
258
+ def test_validates_length_of_within_generates_message_with_title_too_long
259
+ Topic.validates_length_of :title, :within => 3..5
260
+ @topic.title = 'this title is too long'
261
+ @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil})
262
+ @topic.valid?
263
+ end
264
+
265
+ def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message
266
+ Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom'
267
+ @topic.title = 'this title is too long'
268
+ @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'})
269
+ @topic.valid?
270
+ end
271
+
272
+ # validates_length_of :is w/ mocha
273
+
274
+ def test_validates_length_of_is_generates_message
275
+ Topic.validates_length_of :title, :is => 5
276
+ @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => nil})
277
+ @topic.valid?
278
+ end
279
+
280
+ def test_validates_length_of_is_generates_message_with_custom_default_message
281
+ Topic.validates_length_of :title, :is => 5, :message => 'custom'
282
+ @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'})
283
+ @topic.valid?
284
+ end
285
+
286
+ # validates_uniqueness_of w/ mocha
287
+
288
+ def test_validates_uniqueness_of_generates_message
289
+ Topic.validates_uniqueness_of :title
290
+ @topic.title = unique_topic.title
291
+ @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'})
292
+ @topic.valid?
293
+ end
294
+
295
+ def test_validates_uniqueness_of_generates_message_with_custom_default_message
296
+ Topic.validates_uniqueness_of :title, :message => 'custom'
297
+ @topic.title = unique_topic.title
298
+ @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'})
299
+ @topic.valid?
300
+ end
301
+
302
+ # validates_format_of w/ mocha
303
+
304
+ def test_validates_format_of_generates_message
305
+ Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
306
+ @topic.title = '72x'
307
+ @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => nil})
308
+ @topic.valid?
309
+ end
310
+
311
+ def test_validates_format_of_generates_message_with_custom_default_message
312
+ Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom'
313
+ @topic.title = '72x'
314
+ @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'})
315
+ @topic.valid?
316
+ end
317
+
318
+ # validates_inclusion_of w/ mocha
319
+
320
+ def test_validates_inclusion_of_generates_message
321
+ Topic.validates_inclusion_of :title, :in => %w(a b c)
322
+ @topic.title = 'z'
323
+ @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => nil})
324
+ @topic.valid?
325
+ end
326
+
327
+ def test_validates_inclusion_of_generates_message_with_custom_default_message
328
+ Topic.validates_inclusion_of :title, :in => %w(a b c), :message => 'custom'
329
+ @topic.title = 'z'
330
+ @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'})
331
+ @topic.valid?
332
+ end
333
+
334
+ # validates_exclusion_of w/ mocha
335
+
336
+ def test_validates_exclusion_of_generates_message
337
+ Topic.validates_exclusion_of :title, :in => %w(a b c)
338
+ @topic.title = 'a'
339
+ @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil})
340
+ @topic.valid?
341
+ end
342
+
343
+ def test_validates_exclusion_of_generates_message_with_custom_default_message
344
+ Topic.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom'
345
+ @topic.title = 'a'
346
+ @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'})
347
+ @topic.valid?
348
+ end
349
+
350
+ # validates_numericality_of without :only_integer w/ mocha
351
+
352
+ def test_validates_numericality_of_generates_message
353
+ Topic.validates_numericality_of :title
354
+ @topic.title = 'a'
355
+ @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil})
356
+ @topic.valid?
357
+ end
358
+
359
+ def test_validates_numericality_of_generates_message_with_custom_default_message
360
+ Topic.validates_numericality_of :title, :message => 'custom'
361
+ @topic.title = 'a'
362
+ @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'})
363
+ @topic.valid?
364
+ end
365
+
366
+ # validates_numericality_of with :only_integer w/ mocha
367
+
368
+ def test_validates_numericality_of_only_integer_generates_message
369
+ Topic.validates_numericality_of :title, :only_integer => true
370
+ @topic.title = 'a'
371
+ @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil})
372
+ @topic.valid?
373
+ end
374
+
375
+ def test_validates_numericality_of_only_integer_generates_message_with_custom_default_message
376
+ Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom'
377
+ @topic.title = 'a'
378
+ @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'})
379
+ @topic.valid?
380
+ end
381
+
382
+ # validates_numericality_of :odd w/ mocha
383
+
384
+ def test_validates_numericality_of_odd_generates_message
385
+ Topic.validates_numericality_of :title, :only_integer => true, :odd => true
386
+ @topic.title = 0
387
+ @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil})
388
+ @topic.valid?
389
+ end
390
+
391
+ def test_validates_numericality_of_odd_generates_message_with_custom_default_message
392
+ Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom'
393
+ @topic.title = 0
394
+ @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'})
395
+ @topic.valid?
396
+ end
397
+
398
+ # validates_numericality_of :less_than w/ mocha
399
+
400
+ def test_validates_numericality_of_less_than_generates_message
401
+ Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
402
+ @topic.title = 1
403
+ @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => nil})
404
+ @topic.valid?
405
+ end
406
+
407
+ def test_validates_numericality_of_odd_generates_message_with_custom_default_message
408
+ Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom'
409
+ @topic.title = 1
410
+ @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'})
411
+ @topic.valid?
412
+ end
413
+
414
+ # validates_associated w/ mocha
415
+
416
+ def test_validates_associated_generates_message
417
+ Topic.validates_associated :replies
418
+ replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil})
419
+ replied_topic.valid?
420
+ end
421
+
422
+ def test_validates_associated_generates_message_with_custom_default_message
423
+ Topic.validates_associated :replies
424
+ replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil})
425
+ replied_topic.valid?
426
+ end
427
+ end
428
+
429
+ # validates_confirmation_of w/o mocha
430
+
431
+ def test_validates_confirmation_of_finds_custom_model_key_translation
432
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:confirmation => 'custom message'}}}}}}
433
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}}
434
+
435
+ Topic.validates_confirmation_of :title
436
+ @topic.title_confirmation = 'foo'
437
+ @topic.valid?
438
+ assert_equal 'custom message', @topic.errors.on(:title)
439
+ end
440
+
441
+ def test_validates_confirmation_of_finds_global_default_translation
442
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}}
443
+
444
+ Topic.validates_confirmation_of :title
445
+ @topic.title_confirmation = 'foo'
446
+ @topic.valid?
447
+ assert_equal 'global message', @topic.errors.on(:title)
448
+ end
449
+
450
+ # validates_acceptance_of w/o mocha
451
+
452
+ def test_validates_acceptance_of_finds_custom_model_key_translation
453
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:accepted => 'custom message'}}}}}}
454
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}}
455
+
456
+ Topic.validates_acceptance_of :title, :allow_nil => false
457
+ @topic.valid?
458
+ assert_equal 'custom message', @topic.errors.on(:title)
459
+ end
460
+
461
+ def test_validates_acceptance_of_finds_global_default_translation
462
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}}
463
+
464
+ Topic.validates_acceptance_of :title, :allow_nil => false
465
+ @topic.valid?
466
+ assert_equal 'global message', @topic.errors.on(:title)
467
+ end
468
+
469
+ # validates_presence_of w/o mocha
470
+
471
+ def test_validates_presence_of_finds_custom_model_key_translation
472
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:blank => 'custom message'}}}}}}
473
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}}
474
+
475
+ Topic.validates_presence_of :title
476
+ @topic.valid?
477
+ assert_equal 'custom message', @topic.errors.on(:title)
478
+ end
479
+
480
+ def test_validates_presence_of_finds_global_default_translation
481
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}}
482
+
483
+ Topic.validates_presence_of :title
484
+ @topic.valid?
485
+ assert_equal 'global message', @topic.errors.on(:title)
486
+ end
487
+
488
+ # validates_length_of :within w/o mocha
489
+
490
+ def test_validates_length_of_within_finds_custom_model_key_translation
491
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:too_short => 'custom message'}}}}}}
492
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}}
493
+
494
+ Topic.validates_length_of :title, :within => 3..5
495
+ @topic.valid?
496
+ assert_equal 'custom message', @topic.errors.on(:title)
497
+ end
498
+
499
+ def test_validates_length_of_within_finds_global_default_translation
500
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}}
501
+
502
+ Topic.validates_length_of :title, :within => 3..5
503
+ @topic.valid?
504
+ assert_equal 'global message', @topic.errors.on(:title)
505
+ end
506
+
507
+ # validates_length_of :is w/o mocha
508
+
509
+ def test_validates_length_of_within_finds_custom_model_key_translation
510
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
511
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
512
+
513
+ Topic.validates_length_of :title, :is => 5
514
+ @topic.valid?
515
+ assert_equal 'custom message', @topic.errors.on(:title)
516
+ end
517
+
518
+ def test_validates_length_of_within_finds_global_default_translation
519
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
520
+
521
+ Topic.validates_length_of :title, :is => 5
522
+ @topic.valid?
523
+ assert_equal 'global message', @topic.errors.on(:title)
524
+ end
525
+
526
+ # validates_uniqueness_of w/o mocha
527
+
528
+ def test_validates_length_of_within_finds_custom_model_key_translation
529
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
530
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
531
+
532
+ Topic.validates_length_of :title, :is => 5
533
+ @topic.valid?
534
+ assert_equal 'custom message', @topic.errors.on(:title)
535
+ end
536
+
537
+ def test_validates_length_of_within_finds_global_default_translation
538
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
539
+
540
+ Topic.validates_length_of :title, :is => 5
541
+ @topic.valid?
542
+ assert_equal 'global message', @topic.errors.on(:title)
543
+ end
544
+
545
+
546
+ # validates_format_of w/o mocha
547
+
548
+ def test_validates_format_of_finds_custom_model_key_translation
549
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:invalid => 'custom message'}}}}}}
550
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
551
+
552
+ Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
553
+ @topic.valid?
554
+ assert_equal 'custom message', @topic.errors.on(:title)
555
+ end
556
+
557
+ def test_validates_format_of_finds_global_default_translation
558
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
559
+
560
+ Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
561
+ @topic.valid?
562
+ assert_equal 'global message', @topic.errors.on(:title)
563
+ end
564
+
565
+ # validates_inclusion_of w/o mocha
566
+
567
+ def test_validates_inclusion_of_finds_custom_model_key_translation
568
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:inclusion => 'custom message'}}}}}}
569
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}}
570
+
571
+ Topic.validates_inclusion_of :title, :in => %w(a b c)
572
+ @topic.valid?
573
+ assert_equal 'custom message', @topic.errors.on(:title)
574
+ end
575
+
576
+ def test_validates_inclusion_of_finds_global_default_translation
577
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}}
578
+
579
+ Topic.validates_inclusion_of :title, :in => %w(a b c)
580
+ @topic.valid?
581
+ assert_equal 'global message', @topic.errors.on(:title)
582
+ end
583
+
584
+ # validates_exclusion_of w/o mocha
585
+
586
+ def test_validates_exclusion_of_finds_custom_model_key_translation
587
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:exclusion => 'custom message'}}}}}}
588
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}}
589
+
590
+ Topic.validates_exclusion_of :title, :in => %w(a b c)
591
+ @topic.title = 'a'
592
+ @topic.valid?
593
+ assert_equal 'custom message', @topic.errors.on(:title)
594
+ end
595
+
596
+ def test_validates_exclusion_of_finds_global_default_translation
597
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}}
598
+
599
+ Topic.validates_exclusion_of :title, :in => %w(a b c)
600
+ @topic.title = 'a'
601
+ @topic.valid?
602
+ assert_equal 'global message', @topic.errors.on(:title)
603
+ end
604
+
605
+ # validates_numericality_of without :only_integer w/o mocha
606
+
607
+ def test_validates_numericality_of_finds_custom_model_key_translation
608
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}}
609
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
610
+
611
+ Topic.validates_numericality_of :title
612
+ @topic.title = 'a'
613
+ @topic.valid?
614
+ assert_equal 'custom message', @topic.errors.on(:title)
615
+ end
616
+
617
+ def test_validates_numericality_of_finds_global_default_translation
618
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
619
+
620
+ Topic.validates_numericality_of :title, :only_integer => true
621
+ @topic.title = 'a'
622
+ @topic.valid?
623
+ assert_equal 'global message', @topic.errors.on(:title)
624
+ end
625
+
626
+ # validates_numericality_of with :only_integer w/o mocha
627
+
628
+ def test_validates_numericality_of_only_integer_finds_custom_model_key_translation
629
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}}
630
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
631
+
632
+ Topic.validates_numericality_of :title, :only_integer => true
633
+ @topic.title = 'a'
634
+ @topic.valid?
635
+ assert_equal 'custom message', @topic.errors.on(:title)
636
+ end
637
+
638
+ def test_validates_numericality_of_only_integer_finds_global_default_translation
639
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}}
640
+
641
+ Topic.validates_numericality_of :title, :only_integer => true
642
+ @topic.title = 'a'
643
+ @topic.valid?
644
+ assert_equal 'global message', @topic.errors.on(:title)
645
+ end
646
+
647
+ # validates_numericality_of :odd w/o mocha
648
+
649
+ def test_validates_numericality_of_odd_finds_custom_model_key_translation
650
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:odd => 'custom message'}}}}}}
651
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:odd => 'global message'}}}
652
+
653
+ Topic.validates_numericality_of :title, :only_integer => true, :odd => true
654
+ @topic.title = 0
655
+ @topic.valid?
656
+ assert_equal 'custom message', @topic.errors.on(:title)
657
+ end
658
+
659
+ def test_validates_numericality_of_odd_finds_global_default_translation
660
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:odd => 'global message'}}}
661
+
662
+ Topic.validates_numericality_of :title, :only_integer => true, :odd => true
663
+ @topic.title = 0
664
+ @topic.valid?
665
+ assert_equal 'global message', @topic.errors.on(:title)
666
+ end
667
+
668
+ # validates_numericality_of :less_than w/o mocha
669
+
670
+ def test_validates_numericality_of_less_than_finds_custom_model_key_translation
671
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:less_than => 'custom message'}}}}}}
672
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}}
673
+
674
+ Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
675
+ @topic.title = 1
676
+ @topic.valid?
677
+ assert_equal 'custom message', @topic.errors.on(:title)
678
+ end
679
+
680
+ def test_validates_numericality_of_less_than_finds_global_default_translation
681
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}}
682
+
683
+ Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
684
+ @topic.title = 1
685
+ @topic.valid?
686
+ assert_equal 'global message', @topic.errors.on(:title)
687
+ end
688
+
689
+
690
+ # validates_associated w/o mocha
691
+
692
+ def test_validates_associated_finds_custom_model_key_translation
693
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}}
694
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
695
+
696
+ Topic.validates_associated :replies
697
+ replied_topic.valid?
698
+ assert_equal 'custom message', replied_topic.errors.on(:replies)
699
+ end
700
+
701
+ def test_validates_associated_finds_global_default_translation
702
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
703
+
704
+ Topic.validates_associated :replies
705
+ replied_topic.valid?
706
+ assert_equal 'global message', replied_topic.errors.on(:replies)
707
+ end
708
+
709
+ def test_validations_with_message_symbol_must_translate
710
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:custom_error => "I am a custom error"}}}
711
+ Topic.validates_presence_of :title, :message => :custom_error
712
+ @topic.title = nil
713
+ @topic.valid?
714
+ assert_equal "I am a custom error", @topic.errors.on(:title)
715
+ end
716
+
717
+ def test_validates_with_message_symbol_must_translate_per_attribute
718
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:custom_error => "I am a custom error"}}}}}}
719
+ Topic.validates_presence_of :title, :message => :custom_error
720
+ @topic.title = nil
721
+ @topic.valid?
722
+ assert_equal "I am a custom error", @topic.errors.on(:title)
723
+ end
724
+
725
+ def test_validates_with_message_symbol_must_translate_per_model
726
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:custom_error => "I am a custom error"}}}}
727
+ Topic.validates_presence_of :title, :message => :custom_error
728
+ @topic.title = nil
729
+ @topic.valid?
730
+ assert_equal "I am a custom error", @topic.errors.on(:title)
731
+ end
732
+
733
+ def test_validates_with_message_string
734
+ Topic.validates_presence_of :title, :message => "I am a custom error"
735
+ @topic.title = nil
736
+ @topic.valid?
737
+ assert_equal "I am a custom error", @topic.errors.on(:title)
738
+ end
739
+
740
+ end
741
+
742
+ class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase
743
+ def setup
744
+ reset_callbacks Topic
745
+ @topic = Topic.new
746
+ I18n.backend.store_translations :'en', {
747
+ :activerecord => {
748
+ :errors => {
749
+ :messages => {
750
+ :inclusion => "is not included in the list",
751
+ :exclusion => "is reserved",
752
+ :invalid => "is invalid",
753
+ :confirmation => "doesn't match confirmation",
754
+ :accepted => "must be accepted",
755
+ :empty => "can't be empty",
756
+ :blank => "can't be blank",
757
+ :too_long => "is too long (maximum is {{count}} characters)",
758
+ :too_short => "is too short (minimum is {{count}} characters)",
759
+ :wrong_length => "is the wrong length (should be {{count}} characters)",
760
+ :taken => "has already been taken",
761
+ :not_a_number => "is not a number",
762
+ :greater_than => "must be greater than {{count}}",
763
+ :greater_than_or_equal_to => "must be greater than or equal to {{count}}",
764
+ :equal_to => "must be equal to {{count}}",
765
+ :less_than => "must be less than {{count}}",
766
+ :less_than_or_equal_to => "must be less than or equal to {{count}}",
767
+ :odd => "must be odd",
768
+ :even => "must be even"
769
+ }
770
+ }
771
+ }
772
+ }
773
+ end
774
+
775
+ def reset_callbacks(*models)
776
+ models.each do |model|
777
+ model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
778
+ model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
779
+ model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
780
+ end
781
+ end
782
+
783
+ # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value)
784
+ def test_generate_message_inclusion_with_default_message
785
+ assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title')
786
+ end
787
+
788
+ def test_generate_message_inclusion_with_custom_message
789
+ assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title')
790
+ end
791
+
792
+ # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value)
793
+ def test_generate_message_exclusion_with_default_message
794
+ assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title')
795
+ end
796
+
797
+ def test_generate_message_exclusion_with_custom_message
798
+ assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title')
799
+ end
800
+
801
+ # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
802
+ # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value)
803
+ def test_generate_message_invalid_with_default_message
804
+ assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title')
805
+ end
806
+
807
+ def test_generate_message_invalid_with_custom_message
808
+ assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title')
809
+ end
810
+
811
+ # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message])
812
+ def test_generate_message_confirmation_with_default_message
813
+ assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil)
814
+ end
815
+
816
+ def test_generate_message_confirmation_with_custom_message
817
+ assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message')
818
+ end
819
+
820
+ # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message])
821
+ def test_generate_message_accepted_with_default_message
822
+ assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil)
823
+ end
824
+
825
+ def test_generate_message_accepted_with_custom_message
826
+ assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message')
827
+ end
828
+
829
+ # add_on_empty: generate_message(attr, :empty, :default => custom_message)
830
+ def test_generate_message_empty_with_default_message
831
+ assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil)
832
+ end
833
+
834
+ def test_generate_message_empty_with_custom_message
835
+ assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message')
836
+ end
837
+
838
+ # add_on_blank: generate_message(attr, :blank, :default => custom_message)
839
+ def test_generate_message_blank_with_default_message
840
+ assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil)
841
+ end
842
+
843
+ def test_generate_message_blank_with_custom_message
844
+ assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message')
845
+ end
846
+
847
+ # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end)
848
+ def test_generate_message_too_long_with_default_message
849
+ assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10)
850
+ end
851
+
852
+ def test_generate_message_too_long_with_custom_message
853
+ assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10)
854
+ end
855
+
856
+ # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin)
857
+ def test_generate_message_too_short_with_default_message
858
+ assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10)
859
+ end
860
+
861
+ def test_generate_message_too_short_with_custom_message
862
+ assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10)
863
+ end
864
+
865
+ # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value)
866
+ def test_generate_message_wrong_length_with_default_message
867
+ assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10)
868
+ end
869
+
870
+ def test_generate_message_wrong_length_with_custom_message
871
+ assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10)
872
+ end
873
+
874
+ # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message])
875
+ def test_generate_message_taken_with_default_message
876
+ assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title')
877
+ end
878
+
879
+ def test_generate_message_taken_with_custom_message
880
+ assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title')
881
+ end
882
+
883
+ # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
884
+ def test_generate_message_not_a_number_with_default_message
885
+ assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title')
886
+ end
887
+
888
+ def test_generate_message_not_a_number_with_custom_message
889
+ assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title')
890
+ end
891
+
892
+ # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message])
893
+ def test_generate_message_greater_than_with_default_message
894
+ assert_equal "must be greater than 10", @topic.errors.generate_message(:title, :greater_than, :default => nil, :value => 'title', :count => 10)
895
+ end
896
+
897
+ def test_generate_message_greater_than_or_equal_to_with_default_message
898
+ assert_equal "must be greater than or equal to 10", @topic.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10)
899
+ end
900
+
901
+ def test_generate_message_equal_to_with_default_message
902
+ assert_equal "must be equal to 10", @topic.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10)
903
+ end
904
+
905
+ def test_generate_message_less_than_with_default_message
906
+ assert_equal "must be less than 10", @topic.errors.generate_message(:title, :less_than, :default => nil, :value => 'title', :count => 10)
907
+ end
908
+
909
+ def test_generate_message_less_than_or_equal_to_with_default_message
910
+ assert_equal "must be less than or equal to 10", @topic.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10)
911
+ end
912
+
913
+ def test_generate_message_odd_with_default_message
914
+ assert_equal "must be odd", @topic.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10)
915
+ end
916
+
917
+ def test_generate_message_even_with_default_message
918
+ assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10)
919
+ end
920
+
921
+ end