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
@@ -2,15 +2,18 @@ require "cases/helper"
2
2
  require 'models/post'
3
3
  require 'models/person'
4
4
  require 'models/reader'
5
+ require 'models/comment'
5
6
 
6
7
  class HasManyThroughAssociationsTest < ActiveRecord::TestCase
7
- fixtures :posts, :readers, :people
8
+ fixtures :posts, :readers, :people, :comments, :authors
8
9
 
9
10
  def test_associate_existing
10
11
  assert_queries(2) { posts(:thinking);people(:david) }
11
-
12
+
13
+ posts(:thinking).people
14
+
12
15
  assert_queries(1) do
13
- posts(:thinking).people << people(:david)
16
+ posts(:thinking).people << people(:david)
14
17
  end
15
18
 
16
19
  assert_queries(1) do
@@ -197,4 +200,48 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
197
200
  def test_count_with_include_should_alias_join_table
198
201
  assert_equal 2, people(:michael).posts.count(:include => :readers)
199
202
  end
203
+
204
+ def test_get_ids
205
+ assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
206
+ end
207
+
208
+ def test_get_ids_for_loaded_associations
209
+ person = people(:michael)
210
+ person.posts(true)
211
+ assert_queries(0) do
212
+ person.post_ids
213
+ person.post_ids
214
+ end
215
+ end
216
+
217
+ def test_get_ids_for_unloaded_associations_does_not_load_them
218
+ person = people(:michael)
219
+ assert !person.posts.loaded?
220
+ assert_equal [posts(:welcome).id, posts(:authorless).id].sort, person.post_ids.sort
221
+ assert !person.posts.loaded?
222
+ end
223
+
224
+ uses_mocha 'mocking Tag.transaction' do
225
+ def test_association_proxy_transaction_method_starts_transaction_in_association_class
226
+ Tag.expects(:transaction)
227
+ Post.find(:first).tags.transaction do
228
+ # nothing
229
+ end
230
+ end
231
+ end
232
+
233
+ def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
234
+ author = authors(:mary)
235
+ post = Post.create!(:title => "TITLE", :body => "BODY")
236
+ assert_equal [], post.author_favorites
237
+ end
238
+
239
+ def test_has_many_association_through_a_belongs_to_association
240
+ author = authors(:mary)
241
+ post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
242
+ author.author_favorites.create(:favorite_author_id => 1)
243
+ author.author_favorites.create(:favorite_author_id => 2)
244
+ author.author_favorites.create(:favorite_author_id => 3)
245
+ assert_equal post.author.author_favorites, post.author_favorites
246
+ end
200
247
  end
@@ -29,6 +29,13 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
29
29
  assert_equal Firm.find(1, :include => :account_with_select).account_with_select.attributes.size, 2
30
30
  end
31
31
 
32
+ def test_finding_using_primary_key
33
+ firm = companies(:first_firm)
34
+ assert_equal Account.find_by_firm_id(firm.id), firm.account
35
+ firm.firm_id = companies(:rails_core).id
36
+ assert_equal accounts(:rails_core_account), firm.account_using_primary_key
37
+ end
38
+
32
39
  def test_can_marshal_has_one_association_with_nil_target
33
40
  firm = Firm.new
34
41
  assert_nothing_raised do
@@ -342,4 +349,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
342
349
  assert companies(:first_firm).readonly_account.readonly?
343
350
  end
344
351
 
352
+ def test_has_one_proxy_should_not_respond_to_private_methods
353
+ assert_raises(NoMethodError) { accounts(:signals37).private_method }
354
+ assert_raises(NoMethodError) { companies(:first_firm).account.private_method }
355
+ end
356
+
357
+ def test_has_one_proxy_should_respond_to_private_methods_via_send
358
+ accounts(:signals37).send(:private_method)
359
+ companies(:first_firm).account.send(:private_method)
360
+ end
361
+
345
362
  end
@@ -3,9 +3,11 @@ require 'models/club'
3
3
  require 'models/member'
4
4
  require 'models/membership'
5
5
  require 'models/sponsor'
6
+ require 'models/organization'
7
+ require 'models/member_detail'
6
8
 
7
9
  class HasOneThroughAssociationsTest < ActiveRecord::TestCase
8
- fixtures :members, :clubs, :memberships, :sponsors
10
+ fixtures :members, :clubs, :memberships, :sponsors, :organizations
9
11
 
10
12
  def setup
11
13
  @member = members(:groucho)
@@ -110,4 +112,50 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
110
112
  new_member.club = new_club = Club.create(:name => "LRUG")
111
113
  assert_equal new_club, new_member.club.target
112
114
  end
115
+
116
+ def test_has_one_through_proxy_should_not_respond_to_private_methods
117
+ assert_raises(NoMethodError) { clubs(:moustache_club).private_method }
118
+ assert_raises(NoMethodError) { @member.club.private_method }
119
+ end
120
+
121
+ def test_has_one_through_proxy_should_respond_to_private_methods_via_send
122
+ clubs(:moustache_club).send(:private_method)
123
+ @member.club.send(:private_method)
124
+ end
125
+
126
+ def test_assigning_to_has_one_through_preserves_decorated_join_record
127
+ @organization = organizations(:nsa)
128
+ assert_difference 'MemberDetail.count', 1 do
129
+ @member_detail = MemberDetail.new(:extra_data => 'Extra')
130
+ @member.member_detail = @member_detail
131
+ @member.organization = @organization
132
+ end
133
+ assert_equal @organization, @member.organization
134
+ assert @organization.members.include?(@member)
135
+ assert_equal 'Extra', @member.member_detail.extra_data
136
+ end
137
+
138
+ def test_reassigning_has_one_through
139
+ @organization = organizations(:nsa)
140
+ @new_organization = organizations(:discordians)
141
+
142
+ assert_difference 'MemberDetail.count', 1 do
143
+ @member_detail = MemberDetail.new(:extra_data => 'Extra')
144
+ @member.member_detail = @member_detail
145
+ @member.organization = @organization
146
+ end
147
+ assert_equal @organization, @member.organization
148
+ assert_equal 'Extra', @member.member_detail.extra_data
149
+ assert @organization.members.include?(@member)
150
+ assert !@new_organization.members.include?(@member)
151
+
152
+ assert_no_difference 'MemberDetail.count' do
153
+ @member.organization = @new_organization
154
+ end
155
+ assert_equal @new_organization, @member.organization
156
+ assert_equal 'Extra', @member.member_detail.extra_data
157
+ assert !@organization.members.include?(@member)
158
+ assert @new_organization.members.include?(@member)
159
+ end
160
+
113
161
  end
File without changes
@@ -1,5 +1,6 @@
1
1
  require "cases/helper"
2
2
  require 'models/topic'
3
+ require 'models/minimalistic'
3
4
 
4
5
  class AttributeMethodsTest < ActiveRecord::TestCase
5
6
  fixtures :topics
@@ -57,19 +58,19 @@ class AttributeMethodsTest < ActiveRecord::TestCase
57
58
 
58
59
  def test_kernel_methods_not_implemented_in_activerecord
59
60
  %w(test name display y).each do |method|
60
- assert_equal false, ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
61
+ assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
61
62
  end
62
63
  end
63
64
 
64
65
  def test_primary_key_implemented
65
- assert_equal true, Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
66
+ assert Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
66
67
  end
67
68
 
68
69
  def test_defined_kernel_methods_implemented_in_model
69
70
  %w(test name display y).each do |method|
70
71
  klass = Class.new ActiveRecord::Base
71
72
  klass.class_eval "def #{method}() 'defined #{method}' end"
72
- assert_equal true, klass.instance_method_already_implemented?(method), "##{method} is not defined"
73
+ assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
73
74
  end
74
75
  end
75
76
 
@@ -79,7 +80,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
79
80
  abstract.class_eval "def #{method}() 'defined #{method}' end"
80
81
  abstract.abstract_class = true
81
82
  klass = Class.new abstract
82
- assert_equal true, klass.instance_method_already_implemented?(method), "##{method} is not defined"
83
+ assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
83
84
  end
84
85
  end
85
86
 
@@ -219,6 +220,51 @@ class AttributeMethodsTest < ActiveRecord::TestCase
219
220
  end
220
221
  end
221
222
 
223
+ def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
224
+ Topic.skip_time_zone_conversion_for_attributes = [:field_a]
225
+ Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
226
+
227
+ assert_equal [:field_a], Topic.skip_time_zone_conversion_for_attributes
228
+ assert_equal [:field_b], Minimalistic.skip_time_zone_conversion_for_attributes
229
+ end
230
+
231
+ def test_read_attributes_respect_access_control
232
+ privatize("title")
233
+
234
+ topic = @target.new(:title => "The pros and cons of programming naked.")
235
+ assert !topic.respond_to?(:title)
236
+ exception = assert_raise(NoMethodError) { topic.title }
237
+ assert_equal "Attempt to call private method", exception.message
238
+ assert_equal "I'm private", topic.send(:title)
239
+ end
240
+
241
+ def test_write_attributes_respect_access_control
242
+ privatize("title=(value)")
243
+
244
+ topic = @target.new
245
+ assert !topic.respond_to?(:title=)
246
+ exception = assert_raise(NoMethodError) { topic.title = "Pants"}
247
+ assert_equal "Attempt to call private method", exception.message
248
+ topic.send(:title=, "Very large pants")
249
+ end
250
+
251
+ def test_question_attributes_respect_access_control
252
+ privatize("title?")
253
+
254
+ topic = @target.new(:title => "Isaac Newton's pants")
255
+ assert !topic.respond_to?(:title?)
256
+ exception = assert_raise(NoMethodError) { topic.title? }
257
+ assert_equal "Attempt to call private method", exception.message
258
+ assert topic.send(:title?)
259
+ end
260
+
261
+ def test_bulk_update_respects_access_control
262
+ privatize("title=(value)")
263
+
264
+ assert_raise(ActiveRecord::UnknownAttributeError) { topic = @target.new(:title => "Rants about pants") }
265
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
266
+ end
267
+
222
268
  private
223
269
  def time_related_columns_on_topic
224
270
  Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
@@ -235,4 +281,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase
235
281
  Time.zone = old_zone
236
282
  ActiveRecord::Base.time_zone_aware_attributes = old_tz
237
283
  end
284
+
285
+ def privatize(method_signature)
286
+ @target.class_eval <<-private_method
287
+ private
288
+ def #{method_signature}
289
+ "I'm private"
290
+ end
291
+ private_method
292
+ end
238
293
  end
@@ -76,7 +76,7 @@ class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
76
76
  end
77
77
 
78
78
  class BasicsTest < ActiveRecord::TestCase
79
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories
79
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
80
80
 
81
81
  def test_table_exists
82
82
  assert !NonExistentTable.table_exists?
@@ -138,7 +138,7 @@ class BasicsTest < ActiveRecord::TestCase
138
138
  if current_adapter?(:MysqlAdapter)
139
139
  def test_read_attributes_before_type_cast_on_boolean
140
140
  bool = Booleantest.create({ "value" => false })
141
- assert_equal 0, bool.attributes_before_type_cast["value"]
141
+ assert_equal "0", bool.reload.attributes_before_type_cast["value"]
142
142
  end
143
143
  end
144
144
 
@@ -428,9 +428,6 @@ class BasicsTest < ActiveRecord::TestCase
428
428
  end
429
429
 
430
430
  def test_preserving_date_objects
431
- # SQL Server doesn't have a separate column type just for dates, so all are returned as time
432
- return true if current_adapter?(:SQLServerAdapter)
433
-
434
431
  if current_adapter?(:SybaseAdapter, :OracleAdapter)
435
432
  # Sybase ctlib does not (yet?) support the date type; use datetime instead.
436
433
  # Oracle treats all dates/times as Time.
@@ -472,6 +469,18 @@ class BasicsTest < ActiveRecord::TestCase
472
469
  assert topic.instance_variable_get("@custom_approved")
473
470
  end
474
471
 
472
+ def test_delete
473
+ topic = Topic.find(1)
474
+ assert_equal topic, topic.delete, 'topic.delete did not return self'
475
+ assert topic.frozen?, 'topic not frozen after delete'
476
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
477
+ end
478
+
479
+ def test_delete_doesnt_run_callbacks
480
+ Topic.find(1).delete
481
+ assert_not_nil Topic.find(2)
482
+ end
483
+
475
484
  def test_destroy
476
485
  topic = Topic.find(1)
477
486
  assert_equal topic, topic.destroy, 'topic.destroy did not return self'
@@ -664,10 +673,21 @@ class BasicsTest < ActiveRecord::TestCase
664
673
  end
665
674
  end
666
675
 
667
- def test_update_all_ignores_order_limit_from_association
668
- author = Author.find(1)
676
+ def test_update_all_ignores_order_without_limit_from_association
677
+ author = authors(:david)
678
+ assert_nothing_raised do
679
+ assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
680
+ end
681
+ end
682
+
683
+ def test_update_all_with_order_and_limit_updates_subset_only
684
+ author = authors(:david)
669
685
  assert_nothing_raised do
670
- assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all("body = 'bulk update!'")
686
+ assert_equal 1, author.posts_sorted_by_id_limited.size
687
+ assert_equal 2, author.posts_sorted_by_id_limited.find(:all, :limit => 2).size
688
+ assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ])
689
+ assert_equal "bulk update!", posts(:welcome).body
690
+ assert_not_equal "bulk update!", posts(:thinking).body
671
691
  end
672
692
  end
673
693
 
@@ -754,8 +774,8 @@ class BasicsTest < ActiveRecord::TestCase
754
774
  end
755
775
  end
756
776
 
757
- # Oracle, SQLServer, and Sybase do not have a TIME datatype.
758
- unless current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
777
+ # Oracle, and Sybase do not have a TIME datatype.
778
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
759
779
  def test_utc_as_time_zone
760
780
  Topic.default_timezone = :utc
761
781
  attributes = { "bonus_time" => "5:42:00AM" }
@@ -809,6 +829,20 @@ class BasicsTest < ActiveRecord::TestCase
809
829
  assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
810
830
  end
811
831
 
832
+ def test_delete_new_record
833
+ client = Client.new
834
+ client.delete
835
+ assert client.frozen?
836
+ end
837
+
838
+ def test_delete_record_with_associations
839
+ client = Client.find(3)
840
+ client.delete
841
+ assert client.frozen?
842
+ assert_kind_of Firm, client.firm
843
+ assert_raises(ActiveSupport::FrozenObjectError) { client.name = "something else" }
844
+ end
845
+
812
846
  def test_destroy_new_record
813
847
  client = Client.new
814
848
  client.destroy
@@ -880,7 +914,7 @@ class BasicsTest < ActiveRecord::TestCase
880
914
 
881
915
  def test_mass_assignment_protection_against_class_attribute_writers
882
916
  [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
883
- :default_timezone, :allow_concurrency, :schema_format, :verification_timeout, :lock_optimistically, :record_timestamps].each do |method|
917
+ :default_timezone, :schema_format, :lock_optimistically, :record_timestamps].each do |method|
884
918
  assert Task.respond_to?(method)
885
919
  assert Task.respond_to?("#{method}=")
886
920
  assert Task.new.respond_to?(method)
@@ -904,6 +938,14 @@ class BasicsTest < ActiveRecord::TestCase
904
938
  assert_nil keyboard.id
905
939
  end
906
940
 
941
+ def test_mass_assigning_invalid_attribute
942
+ firm = Firm.new
943
+
944
+ assert_raises(ActiveRecord::UnknownAttributeError) do
945
+ firm.attributes = { "id" => 5, "type" => "Client", "i_dont_even_exist" => 20 }
946
+ end
947
+ end
948
+
907
949
  def test_mass_assignment_protection_on_defaults
908
950
  firm = Firm.new
909
951
  firm.attributes = { "id" => 5, "type" => "Client" }
@@ -1112,8 +1154,8 @@ class BasicsTest < ActiveRecord::TestCase
1112
1154
  end
1113
1155
 
1114
1156
  def test_attributes_on_dummy_time
1115
- # Oracle, SQL Server, and Sybase do not have a TIME datatype.
1116
- return true if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
1157
+ # Oracle, and Sybase do not have a TIME datatype.
1158
+ return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
1117
1159
 
1118
1160
  attributes = {
1119
1161
  "bonus_time" => "5:42:00AM"
@@ -1124,11 +1166,15 @@ class BasicsTest < ActiveRecord::TestCase
1124
1166
  end
1125
1167
 
1126
1168
  def test_boolean
1169
+ b_nil = Booleantest.create({ "value" => nil })
1170
+ nil_id = b_nil.id
1127
1171
  b_false = Booleantest.create({ "value" => false })
1128
1172
  false_id = b_false.id
1129
1173
  b_true = Booleantest.create({ "value" => true })
1130
1174
  true_id = b_true.id
1131
1175
 
1176
+ b_nil = Booleantest.find(nil_id)
1177
+ assert_nil b_nil.value
1132
1178
  b_false = Booleantest.find(false_id)
1133
1179
  assert !b_false.value?
1134
1180
  b_true = Booleantest.find(true_id)
@@ -1136,11 +1182,15 @@ class BasicsTest < ActiveRecord::TestCase
1136
1182
  end
1137
1183
 
1138
1184
  def test_boolean_cast_from_string
1185
+ b_blank = Booleantest.create({ "value" => "" })
1186
+ blank_id = b_blank.id
1139
1187
  b_false = Booleantest.create({ "value" => "0" })
1140
1188
  false_id = b_false.id
1141
1189
  b_true = Booleantest.create({ "value" => "1" })
1142
1190
  true_id = b_true.id
1143
1191
 
1192
+ b_blank = Booleantest.find(blank_id)
1193
+ assert_nil b_blank.value
1144
1194
  b_false = Booleantest.find(false_id)
1145
1195
  assert !b_false.value?
1146
1196
  b_true = Booleantest.find(true_id)
@@ -1376,6 +1426,12 @@ class BasicsTest < ActiveRecord::TestCase
1376
1426
  topic = Topic.create("content" => myobj).reload
1377
1427
  assert_equal(myobj, topic.content)
1378
1428
  end
1429
+
1430
+ def test_serialized_string_attribute
1431
+ myobj = "Yes"
1432
+ topic = Topic.create("content" => myobj).reload
1433
+ assert_equal(myobj, topic.content)
1434
+ end
1379
1435
 
1380
1436
  def test_nil_serialized_attribute_with_class_constraint
1381
1437
  myobj = MyObject.new('value1', 'value2')
@@ -1411,15 +1467,17 @@ class BasicsTest < ActiveRecord::TestCase
1411
1467
 
1412
1468
  if RUBY_VERSION < '1.9'
1413
1469
  def test_quote_chars
1414
- str = 'The Narrator'
1415
- topic = Topic.create(:author_name => str)
1416
- assert_equal str, topic.author_name
1470
+ with_kcode('UTF8') do
1471
+ str = 'The Narrator'
1472
+ topic = Topic.create(:author_name => str)
1473
+ assert_equal str, topic.author_name
1417
1474
 
1418
- assert_kind_of ActiveSupport::Multibyte::Chars, str.chars
1419
- topic = Topic.find_by_author_name(str.chars)
1475
+ assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
1476
+ topic = Topic.find_by_author_name(str.mb_chars)
1420
1477
 
1421
- assert_kind_of Topic, topic
1422
- assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1478
+ assert_kind_of Topic, topic
1479
+ assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1480
+ end
1423
1481
  end
1424
1482
  end
1425
1483
 
@@ -1813,7 +1871,7 @@ class BasicsTest < ActiveRecord::TestCase
1813
1871
  assert_equal "integer", xml.elements["//parent-id"].attributes['type']
1814
1872
  assert_equal "true", xml.elements["//parent-id"].attributes['nil']
1815
1873
 
1816
- if current_adapter?(:SybaseAdapter, :SQLServerAdapter, :OracleAdapter)
1874
+ if current_adapter?(:SybaseAdapter, :OracleAdapter)
1817
1875
  assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
1818
1876
  assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
1819
1877
  else
@@ -2012,4 +2070,18 @@ class BasicsTest < ActiveRecord::TestCase
2012
2070
  ensure
2013
2071
  ActiveRecord::Base.logger = original_logger
2014
2072
  end
2073
+
2074
+ private
2075
+ def with_kcode(kcode)
2076
+ if RUBY_VERSION < '1.9'
2077
+ orig_kcode, $KCODE = $KCODE, kcode
2078
+ begin
2079
+ yield
2080
+ ensure
2081
+ $KCODE = orig_kcode
2082
+ end
2083
+ else
2084
+ yield
2085
+ end
2086
+ end
2015
2087
  end