activerecord 2.0.5 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (289) hide show
  1. data/CHANGELOG +168 -6
  2. data/README +27 -22
  3. data/RUNNING_UNIT_TESTS +7 -4
  4. data/Rakefile +22 -25
  5. data/lib/active_record.rb +8 -2
  6. data/lib/active_record/aggregations.rb +21 -12
  7. data/lib/active_record/association_preload.rb +277 -0
  8. data/lib/active_record/associations.rb +481 -295
  9. data/lib/active_record/associations/association_collection.rb +162 -37
  10. data/lib/active_record/associations/association_proxy.rb +71 -7
  11. data/lib/active_record/associations/belongs_to_association.rb +5 -3
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -6
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -64
  14. data/lib/active_record/associations/has_many_association.rb +8 -73
  15. data/lib/active_record/associations/has_many_through_association.rb +68 -117
  16. data/lib/active_record/associations/has_one_association.rb +7 -5
  17. data/lib/active_record/associations/has_one_through_association.rb +28 -0
  18. data/lib/active_record/attribute_methods.rb +69 -19
  19. data/lib/active_record/base.rb +496 -275
  20. data/lib/active_record/calculations.rb +28 -21
  21. data/lib/active_record/callbacks.rb +9 -38
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +232 -45
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +141 -27
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -13
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +57 -24
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +143 -42
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  31. data/lib/active_record/connection_adapters/sqlite_adapter.rb +18 -10
  32. data/lib/active_record/dirty.rb +158 -0
  33. data/lib/active_record/fixtures.rb +121 -156
  34. data/lib/active_record/locking/optimistic.rb +14 -11
  35. data/lib/active_record/locking/pessimistic.rb +2 -2
  36. data/lib/active_record/migration.rb +157 -77
  37. data/lib/active_record/named_scope.rb +163 -0
  38. data/lib/active_record/observer.rb +19 -5
  39. data/lib/active_record/reflection.rb +34 -14
  40. data/lib/active_record/schema.rb +7 -14
  41. data/lib/active_record/schema_dumper.rb +4 -4
  42. data/lib/active_record/serialization.rb +5 -5
  43. data/lib/active_record/serializers/json_serializer.rb +37 -28
  44. data/lib/active_record/serializers/xml_serializer.rb +52 -29
  45. data/lib/active_record/test_case.rb +36 -0
  46. data/lib/active_record/timestamp.rb +4 -4
  47. data/lib/active_record/transactions.rb +3 -3
  48. data/lib/active_record/validations.rb +182 -248
  49. data/lib/active_record/version.rb +2 -2
  50. data/test/{fixtures → assets}/example.log +0 -0
  51. data/test/{fixtures → assets}/flowers.jpg +0 -0
  52. data/test/cases/aaa_create_tables_test.rb +24 -0
  53. data/test/cases/active_schema_test_mysql.rb +95 -0
  54. data/test/cases/active_schema_test_postgresql.rb +24 -0
  55. data/test/{adapter_test.rb → cases/adapter_test.rb} +15 -14
  56. data/test/{adapter_test_sqlserver.rb → cases/adapter_test_sqlserver.rb} +95 -95
  57. data/test/{aggregations_test.rb → cases/aggregations_test.rb} +20 -20
  58. data/test/{ar_schema_test.rb → cases/ar_schema_test.rb} +6 -6
  59. data/test/cases/associations/belongs_to_associations_test.rb +412 -0
  60. data/test/{associations → cases/associations}/callbacks_test.rb +24 -10
  61. data/test/{associations → cases/associations}/cascaded_eager_loading_test.rb +18 -17
  62. data/test/cases/associations/eager_load_nested_include_test.rb +83 -0
  63. data/test/{associations → cases/associations}/eager_singularization_test.rb +5 -5
  64. data/test/{associations → cases/associations}/eager_test.rb +216 -51
  65. data/test/{associations → cases/associations}/extension_test.rb +8 -8
  66. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +684 -0
  67. data/test/cases/associations/has_many_associations_test.rb +932 -0
  68. data/test/cases/associations/has_many_through_associations_test.rb +190 -0
  69. data/test/cases/associations/has_one_associations_test.rb +323 -0
  70. data/test/cases/associations/has_one_through_associations_test.rb +74 -0
  71. data/test/{associations → cases/associations}/inner_join_association_test.rb +20 -20
  72. data/test/{associations → cases/associations}/join_model_test.rb +175 -35
  73. data/test/cases/associations_test.rb +262 -0
  74. data/test/{attribute_methods_test.rb → cases/attribute_methods_test.rb} +103 -11
  75. data/test/{base_test.rb → cases/base_test.rb} +338 -191
  76. data/test/{binary_test.rb → cases/binary_test.rb} +6 -4
  77. data/test/{calculations_test.rb → cases/calculations_test.rb} +35 -23
  78. data/test/{callbacks_test.rb → cases/callbacks_test.rb} +7 -7
  79. data/test/{class_inheritable_attributes_test.rb → cases/class_inheritable_attributes_test.rb} +3 -3
  80. data/test/{column_alias_test.rb → cases/column_alias_test.rb} +3 -3
  81. data/test/{connection_test_firebird.rb → cases/connection_test_firebird.rb} +2 -2
  82. data/test/{connection_test_mysql.rb → cases/connection_test_mysql.rb} +2 -2
  83. data/test/{copy_table_test_sqlite.rb → cases/copy_table_test_sqlite.rb} +13 -13
  84. data/test/{datatype_test_postgresql.rb → cases/datatype_test_postgresql.rb} +8 -8
  85. data/test/{date_time_test.rb → cases/date_time_test.rb} +5 -5
  86. data/test/{default_test_firebird.rb → cases/default_test_firebird.rb} +3 -3
  87. data/test/{defaults_test.rb → cases/defaults_test.rb} +8 -6
  88. data/test/{deprecated_finder_test.rb → cases/deprecated_finder_test.rb} +3 -3
  89. data/test/cases/dirty_test.rb +163 -0
  90. data/test/cases/finder_respond_to_test.rb +76 -0
  91. data/test/{finder_test.rb → cases/finder_test.rb} +266 -33
  92. data/test/{fixtures_test.rb → cases/fixtures_test.rb} +88 -72
  93. data/test/cases/helper.rb +47 -0
  94. data/test/{inheritance_test.rb → cases/inheritance_test.rb} +61 -17
  95. data/test/cases/invalid_date_test.rb +24 -0
  96. data/test/{json_serialization_test.rb → cases/json_serialization_test.rb} +36 -11
  97. data/test/{lifecycle_test.rb → cases/lifecycle_test.rb} +16 -13
  98. data/test/{locking_test.rb → cases/locking_test.rb} +17 -10
  99. data/test/{method_scoping_test.rb → cases/method_scoping_test.rb} +75 -39
  100. data/test/{migration_test.rb → cases/migration_test.rb} +420 -80
  101. data/test/{migration_test_firebird.rb → cases/migration_test_firebird.rb} +3 -3
  102. data/test/{mixin_test.rb → cases/mixin_test.rb} +7 -6
  103. data/test/{modules_test.rb → cases/modules_test.rb} +11 -6
  104. data/test/{multiple_db_test.rb → cases/multiple_db_test.rb} +5 -5
  105. data/test/cases/named_scope_test.rb +157 -0
  106. data/test/{pk_test.rb → cases/pk_test.rb} +10 -10
  107. data/test/{query_cache_test.rb → cases/query_cache_test.rb} +12 -10
  108. data/test/{readonly_test.rb → cases/readonly_test.rb} +11 -11
  109. data/test/{reflection_test.rb → cases/reflection_test.rb} +15 -14
  110. data/test/{reserved_word_test_mysql.rb → cases/reserved_word_test_mysql.rb} +4 -5
  111. data/test/{schema_authorization_test_postgresql.rb → cases/schema_authorization_test_postgresql.rb} +5 -5
  112. data/test/cases/schema_dumper_test.rb +138 -0
  113. data/test/cases/schema_test_postgresql.rb +102 -0
  114. data/test/{serialization_test.rb → cases/serialization_test.rb} +7 -7
  115. data/test/{synonym_test_oracle.rb → cases/synonym_test_oracle.rb} +5 -5
  116. data/test/{table_name_test_sqlserver.rb → cases/table_name_test_sqlserver.rb} +3 -3
  117. data/test/{threaded_connections_test.rb → cases/threaded_connections_test.rb} +7 -7
  118. data/test/{transactions_test.rb → cases/transactions_test.rb} +31 -5
  119. data/test/{unconnected_test.rb → cases/unconnected_test.rb} +2 -2
  120. data/test/{validations_test.rb → cases/validations_test.rb} +141 -39
  121. data/test/{xml_serialization_test.rb → cases/xml_serialization_test.rb} +12 -12
  122. data/test/config.rb +5 -0
  123. data/test/connections/native_db2/connection.rb +1 -1
  124. data/test/connections/native_firebird/connection.rb +1 -1
  125. data/test/connections/native_frontbase/connection.rb +1 -1
  126. data/test/connections/native_mysql/connection.rb +1 -1
  127. data/test/connections/native_openbase/connection.rb +1 -1
  128. data/test/connections/native_oracle/connection.rb +1 -1
  129. data/test/connections/native_postgresql/connection.rb +1 -3
  130. data/test/connections/native_sqlite/connection.rb +2 -2
  131. data/test/connections/native_sqlite3/connection.rb +2 -2
  132. data/test/connections/native_sqlite3/in_memory_connection.rb +3 -3
  133. data/test/connections/native_sybase/connection.rb +1 -1
  134. data/test/fixtures/author_addresses.yml +5 -0
  135. data/test/fixtures/authors.yml +2 -0
  136. data/test/fixtures/clubs.yml +6 -0
  137. data/test/fixtures/jobs.yml +7 -0
  138. data/test/fixtures/members.yml +4 -0
  139. data/test/fixtures/memberships.yml +20 -0
  140. data/test/fixtures/owners.yml +7 -0
  141. data/test/fixtures/people.yml +4 -1
  142. data/test/fixtures/pets.yml +14 -0
  143. data/test/fixtures/posts.yml +1 -0
  144. data/test/fixtures/price_estimates.yml +7 -0
  145. data/test/fixtures/readers.yml +5 -0
  146. data/test/fixtures/references.yml +17 -0
  147. data/test/fixtures/sponsors.yml +9 -0
  148. data/test/fixtures/subscribers.yml +7 -0
  149. data/test/fixtures/subscriptions.yml +12 -0
  150. data/test/fixtures/taggings.yml +4 -1
  151. data/test/fixtures/topics.yml +22 -2
  152. data/test/fixtures/warehouse-things.yml +3 -0
  153. data/test/{fixtures/migrations_with_decimal → migrations/decimal}/1_give_me_big_numbers.rb +0 -0
  154. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/1_people_have_last_names.rb +1 -1
  155. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/2_we_need_reminders.rb +1 -1
  156. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/3_foo.rb +0 -0
  157. data/test/{fixtures/migrations → migrations/duplicate}/3_innocent_jointable.rb +0 -0
  158. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  159. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  160. data/test/{fixtures/migrations_with_duplicate → migrations/interleaved/pass_1}/3_innocent_jointable.rb +0 -0
  161. data/test/{fixtures/migrations → migrations/interleaved/pass_2}/1_people_have_last_names.rb +1 -1
  162. data/test/{fixtures/migrations_with_missing_versions/4_innocent_jointable.rb → migrations/interleaved/pass_2/3_innocent_jointable.rb} +0 -0
  163. data/test/{fixtures/migrations_with_missing_versions → migrations/interleaved/pass_3}/1_people_have_last_names.rb +1 -1
  164. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  165. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  166. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/1000_people_have_middle_names.rb +1 -1
  167. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  168. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/3_we_need_reminders.rb +1 -1
  169. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  170. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  171. data/test/{fixtures/migrations → migrations/valid}/2_we_need_reminders.rb +1 -1
  172. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  173. data/test/{fixtures → models}/author.rb +28 -4
  174. data/test/{fixtures → models}/auto_id.rb +0 -0
  175. data/test/{fixtures → models}/binary.rb +0 -0
  176. data/test/{fixtures → models}/book.rb +0 -0
  177. data/test/{fixtures → models}/categorization.rb +0 -0
  178. data/test/{fixtures → models}/category.rb +8 -5
  179. data/test/{fixtures → models}/citation.rb +0 -0
  180. data/test/models/club.rb +7 -0
  181. data/test/{fixtures → models}/column_name.rb +0 -0
  182. data/test/{fixtures → models}/comment.rb +5 -3
  183. data/test/{fixtures → models}/company.rb +15 -6
  184. data/test/{fixtures → models}/company_in_module.rb +5 -3
  185. data/test/{fixtures → models}/computer.rb +0 -1
  186. data/test/{fixtures → models}/contact.rb +1 -1
  187. data/test/{fixtures → models}/course.rb +0 -0
  188. data/test/{fixtures → models}/customer.rb +8 -8
  189. data/test/{fixtures → models}/default.rb +0 -0
  190. data/test/{fixtures → models}/developer.rb +14 -10
  191. data/test/{fixtures → models}/edge.rb +0 -0
  192. data/test/{fixtures → models}/entrant.rb +0 -0
  193. data/test/models/guid.rb +2 -0
  194. data/test/{fixtures → models}/item.rb +0 -0
  195. data/test/models/job.rb +5 -0
  196. data/test/{fixtures → models}/joke.rb +0 -0
  197. data/test/{fixtures → models}/keyboard.rb +0 -0
  198. data/test/{fixtures → models}/legacy_thing.rb +0 -0
  199. data/test/{fixtures → models}/matey.rb +0 -0
  200. data/test/models/member.rb +9 -0
  201. data/test/models/membership.rb +9 -0
  202. data/test/{fixtures → models}/minimalistic.rb +0 -0
  203. data/test/{fixtures → models}/mixed_case_monkey.rb +0 -0
  204. data/test/{fixtures → models}/movie.rb +0 -0
  205. data/test/{fixtures → models}/order.rb +2 -2
  206. data/test/models/owner.rb +4 -0
  207. data/test/{fixtures → models}/parrot.rb +0 -0
  208. data/test/models/person.rb +10 -0
  209. data/test/models/pet.rb +4 -0
  210. data/test/models/pirate.rb +9 -0
  211. data/test/{fixtures → models}/post.rb +23 -2
  212. data/test/models/price_estimate.rb +3 -0
  213. data/test/{fixtures → models}/project.rb +1 -0
  214. data/test/{fixtures → models}/reader.rb +0 -0
  215. data/test/models/reference.rb +4 -0
  216. data/test/{fixtures → models}/reply.rb +7 -5
  217. data/test/{fixtures → models}/ship.rb +0 -0
  218. data/test/models/sponsor.rb +4 -0
  219. data/test/{fixtures → models}/subject.rb +0 -0
  220. data/test/{fixtures → models}/subscriber.rb +2 -0
  221. data/test/models/subscription.rb +4 -0
  222. data/test/{fixtures → models}/tag.rb +0 -0
  223. data/test/{fixtures → models}/tagging.rb +0 -0
  224. data/test/{fixtures → models}/task.rb +0 -0
  225. data/test/{fixtures → models}/topic.rb +32 -4
  226. data/test/{fixtures → models}/treasure.rb +2 -0
  227. data/test/{fixtures → models}/vertex.rb +0 -0
  228. data/test/models/warehouse_thing.rb +5 -0
  229. data/test/schema/mysql_specific_schema.rb +12 -0
  230. data/test/schema/postgresql_specific_schema.rb +103 -0
  231. data/test/schema/schema.rb +421 -0
  232. data/test/schema/schema2.rb +6 -0
  233. data/test/schema/sqlite_specific_schema.rb +25 -0
  234. data/test/schema/sqlserver_specific_schema.rb +5 -0
  235. metadata +192 -176
  236. data/test/aaa_create_tables_test.rb +0 -72
  237. data/test/abstract_unit.rb +0 -84
  238. data/test/active_schema_test_mysql.rb +0 -46
  239. data/test/all.sh +0 -8
  240. data/test/association_inheritance_reload.rb +0 -14
  241. data/test/associations_test.rb +0 -2177
  242. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +0 -1
  243. data/test/fixtures/bad_fixtures/attr_with_spaces +0 -1
  244. data/test/fixtures/bad_fixtures/blank_line +0 -3
  245. data/test/fixtures/bad_fixtures/duplicate_attributes +0 -3
  246. data/test/fixtures/bad_fixtures/missing_value +0 -1
  247. data/test/fixtures/db_definitions/db2.drop.sql +0 -33
  248. data/test/fixtures/db_definitions/db2.sql +0 -235
  249. data/test/fixtures/db_definitions/db22.drop.sql +0 -2
  250. data/test/fixtures/db_definitions/db22.sql +0 -5
  251. data/test/fixtures/db_definitions/firebird.drop.sql +0 -65
  252. data/test/fixtures/db_definitions/firebird.sql +0 -310
  253. data/test/fixtures/db_definitions/firebird2.drop.sql +0 -2
  254. data/test/fixtures/db_definitions/firebird2.sql +0 -6
  255. data/test/fixtures/db_definitions/frontbase.drop.sql +0 -33
  256. data/test/fixtures/db_definitions/frontbase.sql +0 -273
  257. data/test/fixtures/db_definitions/frontbase2.drop.sql +0 -1
  258. data/test/fixtures/db_definitions/frontbase2.sql +0 -4
  259. data/test/fixtures/db_definitions/openbase.drop.sql +0 -2
  260. data/test/fixtures/db_definitions/openbase.sql +0 -318
  261. data/test/fixtures/db_definitions/openbase2.drop.sql +0 -2
  262. data/test/fixtures/db_definitions/openbase2.sql +0 -7
  263. data/test/fixtures/db_definitions/oracle.drop.sql +0 -67
  264. data/test/fixtures/db_definitions/oracle.sql +0 -330
  265. data/test/fixtures/db_definitions/oracle2.drop.sql +0 -2
  266. data/test/fixtures/db_definitions/oracle2.sql +0 -6
  267. data/test/fixtures/db_definitions/postgresql.drop.sql +0 -44
  268. data/test/fixtures/db_definitions/postgresql.sql +0 -292
  269. data/test/fixtures/db_definitions/postgresql2.drop.sql +0 -2
  270. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  271. data/test/fixtures/db_definitions/schema.rb +0 -354
  272. data/test/fixtures/db_definitions/schema2.rb +0 -11
  273. data/test/fixtures/db_definitions/sqlite.drop.sql +0 -33
  274. data/test/fixtures/db_definitions/sqlite.sql +0 -219
  275. data/test/fixtures/db_definitions/sqlite2.drop.sql +0 -2
  276. data/test/fixtures/db_definitions/sqlite2.sql +0 -5
  277. data/test/fixtures/db_definitions/sybase.drop.sql +0 -35
  278. data/test/fixtures/db_definitions/sybase.sql +0 -222
  279. data/test/fixtures/db_definitions/sybase2.drop.sql +0 -4
  280. data/test/fixtures/db_definitions/sybase2.sql +0 -5
  281. data/test/fixtures/developers_projects/david_action_controller +0 -3
  282. data/test/fixtures/developers_projects/david_active_record +0 -3
  283. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  284. data/test/fixtures/person.rb +0 -4
  285. data/test/fixtures/pirate.rb +0 -5
  286. data/test/fixtures/subscribers/first +0 -2
  287. data/test/fixtures/subscribers/second +0 -2
  288. data/test/schema_dumper_test.rb +0 -131
  289. data/test/schema_test_postgresql.rb +0 -64
@@ -0,0 +1,262 @@
1
+ require "cases/helper"
2
+ require 'models/developer'
3
+ require 'models/project'
4
+ require 'models/company'
5
+ require 'models/topic'
6
+ require 'models/reply'
7
+ require 'models/computer'
8
+ require 'models/customer'
9
+ require 'models/order'
10
+ require 'models/categorization'
11
+ require 'models/category'
12
+ require 'models/post'
13
+ require 'models/author'
14
+ require 'models/comment'
15
+ require 'models/tag'
16
+ require 'models/tagging'
17
+ require 'models/person'
18
+ require 'models/reader'
19
+ require 'models/parrot'
20
+ require 'models/pirate'
21
+ require 'models/treasure'
22
+ require 'models/price_estimate'
23
+ require 'models/club'
24
+ require 'models/member'
25
+ require 'models/membership'
26
+ require 'models/sponsor'
27
+
28
+ class AssociationsTest < ActiveRecord::TestCase
29
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects,
30
+ :computers
31
+
32
+ def test_include_with_order_works
33
+ assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
34
+ assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)}
35
+ end
36
+
37
+ def test_bad_collection_keys
38
+ assert_raise(ArgumentError, 'ActiveRecord should have barked on bad collection keys') do
39
+ Class.new(ActiveRecord::Base).has_many(:wheels, :name => 'wheels')
40
+ end
41
+ end
42
+
43
+ def test_should_construct_new_finder_sql_after_create
44
+ person = Person.new :first_name => 'clark'
45
+ assert_equal [], person.readers.find(:all)
46
+ person.save!
47
+ reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
48
+ assert_equal [reader], person.readers.find(:all)
49
+ end
50
+
51
+ def test_force_reload
52
+ firm = Firm.new("name" => "A New Firm, Inc")
53
+ firm.save
54
+ firm.clients.each {|c|} # forcing to load all clients
55
+ assert firm.clients.empty?, "New firm shouldn't have client objects"
56
+ assert_equal 0, firm.clients.size, "New firm should have 0 clients"
57
+
58
+ client = Client.new("name" => "TheClient.com", "firm_id" => firm.id)
59
+ client.save
60
+
61
+ assert firm.clients.empty?, "New firm should have cached no client objects"
62
+ assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"
63
+
64
+ assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
65
+ assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
66
+ end
67
+
68
+ def test_storing_in_pstore
69
+ require "tmpdir"
70
+ store_filename = File.join(Dir.tmpdir, "ar-pstore-association-test")
71
+ File.delete(store_filename) if File.exist?(store_filename)
72
+ require "pstore"
73
+ apple = Firm.create("name" => "Apple")
74
+ natural = Client.new("name" => "Natural Company")
75
+ apple.clients << natural
76
+
77
+ db = PStore.new(store_filename)
78
+ db.transaction do
79
+ db["apple"] = apple
80
+ end
81
+
82
+ db = PStore.new(store_filename)
83
+ db.transaction do
84
+ assert_equal "Natural Company", db["apple"].clients.first.name
85
+ end
86
+ end
87
+ end
88
+
89
+ class AssociationProxyTest < ActiveRecord::TestCase
90
+ fixtures :authors, :posts, :categorizations, :categories, :developers, :projects, :developers_projects
91
+
92
+ def test_proxy_accessors
93
+ welcome = posts(:welcome)
94
+ assert_equal welcome, welcome.author.proxy_owner
95
+ assert_equal welcome.class.reflect_on_association(:author), welcome.author.proxy_reflection
96
+ welcome.author.class # force load target
97
+ assert_equal welcome.author, welcome.author.proxy_target
98
+
99
+ david = authors(:david)
100
+ assert_equal david, david.posts.proxy_owner
101
+ assert_equal david.class.reflect_on_association(:posts), david.posts.proxy_reflection
102
+ david.posts.class # force load target
103
+ assert_equal david.posts, david.posts.proxy_target
104
+
105
+ assert_equal david, david.posts_with_extension.testing_proxy_owner
106
+ assert_equal david.class.reflect_on_association(:posts_with_extension), david.posts_with_extension.testing_proxy_reflection
107
+ david.posts_with_extension.class # force load target
108
+ assert_equal david.posts_with_extension, david.posts_with_extension.testing_proxy_target
109
+ end
110
+
111
+ def test_push_does_not_load_target
112
+ david = authors(:david)
113
+
114
+ david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
115
+ assert !david.posts.loaded?
116
+ assert david.posts.include?(post)
117
+ end
118
+
119
+ def test_push_has_many_through_does_not_load_target
120
+ david = authors(:david)
121
+
122
+ david.categories << categories(:technology)
123
+ assert !david.categories.loaded?
124
+ assert david.categories.include?(categories(:technology))
125
+ end
126
+
127
+ def test_push_followed_by_save_does_not_load_target
128
+ david = authors(:david)
129
+
130
+ david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
131
+ assert !david.posts.loaded?
132
+ david.save
133
+ assert !david.posts.loaded?
134
+ assert david.posts.include?(post)
135
+ end
136
+
137
+ def test_push_does_not_lose_additions_to_new_record
138
+ josh = Author.new(:name => "Josh")
139
+ josh.posts << Post.new(:title => "New on Edge", :body => "More cool stuff!")
140
+ assert josh.posts.loaded?
141
+ assert_equal 1, josh.posts.size
142
+ end
143
+
144
+ def test_save_on_parent_does_not_load_target
145
+ david = developers(:david)
146
+
147
+ assert !david.projects.loaded?
148
+ david.update_attribute(:created_at, Time.now)
149
+ assert !david.projects.loaded?
150
+ end
151
+
152
+ def test_inspect_does_not_reload_a_not_yet_loaded_target
153
+ andreas = Developer.new :name => 'Andreas', :log => 'new developer added'
154
+ assert !andreas.audit_logs.loaded?
155
+ assert_match(/message: "new developer added"/, andreas.audit_logs.inspect)
156
+ end
157
+
158
+ def test_save_on_parent_saves_children
159
+ developer = Developer.create :name => "Bryan", :salary => 50_000
160
+ assert_equal 1, developer.reload.audit_logs.size
161
+ end
162
+
163
+ def test_create_via_association_with_block
164
+ post = authors(:david).posts.create(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
165
+ assert_equal post.title, "New on Edge"
166
+ assert_equal post.body, "More cool stuff!"
167
+ end
168
+
169
+ def test_create_with_bang_via_association_with_block
170
+ post = authors(:david).posts.create!(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
171
+ assert_equal post.title, "New on Edge"
172
+ assert_equal post.body, "More cool stuff!"
173
+ end
174
+
175
+ def test_failed_reload_returns_nil
176
+ p = setup_dangling_association
177
+ assert_nil p.author.reload
178
+ end
179
+
180
+ def test_failed_reset_returns_nil
181
+ p = setup_dangling_association
182
+ assert_nil p.author.reset
183
+ end
184
+
185
+ def test_reload_returns_assocition
186
+ david = developers(:david)
187
+ assert_nothing_raised do
188
+ assert_equal david.projects, david.projects.reload.reload
189
+ end
190
+ end
191
+
192
+ def setup_dangling_association
193
+ josh = Author.create(:name => "Josh")
194
+ p = Post.create(:title => "New on Edge", :body => "More cool stuff!", :author => josh)
195
+ josh.destroy
196
+ p
197
+ end
198
+ end
199
+
200
+ class OverridingAssociationsTest < ActiveRecord::TestCase
201
+ class Person < ActiveRecord::Base; end
202
+ class DifferentPerson < ActiveRecord::Base; end
203
+
204
+ class PeopleList < ActiveRecord::Base
205
+ has_and_belongs_to_many :has_and_belongs_to_many, :before_add => :enlist
206
+ has_many :has_many, :before_add => :enlist
207
+ belongs_to :belongs_to
208
+ has_one :has_one
209
+ end
210
+
211
+ class DifferentPeopleList < PeopleList
212
+ # Different association with the same name, callbacks should be omitted here.
213
+ has_and_belongs_to_many :has_and_belongs_to_many, :class_name => 'DifferentPerson'
214
+ has_many :has_many, :class_name => 'DifferentPerson'
215
+ belongs_to :belongs_to, :class_name => 'DifferentPerson'
216
+ has_one :has_one, :class_name => 'DifferentPerson'
217
+ end
218
+
219
+ def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
220
+ # redeclared association on AR descendant should not inherit callbacks from superclass
221
+ callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
222
+ assert_equal([:enlist], callbacks)
223
+ callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
224
+ assert_equal([], callbacks)
225
+ end
226
+
227
+ def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited
228
+ # redeclared association on AR descendant should not inherit callbacks from superclass
229
+ callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_many)
230
+ assert_equal([:enlist], callbacks)
231
+ callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_many)
232
+ assert_equal([], callbacks)
233
+ end
234
+
235
+ def test_habtm_association_redefinition_reflections_should_differ_and_not_inherited
236
+ assert_not_equal(
237
+ PeopleList.reflect_on_association(:has_and_belongs_to_many),
238
+ DifferentPeopleList.reflect_on_association(:has_and_belongs_to_many)
239
+ )
240
+ end
241
+
242
+ def test_has_many_association_redefinition_reflections_should_differ_and_not_inherited
243
+ assert_not_equal(
244
+ PeopleList.reflect_on_association(:has_many),
245
+ DifferentPeopleList.reflect_on_association(:has_many)
246
+ )
247
+ end
248
+
249
+ def test_belongs_to_association_redefinition_reflections_should_differ_and_not_inherited
250
+ assert_not_equal(
251
+ PeopleList.reflect_on_association(:belongs_to),
252
+ DifferentPeopleList.reflect_on_association(:belongs_to)
253
+ )
254
+ end
255
+
256
+ def test_has_one_association_redefinition_reflections_should_differ_and_not_inherited
257
+ assert_not_equal(
258
+ PeopleList.reflect_on_association(:has_one),
259
+ DifferentPeopleList.reflect_on_association(:has_one)
260
+ )
261
+ end
262
+ end
@@ -1,7 +1,7 @@
1
- require 'abstract_unit'
2
- require 'fixtures/topic'
1
+ require "cases/helper"
2
+ require 'models/topic'
3
3
 
4
- class AttributeMethodsTest < Test::Unit::TestCase
4
+ class AttributeMethodsTest < ActiveRecord::TestCase
5
5
  fixtures :topics
6
6
  def setup
7
7
  @old_suffixes = ActiveRecord::Base.send(:attribute_method_suffixes).dup
@@ -14,7 +14,6 @@ class AttributeMethodsTest < Test::Unit::TestCase
14
14
  ActiveRecord::Base.attribute_method_suffix *@old_suffixes
15
15
  end
16
16
 
17
-
18
17
  def test_match_attribute_method_query_returns_match_data
19
18
  assert_not_nil md = @target.match_attribute_method?('title=')
20
19
  assert_equal 'title', md.pre_match
@@ -48,24 +47,24 @@ class AttributeMethodsTest < Test::Unit::TestCase
48
47
  assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
49
48
  end
50
49
  end
51
-
50
+
52
51
  def test_should_unserialize_attributes_for_frozen_records
53
52
  myobj = {:value1 => :value2}
54
53
  topic = Topic.create("content" => myobj)
55
54
  topic.freeze
56
55
  assert_equal myobj, topic.content
57
56
  end
58
-
57
+
59
58
  def test_kernel_methods_not_implemented_in_activerecord
60
59
  %w(test name display y).each do |method|
61
60
  assert_equal false, ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
62
61
  end
63
62
  end
64
-
63
+
65
64
  def test_primary_key_implemented
66
65
  assert_equal true, Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
67
66
  end
68
-
67
+
69
68
  def test_defined_kernel_methods_implemented_in_model
70
69
  %w(test name display y).each do |method|
71
70
  klass = Class.new ActiveRecord::Base
@@ -73,7 +72,7 @@ class AttributeMethodsTest < Test::Unit::TestCase
73
72
  assert_equal true, klass.instance_method_already_implemented?(method), "##{method} is not defined"
74
73
  end
75
74
  end
76
-
75
+
77
76
  def test_defined_kernel_methods_implemented_in_model_abstract_subclass
78
77
  %w(test name display y).each do |method|
79
78
  abstract = Class.new ActiveRecord::Base
@@ -83,7 +82,7 @@ class AttributeMethodsTest < Test::Unit::TestCase
83
82
  assert_equal true, klass.instance_method_already_implemented?(method), "##{method} is not defined"
84
83
  end
85
84
  end
86
-
85
+
87
86
  def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
88
87
  %w(save create_or_update).each do |method|
89
88
  klass = Class.new ActiveRecord::Base
@@ -97,7 +96,7 @@ class AttributeMethodsTest < Test::Unit::TestCase
97
96
  def test_only_time_related_columns_are_meant_to_be_cached_by_default
98
97
  expected = %w(datetime timestamp time date).sort
99
98
  assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
100
- end
99
+ end
101
100
 
102
101
  def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
103
102
  default_attributes = Topic.cached_attributes
@@ -138,9 +137,102 @@ end
138
137
  end
139
138
  end
140
139
  end
140
+
141
+ def test_time_attributes_are_retrieved_in_current_time_zone
142
+ in_time_zone "Pacific Time (US & Canada)" do
143
+ utc_time = Time.utc(2008, 1, 1)
144
+ record = @target.new
145
+ record[:written_on] = utc_time
146
+ assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
147
+ assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
148
+ assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
149
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
150
+ end
151
+ end
152
+
153
+ def test_setting_time_zone_aware_attribute_to_utc
154
+ in_time_zone "Pacific Time (US & Canada)" do
155
+ utc_time = Time.utc(2008, 1, 1)
156
+ record = @target.new
157
+ record.written_on = utc_time
158
+ assert_equal utc_time, record.written_on
159
+ assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
160
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
161
+ end
162
+ end
163
+
164
+ def test_setting_time_zone_aware_attribute_in_other_time_zone
165
+ utc_time = Time.utc(2008, 1, 1)
166
+ cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
167
+ in_time_zone "Pacific Time (US & Canada)" do
168
+ record = @target.new
169
+ record.written_on = cst_time
170
+ assert_equal utc_time, record.written_on
171
+ assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
172
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
173
+ end
174
+ end
175
+
176
+ def test_setting_time_zone_aware_attribute_with_string
177
+ utc_time = Time.utc(2008, 1, 1)
178
+ (-11..13).each do |timezone_offset|
179
+ time_string = utc_time.in_time_zone(timezone_offset).to_s
180
+ in_time_zone "Pacific Time (US & Canada)" do
181
+ record = @target.new
182
+ record.written_on = time_string
183
+ assert_equal Time.zone.parse(time_string), record.written_on
184
+ assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
185
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
186
+ end
187
+ end
188
+ end
189
+
190
+ def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
191
+ in_time_zone "Pacific Time (US & Canada)" do
192
+ record = @target.new
193
+ record.written_on = ' '
194
+ assert_nil record.written_on
195
+ end
196
+ end
197
+
198
+ def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
199
+ time_string = 'Tue Jan 01 00:00:00 2008'
200
+ (-11..13).each do |timezone_offset|
201
+ in_time_zone timezone_offset do
202
+ record = @target.new
203
+ record.written_on = time_string
204
+ assert_equal Time.zone.parse(time_string), record.written_on
205
+ assert_equal TimeZone[timezone_offset], record.written_on.time_zone
206
+ assert_equal Time.utc(2008, 1, 1), record.written_on.time
207
+ end
208
+ end
209
+ end
210
+
211
+ def test_setting_time_zone_aware_attribute_in_current_time_zone
212
+ utc_time = Time.utc(2008, 1, 1)
213
+ in_time_zone "Pacific Time (US & Canada)" do
214
+ record = @target.new
215
+ record.written_on = utc_time.in_time_zone
216
+ assert_equal utc_time, record.written_on
217
+ assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
218
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
219
+ end
220
+ end
141
221
 
142
222
  private
143
223
  def time_related_columns_on_topic
144
224
  Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
145
225
  end
226
+
227
+ def in_time_zone(zone)
228
+ old_zone = Time.zone
229
+ old_tz = ActiveRecord::Base.time_zone_aware_attributes
230
+
231
+ Time.zone = zone ? TimeZone[zone] : nil
232
+ ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
233
+ yield
234
+ ensure
235
+ Time.zone = old_zone
236
+ ActiveRecord::Base.time_zone_aware_attributes = old_tz
237
+ end
146
238
  end
@@ -1,17 +1,21 @@
1
- require 'abstract_unit'
2
- require 'fixtures/topic'
3
- require 'fixtures/reply'
4
- require 'fixtures/company'
5
- require 'fixtures/customer'
6
- require 'fixtures/developer'
7
- require 'fixtures/project'
8
- require 'fixtures/default'
9
- require 'fixtures/auto_id'
10
- require 'fixtures/column_name'
11
- require 'fixtures/subscriber'
12
- require 'fixtures/keyboard'
13
- require 'fixtures/post'
14
- require 'fixtures/minimalistic'
1
+ require "cases/helper"
2
+ require 'models/author'
3
+ require 'models/topic'
4
+ require 'models/reply'
5
+ require 'models/category'
6
+ require 'models/company'
7
+ require 'models/customer'
8
+ require 'models/developer'
9
+ require 'models/project'
10
+ require 'models/default'
11
+ require 'models/auto_id'
12
+ require 'models/column_name'
13
+ require 'models/subscriber'
14
+ require 'models/keyboard'
15
+ require 'models/post'
16
+ require 'models/comment'
17
+ require 'models/minimalistic'
18
+ require 'models/warehouse_thing'
15
19
  require 'rexml/document'
16
20
 
17
21
  class Category < ActiveRecord::Base; end
@@ -64,20 +68,20 @@ class Task < ActiveRecord::Base
64
68
  attr_protected :starting
65
69
  end
66
70
 
67
- class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
68
- self.table_name = 'topics'
71
+ class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
72
+ self.table_name = 'topics'
69
73
  attr_accessible :author_name
70
74
  attr_protected :content
71
75
  end
72
76
 
73
- class BasicsTest < Test::Unit::TestCase
74
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics
77
+ class BasicsTest < ActiveRecord::TestCase
78
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors
75
79
 
76
80
  def test_table_exists
77
81
  assert !NonExistentTable.table_exists?
78
82
  assert Topic.table_exists?
79
83
  end
80
-
84
+
81
85
  def test_set_attributes
82
86
  topic = Topic.find(1)
83
87
  topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
@@ -91,7 +95,7 @@ class BasicsTest < Test::Unit::TestCase
91
95
  test = AutoId.create('value' => '')
92
96
  assert_nil AutoId.find(test.id).value
93
97
  end
94
-
98
+
95
99
  def test_set_attributes_with_block
96
100
  topic = Topic.new do |t|
97
101
  t.title = "Budget"
@@ -101,7 +105,7 @@ class BasicsTest < Test::Unit::TestCase
101
105
  assert_equal("Budget", topic.title)
102
106
  assert_equal("Jason", topic.author_name)
103
107
  end
104
-
108
+
105
109
  def test_respond_to?
106
110
  topic = Topic.find(1)
107
111
  assert topic.respond_to?("title")
@@ -115,7 +119,7 @@ class BasicsTest < Test::Unit::TestCase
115
119
  assert !topic.respond_to?("nothingness")
116
120
  assert !topic.respond_to?(:nothingness)
117
121
  end
118
-
122
+
119
123
  def test_array_content
120
124
  topic = Topic.new
121
125
  topic.content = %w( one two three )
@@ -124,19 +128,38 @@ class BasicsTest < Test::Unit::TestCase
124
128
  assert_equal(%w( one two three ), Topic.find(topic.id).content)
125
129
  end
126
130
 
131
+ def test_read_attributes_before_type_cast
132
+ category = Category.new({:name=>"Test categoty", :type => nil})
133
+ category_attrs = {"name"=>"Test categoty", "type" => nil}
134
+ assert_equal category_attrs , category.attributes_before_type_cast
135
+ end
136
+
137
+ if current_adapter?(:MysqlAdapter)
138
+ def test_read_attributes_before_type_cast_on_boolean
139
+ bool = Booleantest.create({ "value" => false })
140
+ assert_equal 0, bool.attributes_before_type_cast["value"]
141
+ end
142
+ end
143
+
144
+ def test_read_attributes_before_type_cast_on_datetime
145
+ developer = Developer.find(:first)
146
+ assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
147
+ end
148
+
127
149
  def test_hash_content
128
150
  topic = Topic.new
129
151
  topic.content = { "one" => 1, "two" => 2 }
130
152
  topic.save
131
153
 
132
154
  assert_equal 2, Topic.find(topic.id).content["two"]
133
-
155
+
156
+ topic.content_will_change!
134
157
  topic.content["three"] = 3
135
158
  topic.save
136
159
 
137
160
  assert_equal 3, Topic.find(topic.id).content["three"]
138
161
  end
139
-
162
+
140
163
  def test_update_array_content
141
164
  topic = Topic.new
142
165
  topic.content = %w( one two three )
@@ -145,12 +168,12 @@ class BasicsTest < Test::Unit::TestCase
145
168
  assert_equal(%w( one two three four ), topic.content)
146
169
 
147
170
  topic.save
148
-
171
+
149
172
  topic = Topic.find(topic.id)
150
173
  topic.content << "five"
151
174
  assert_equal(%w( one two three four five ), topic.content)
152
175
  end
153
-
176
+
154
177
  def test_case_sensitive_attributes_hash
155
178
  # DB2 is not case-sensitive
156
179
  return true if current_adapter?(:DB2Adapter)
@@ -165,11 +188,11 @@ class BasicsTest < Test::Unit::TestCase
165
188
  topic_reloaded = Topic.find(topic.id)
166
189
  assert_equal("New Topic", topic_reloaded.title)
167
190
  end
168
-
191
+
169
192
  def test_save!
170
193
  topic = Topic.new(:title => "New Topic")
171
194
  assert topic.save!
172
-
195
+
173
196
  reply = Reply.new
174
197
  assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
175
198
  end
@@ -210,7 +233,7 @@ class BasicsTest < Test::Unit::TestCase
210
233
  topic.attributes= new_topic_values
211
234
  assert_equal new_topic_values[:title], topic.title
212
235
  end
213
-
236
+
214
237
  def test_create_many
215
238
  topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
216
239
  assert_equal 2, topics.size
@@ -228,6 +251,27 @@ class BasicsTest < Test::Unit::TestCase
228
251
  topic = Topic.create("title" => "New Topic")
229
252
  topicReloaded = Topic.find(topic.id)
230
253
  assert_equal(topic, topicReloaded)
254
+ end
255
+
256
+ def test_create_through_factory_with_block
257
+ topic = Topic.create("title" => "New Topic") do |t|
258
+ t.author_name = "David"
259
+ end
260
+ topicReloaded = Topic.find(topic.id)
261
+ assert_equal("New Topic", topic.title)
262
+ assert_equal("David", topic.author_name)
263
+ end
264
+
265
+ def test_create_many_through_factory_with_block
266
+ topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t|
267
+ t.author_name = "David"
268
+ end
269
+ assert_equal 2, topics.size
270
+ topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id)
271
+ assert_equal "first", topic1.title
272
+ assert_equal "David", topic1.author_name
273
+ assert_equal "second", topic2.title
274
+ assert_equal "David", topic2.author_name
231
275
  end
232
276
 
233
277
  def test_update
@@ -240,9 +284,9 @@ class BasicsTest < Test::Unit::TestCase
240
284
 
241
285
  topicReloaded.title = "Updated topic"
242
286
  topicReloaded.save
243
-
287
+
244
288
  topicReloadedAgain = Topic.find(topic.id)
245
-
289
+
246
290
  assert_equal("Updated topic", topicReloadedAgain.title)
247
291
  end
248
292
 
@@ -250,7 +294,7 @@ class BasicsTest < Test::Unit::TestCase
250
294
  topic = Topic.new
251
295
  topic.title = "Still another topic"
252
296
  topic.save
253
-
297
+
254
298
  topicReloaded = Topic.find(topic.id)
255
299
  topicReloaded.title = "A New Topic"
256
300
  topicReloaded.send :write_attribute, 'does_not_exist', 'test'
@@ -261,7 +305,7 @@ class BasicsTest < Test::Unit::TestCase
261
305
  minimalistic = minimalistics(:first)
262
306
  assert_nothing_raised { minimalistic.save }
263
307
  end
264
-
308
+
265
309
  def test_write_attribute
266
310
  topic = Topic.new
267
311
  topic.send(:write_attribute, :title, "Still another topic")
@@ -320,29 +364,29 @@ class BasicsTest < Test::Unit::TestCase
320
364
  assert topic.approved?, "approved should be true"
321
365
  # puts ""
322
366
  end
323
-
367
+
324
368
  def test_query_attribute_string
325
369
  [nil, "", " "].each do |value|
326
370
  assert_equal false, Topic.new(:author_name => value).author_name?
327
371
  end
328
-
372
+
329
373
  assert_equal true, Topic.new(:author_name => "Name").author_name?
330
374
  end
331
-
375
+
332
376
  def test_query_attribute_number
333
377
  [nil, 0, "0"].each do |value|
334
378
  assert_equal false, Developer.new(:salary => value).salary?
335
379
  end
336
-
380
+
337
381
  assert_equal true, Developer.new(:salary => 1).salary?
338
382
  assert_equal true, Developer.new(:salary => "1").salary?
339
383
  end
340
-
384
+
341
385
  def test_query_attribute_boolean
342
386
  [nil, "", false, "false", "f", 0].each do |value|
343
387
  assert_equal false, Topic.new(:approved => value).approved?
344
388
  end
345
-
389
+
346
390
  [true, "true", "1", 1].each do |value|
347
391
  assert_equal true, Topic.new(:approved => value).approved?
348
392
  end
@@ -390,12 +434,12 @@ class BasicsTest < Test::Unit::TestCase
390
434
  # Sybase ctlib does not (yet?) support the date type; use datetime instead.
391
435
  # Oracle treats all dates/times as Time.
392
436
  assert_kind_of(
393
- Time, Topic.find(1).last_read,
437
+ Time, Topic.find(1).last_read,
394
438
  "The last_read attribute should be of the Time class"
395
439
  )
396
440
  else
397
441
  assert_kind_of(
398
- Date, Topic.find(1).last_read,
442
+ Date, Topic.find(1).last_read,
399
443
  "The last_read attribute should be of the Date class"
400
444
  )
401
445
  end
@@ -419,7 +463,7 @@ class BasicsTest < Test::Unit::TestCase
419
463
  assert_equal 9900, Topic.find(2).written_on.usec
420
464
  end
421
465
  end
422
-
466
+
423
467
  def test_custom_mutator
424
468
  topic = Topic.find(1)
425
469
  # This mutator is protected in the class definition
@@ -437,34 +481,34 @@ class BasicsTest < Test::Unit::TestCase
437
481
  def test_record_not_found_exception
438
482
  assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
439
483
  end
440
-
484
+
441
485
  def test_initialize_with_attributes
442
- topic = Topic.new({
486
+ topic = Topic.new({
443
487
  "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
444
488
  })
445
-
489
+
446
490
  assert_equal("initialized from attributes", topic.title)
447
491
  end
448
-
492
+
449
493
  def test_initialize_with_invalid_attribute
450
494
  begin
451
- topic = Topic.new({ "title" => "test",
495
+ topic = Topic.new({ "title" => "test",
452
496
  "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
453
497
  rescue ActiveRecord::MultiparameterAssignmentErrors => ex
454
498
  assert_equal(1, ex.errors.size)
455
499
  assert_equal("last_read", ex.errors[0].attribute)
456
500
  end
457
501
  end
458
-
502
+
459
503
  def test_load
460
- topics = Topic.find(:all, :order => 'id')
461
- assert_equal(2, topics.size)
504
+ topics = Topic.find(:all, :order => 'id')
505
+ assert_equal(4, topics.size)
462
506
  assert_equal(topics(:first).title, topics.first.title)
463
507
  end
464
-
508
+
465
509
  def test_load_with_condition
466
510
  topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
467
-
511
+
468
512
  assert_equal(1, topics.size)
469
513
  assert_equal(topics(:second).title, topics.first.title)
470
514
  end
@@ -528,12 +572,13 @@ class BasicsTest < Test::Unit::TestCase
528
572
  ActiveRecord::Base.pluralize_table_names = true
529
573
  classes.each(&:reset_table_name)
530
574
  end
531
-
532
- def test_destroy_all
533
- assert_equal 2, Topic.count
534
575
 
535
- Topic.destroy_all "author_name = 'Mary'"
536
- assert_equal 1, Topic.count
576
+ def test_destroy_all
577
+ original_count = Topic.count
578
+ topics_by_mary = Topic.count(:conditions => mary = "author_name = 'Mary'")
579
+
580
+ Topic.destroy_all mary
581
+ assert_equal original_count - topics_by_mary, Topic.count
537
582
  end
538
583
 
539
584
  def test_destroy_many
@@ -543,15 +588,16 @@ class BasicsTest < Test::Unit::TestCase
543
588
  end
544
589
 
545
590
  def test_delete_many
546
- Topic.delete([1, 2])
547
- assert_equal 0, Topic.count
591
+ original_count = Topic.count
592
+ Topic.delete(deleting = [1, 2])
593
+ assert_equal original_count - deleting.size, Topic.count
548
594
  end
549
595
 
550
596
  def test_boolean_attributes
551
597
  assert ! Topic.find(1).approved?
552
598
  assert Topic.find(2).approved?
553
599
  end
554
-
600
+
555
601
  def test_increment_counter
556
602
  Topic.increment_counter("replies_count", 1)
557
603
  assert_equal 2, Topic.find(1).replies_count
@@ -559,7 +605,7 @@ class BasicsTest < Test::Unit::TestCase
559
605
  Topic.increment_counter("replies_count", 1)
560
606
  assert_equal 3, Topic.find(1).replies_count
561
607
  end
562
-
608
+
563
609
  def test_decrement_counter
564
610
  Topic.decrement_counter("replies_count", 2)
565
611
  assert_equal -1, Topic.find(2).replies_count
@@ -569,27 +615,32 @@ class BasicsTest < Test::Unit::TestCase
569
615
  end
570
616
 
571
617
  def test_update_all
572
- assert_equal 2, Topic.update_all("content = 'bulk updated!'")
618
+ assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'")
573
619
  assert_equal "bulk updated!", Topic.find(1).content
574
620
  assert_equal "bulk updated!", Topic.find(2).content
575
621
 
576
- assert_equal 2, Topic.update_all(['content = ?', 'bulk updated again!'])
622
+ assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!'])
577
623
  assert_equal "bulk updated again!", Topic.find(1).content
578
624
  assert_equal "bulk updated again!", Topic.find(2).content
579
625
 
580
- assert_equal 2, Topic.update_all(['content = ?', nil])
626
+ assert_equal Topic.count, Topic.update_all(['content = ?', nil])
581
627
  assert_nil Topic.find(1).content
582
628
  end
583
629
 
584
630
  def test_update_all_with_hash
585
631
  assert_not_nil Topic.find(1).last_read
586
- assert_equal 2, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
632
+ assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
587
633
  assert_equal "bulk updated with hash!", Topic.find(1).content
588
634
  assert_equal "bulk updated with hash!", Topic.find(2).content
589
635
  assert_nil Topic.find(1).last_read
590
636
  assert_nil Topic.find(2).last_read
591
637
  end
592
638
 
639
+ def test_update_all_with_non_standard_table_name
640
+ assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1])
641
+ assert_equal 0, WarehouseThing.find(1).value
642
+ end
643
+
593
644
  if current_adapter?(:MysqlAdapter)
594
645
  def test_update_all_with_order_and_limit
595
646
  assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
@@ -613,7 +664,9 @@ class BasicsTest < Test::Unit::TestCase
613
664
  end
614
665
 
615
666
  def test_delete_all
616
- assert_equal 2, Topic.delete_all
667
+ assert Topic.count > 0
668
+
669
+ assert_equal Topic.count, Topic.delete_all
617
670
  end
618
671
 
619
672
  def test_update_by_condition
@@ -621,7 +674,7 @@ class BasicsTest < Test::Unit::TestCase
621
674
  assert_equal "Have a nice day", Topic.find(1).content
622
675
  assert_equal "bulk updated!", Topic.find(2).content
623
676
  end
624
-
677
+
625
678
  def test_attribute_present
626
679
  t = Topic.new
627
680
  t.title = "hello there!"
@@ -630,13 +683,13 @@ class BasicsTest < Test::Unit::TestCase
630
683
  assert t.attribute_present?("written_on")
631
684
  assert !t.attribute_present?("content")
632
685
  end
633
-
686
+
634
687
  def test_attribute_keys_on_new_instance
635
688
  t = Topic.new
636
689
  assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
637
690
  assert_raise(NoMethodError) { t.title2 }
638
691
  end
639
-
692
+
640
693
  def test_class_name
641
694
  assert_equal "Firm", ActiveRecord::Base.class_name("firms")
642
695
  assert_equal "Category", ActiveRecord::Base.class_name("categories")
@@ -655,26 +708,26 @@ class BasicsTest < Test::Unit::TestCase
655
708
  ActiveRecord::Base.table_name_suffix = ""
656
709
  assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
657
710
  end
658
-
711
+
659
712
  def test_null_fields
660
713
  assert_nil Topic.find(1).parent_id
661
714
  assert_nil Topic.create("title" => "Hey you").parent_id
662
715
  end
663
-
716
+
664
717
  def test_default_values
665
718
  topic = Topic.new
666
719
  assert topic.approved?
667
720
  assert_nil topic.written_on
668
721
  assert_nil topic.bonus_time
669
722
  assert_nil topic.last_read
670
-
723
+
671
724
  topic.save
672
725
 
673
726
  topic = Topic.find(topic.id)
674
727
  assert topic.approved?
675
728
  assert_nil topic.last_read
676
729
 
677
- # Oracle has some funky default handling, so it requires a bit of
730
+ # Oracle has some funky default handling, so it requires a bit of
678
731
  # extra testing. See ticket #2788.
679
732
  if current_adapter?(:OracleAdapter)
680
733
  test = TestOracleDefault.new
@@ -730,29 +783,29 @@ class BasicsTest < Test::Unit::TestCase
730
783
  def test_equality
731
784
  assert_equal Topic.find(1), Topic.find(2).topic
732
785
  end
733
-
786
+
734
787
  def test_equality_of_new_records
735
788
  assert_not_equal Topic.new, Topic.new
736
789
  end
737
-
790
+
738
791
  def test_hashing
739
792
  assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
740
793
  end
741
-
794
+
742
795
  def test_destroy_new_record
743
796
  client = Client.new
744
797
  client.destroy
745
798
  assert client.frozen?
746
799
  end
747
-
800
+
748
801
  def test_destroy_record_with_associations
749
802
  client = Client.find(3)
750
803
  client.destroy
751
804
  assert client.frozen?
752
805
  assert_kind_of Firm, client.firm
753
- assert_raises(TypeError) { client.name = "something else" }
806
+ assert_raises(ActiveSupport::FrozenObjectError) { client.name = "something else" }
754
807
  end
755
-
808
+
756
809
  def test_update_attribute
757
810
  assert !Topic.find(1).approved?
758
811
  Topic.find(1).update_attribute("approved", true)
@@ -761,12 +814,12 @@ class BasicsTest < Test::Unit::TestCase
761
814
  Topic.find(1).update_attribute(:approved, false)
762
815
  assert !Topic.find(1).approved?
763
816
  end
764
-
817
+
765
818
  def test_update_attributes
766
819
  topic = Topic.find(1)
767
820
  assert !topic.approved?
768
821
  assert_equal "The First Topic", topic.title
769
-
822
+
770
823
  topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
771
824
  topic.reload
772
825
  assert topic.approved?
@@ -777,37 +830,37 @@ class BasicsTest < Test::Unit::TestCase
777
830
  assert !topic.approved?
778
831
  assert_equal "The First Topic", topic.title
779
832
  end
780
-
833
+
781
834
  def test_update_attributes!
782
835
  reply = Reply.find(2)
783
- assert_equal "The Second Topic's of the day", reply.title
836
+ assert_equal "The Second Topic of the day", reply.title
784
837
  assert_equal "Have a nice day", reply.content
785
-
786
- reply.update_attributes!("title" => "The Second Topic's of the day updated", "content" => "Have a nice evening")
838
+
839
+ reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening")
787
840
  reply.reload
788
- assert_equal "The Second Topic's of the day updated", reply.title
841
+ assert_equal "The Second Topic of the day updated", reply.title
789
842
  assert_equal "Have a nice evening", reply.content
790
-
791
- reply.update_attributes!(:title => "The Second Topic's of the day", :content => "Have a nice day")
843
+
844
+ reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day")
792
845
  reply.reload
793
- assert_equal "The Second Topic's of the day", reply.title
846
+ assert_equal "The Second Topic of the day", reply.title
794
847
  assert_equal "Have a nice day", reply.content
795
-
848
+
796
849
  assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
797
850
  end
798
-
851
+
799
852
  def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
800
853
  topic = TopicWithProtectedContentAndAccessibleAuthorName.new
801
854
  assert_raises(RuntimeError) { topic.attributes = { "author_name" => "me" } }
802
855
  assert_raises(RuntimeError) { topic.attributes = { "content" => "stuff" } }
803
856
  end
804
-
857
+
805
858
  def test_mass_assignment_protection
806
859
  firm = Firm.new
807
860
  firm.attributes = { "name" => "Next Angle", "rating" => 5 }
808
861
  assert_equal 1, firm.rating
809
862
  end
810
-
863
+
811
864
  def test_mass_assignment_protection_against_class_attribute_writers
812
865
  [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
813
866
  :default_timezone, :allow_concurrency, :schema_format, :verification_timeout, :lock_optimistically, :record_timestamps].each do |method|
@@ -833,26 +886,26 @@ class BasicsTest < Test::Unit::TestCase
833
886
  keyboard = Keyboard.new(:id => 9, :name => 'nice try')
834
887
  assert_nil keyboard.id
835
888
  end
836
-
889
+
837
890
  def test_mass_assignment_protection_on_defaults
838
891
  firm = Firm.new
839
892
  firm.attributes = { "id" => 5, "type" => "Client" }
840
893
  assert_nil firm.id
841
894
  assert_equal "Firm", firm[:type]
842
895
  end
843
-
896
+
844
897
  def test_mass_assignment_accessible
845
898
  reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
846
899
  reply.save
847
900
 
848
901
  assert reply.approved?
849
-
902
+
850
903
  reply.approved = false
851
904
  reply.save
852
905
 
853
906
  assert !reply.approved?
854
907
  end
855
-
908
+
856
909
  def test_mass_assignment_protection_inheritance
857
910
  assert_nil LoosePerson.accessible_attributes
858
911
  assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
@@ -869,14 +922,14 @@ class BasicsTest < Test::Unit::TestCase
869
922
  assert_nil TightDescendant.protected_attributes
870
923
  assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
871
924
  end
872
-
925
+
873
926
  def test_readonly_attributes
874
- assert_equal Set.new([ 'title' ]), ReadonlyTitlePost.readonly_attributes
875
-
927
+ assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
928
+
876
929
  post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
877
930
  post.reload
878
931
  assert_equal "cannot change this", post.title
879
-
932
+
880
933
  post.update_attributes(:title => "try to change", :body => "changed")
881
934
  post.reload
882
935
  assert_equal "cannot change this", post.title
@@ -887,7 +940,7 @@ class BasicsTest < Test::Unit::TestCase
887
940
  attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
888
941
  topic = Topic.find(1)
889
942
  topic.attributes = attributes
890
- # note that extra #to_date call allows test to pass for Oracle, which
943
+ # note that extra #to_date call allows test to pass for Oracle, which
891
944
  # treats dates/times the same
892
945
  assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
893
946
  end
@@ -896,7 +949,7 @@ class BasicsTest < Test::Unit::TestCase
896
949
  attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
897
950
  topic = Topic.find(1)
898
951
  topic.attributes = attributes
899
- # note that extra #to_date call allows test to pass for Oracle, which
952
+ # note that extra #to_date call allows test to pass for Oracle, which
900
953
  # treats dates/times the same
901
954
  assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
902
955
  end
@@ -909,18 +962,96 @@ class BasicsTest < Test::Unit::TestCase
909
962
  end
910
963
 
911
964
  def test_multiparameter_attributes_on_time
912
- attributes = {
913
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
965
+ attributes = {
966
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
914
967
  "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
915
968
  }
916
969
  topic = Topic.find(1)
917
970
  topic.attributes = attributes
918
971
  assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
919
972
  end
973
+
974
+ def test_multiparameter_attributes_on_time_with_old_date
975
+ attributes = {
976
+ "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
977
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
978
+ }
979
+ topic = Topic.find(1)
980
+ topic.attributes = attributes
981
+ # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
982
+ assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
983
+ end
984
+
985
+ def test_multiparameter_attributes_on_time_with_utc
986
+ ActiveRecord::Base.default_timezone = :utc
987
+ attributes = {
988
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
989
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
990
+ }
991
+ topic = Topic.find(1)
992
+ topic.attributes = attributes
993
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
994
+ ensure
995
+ ActiveRecord::Base.default_timezone = :local
996
+ end
997
+
998
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
999
+ ActiveRecord::Base.time_zone_aware_attributes = true
1000
+ ActiveRecord::Base.default_timezone = :utc
1001
+ Time.zone = TimeZone[-28800]
1002
+ attributes = {
1003
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1004
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1005
+ }
1006
+ topic = Topic.find(1)
1007
+ topic.attributes = attributes
1008
+ assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
1009
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
1010
+ assert_equal Time.zone, topic.written_on.time_zone
1011
+ ensure
1012
+ ActiveRecord::Base.time_zone_aware_attributes = false
1013
+ ActiveRecord::Base.default_timezone = :local
1014
+ Time.zone = nil
1015
+ end
1016
+
1017
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
1018
+ ActiveRecord::Base.time_zone_aware_attributes = false
1019
+ Time.zone = TimeZone[-28800]
1020
+ attributes = {
1021
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1022
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1023
+ }
1024
+ topic = Topic.find(1)
1025
+ topic.attributes = attributes
1026
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1027
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
1028
+ ensure
1029
+ Time.zone = nil
1030
+ end
1031
+
1032
+ def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
1033
+ ActiveRecord::Base.time_zone_aware_attributes = true
1034
+ ActiveRecord::Base.default_timezone = :utc
1035
+ Time.zone = TimeZone[-28800]
1036
+ Topic.skip_time_zone_conversion_for_attributes = [:written_on]
1037
+ attributes = {
1038
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1039
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1040
+ }
1041
+ topic = Topic.find(1)
1042
+ topic.attributes = attributes
1043
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
1044
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
1045
+ ensure
1046
+ ActiveRecord::Base.time_zone_aware_attributes = false
1047
+ ActiveRecord::Base.default_timezone = :local
1048
+ Time.zone = nil
1049
+ Topic.skip_time_zone_conversion_for_attributes = []
1050
+ end
920
1051
 
921
1052
  def test_multiparameter_attributes_on_time_with_empty_seconds
922
- attributes = {
923
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1053
+ attributes = {
1054
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
924
1055
  "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
925
1056
  }
926
1057
  topic = Topic.find(1)
@@ -931,12 +1062,12 @@ class BasicsTest < Test::Unit::TestCase
931
1062
  def test_multiparameter_mass_assignment_protector
932
1063
  task = Task.new
933
1064
  time = Time.mktime(2000, 1, 1, 1)
934
- task.starting = time
1065
+ task.starting = time
935
1066
  attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
936
1067
  task.attributes = attributes
937
1068
  assert_equal time, task.starting
938
1069
  end
939
-
1070
+
940
1071
  def test_multiparameter_assignment_of_aggregation
941
1072
  customer = Customer.new
942
1073
  address = Address.new("The Street", "The City", "The Country")
@@ -978,9 +1109,9 @@ class BasicsTest < Test::Unit::TestCase
978
1109
  b_false = Booleantest.find(false_id)
979
1110
  assert !b_false.value?
980
1111
  b_true = Booleantest.find(true_id)
981
- assert b_true.value?
1112
+ assert b_true.value?
982
1113
  end
983
-
1114
+
984
1115
  def test_clone
985
1116
  topic = Topic.find(1)
986
1117
  cloned_topic = nil
@@ -989,15 +1120,15 @@ class BasicsTest < Test::Unit::TestCase
989
1120
  assert cloned_topic.new_record?
990
1121
 
991
1122
  # test if the attributes have been cloned
992
- topic.title = "a"
993
- cloned_topic.title = "b"
1123
+ topic.title = "a"
1124
+ cloned_topic.title = "b"
994
1125
  assert_equal "a", topic.title
995
1126
  assert_equal "b", cloned_topic.title
996
1127
 
997
1128
  # test if the attribute values have been cloned
998
1129
  topic.title = {"a" => "b"}
999
1130
  cloned_topic = topic.clone
1000
- cloned_topic.title["a"] = "c"
1131
+ cloned_topic.title["a"] = "c"
1001
1132
  assert_equal "b", topic.title["a"]
1002
1133
 
1003
1134
  #test if attributes set as part of after_initialize are cloned correctly
@@ -1047,11 +1178,11 @@ class BasicsTest < Test::Unit::TestCase
1047
1178
  if current_adapter?(:PostgreSQLAdapter)
1048
1179
  def test_default
1049
1180
  default = Default.new
1050
-
1181
+
1051
1182
  # fixed dates / times
1052
1183
  assert_equal Date.new(2004, 1, 1), default.fixed_date
1053
1184
  assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
1054
-
1185
+
1055
1186
  # char types
1056
1187
  assert_equal 'Y', default.char1
1057
1188
  assert_equal 'a varchar field', default.char2
@@ -1074,7 +1205,7 @@ class BasicsTest < Test::Unit::TestCase
1074
1205
  :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
1075
1206
  :a_circle => '<(5.3, 10.4), 2>'
1076
1207
  )
1077
-
1208
+
1078
1209
  assert g.save
1079
1210
 
1080
1211
  # Reload and check that we have all the geometric attributes.
@@ -1092,7 +1223,7 @@ class BasicsTest < Test::Unit::TestCase
1092
1223
  assert_equal objs[0].isopen, 't'
1093
1224
 
1094
1225
  # test alternate formats when defining the geometric types
1095
-
1226
+
1096
1227
  g = Geometric.new(
1097
1228
  :a_point => '5.0, 6.1',
1098
1229
  #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
@@ -1107,7 +1238,7 @@ class BasicsTest < Test::Unit::TestCase
1107
1238
 
1108
1239
  # Reload and check that we have all the geometric attributes.
1109
1240
  h = Geometric.find(g.id)
1110
-
1241
+
1111
1242
  assert_equal '(5,6.1)', h.a_point
1112
1243
  assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1113
1244
  assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
@@ -1197,10 +1328,10 @@ class BasicsTest < Test::Unit::TestCase
1197
1328
  end
1198
1329
 
1199
1330
  MyObject = Struct.new :attribute1, :attribute2
1200
-
1331
+
1201
1332
  def test_serialized_attribute
1202
1333
  myobj = MyObject.new('value1', 'value2')
1203
- topic = Topic.create("content" => myobj)
1334
+ topic = Topic.create("content" => myobj)
1204
1335
  Topic.serialize("content", MyObject)
1205
1336
  assert_equal(myobj, topic.content)
1206
1337
  end
@@ -1236,19 +1367,21 @@ class BasicsTest < Test::Unit::TestCase
1236
1367
  topic = Topic.create('author_name' => author_name)
1237
1368
  assert_equal author_name, Topic.find(topic.id).author_name
1238
1369
  end
1239
-
1240
- def test_quote_chars
1241
- str = 'The Narrator'
1242
- topic = Topic.create(:author_name => str)
1243
- assert_equal str, topic.author_name
1244
-
1245
- assert_kind_of ActiveSupport::Multibyte::Chars, str.chars
1246
- topic = Topic.find_by_author_name(str.chars)
1247
-
1248
- assert_kind_of Topic, topic
1249
- assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1370
+
1371
+ if RUBY_VERSION < '1.9'
1372
+ def test_quote_chars
1373
+ str = 'The Narrator'
1374
+ topic = Topic.create(:author_name => str)
1375
+ assert_equal str, topic.author_name
1376
+
1377
+ assert_kind_of ActiveSupport::Multibyte::Chars, str.chars
1378
+ topic = Topic.find_by_author_name(str.chars)
1379
+
1380
+ assert_kind_of Topic, topic
1381
+ assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1382
+ end
1250
1383
  end
1251
-
1384
+
1252
1385
  def test_class_level_destroy
1253
1386
  should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1254
1387
  Topic.find(1).replies << should_be_destroyed_reply
@@ -1270,46 +1403,46 @@ class BasicsTest < Test::Unit::TestCase
1270
1403
  def test_increment_attribute
1271
1404
  assert_equal 50, accounts(:signals37).credit_limit
1272
1405
  accounts(:signals37).increment! :credit_limit
1273
- assert_equal 51, accounts(:signals37, :reload).credit_limit
1406
+ assert_equal 51, accounts(:signals37, :reload).credit_limit
1274
1407
 
1275
1408
  accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
1276
1409
  assert_equal 53, accounts(:signals37, :reload).credit_limit
1277
1410
  end
1278
-
1411
+
1279
1412
  def test_increment_nil_attribute
1280
1413
  assert_nil topics(:first).parent_id
1281
1414
  topics(:first).increment! :parent_id
1282
1415
  assert_equal 1, topics(:first).parent_id
1283
1416
  end
1284
-
1417
+
1285
1418
  def test_increment_attribute_by
1286
1419
  assert_equal 50, accounts(:signals37).credit_limit
1287
1420
  accounts(:signals37).increment! :credit_limit, 5
1288
- assert_equal 55, accounts(:signals37, :reload).credit_limit
1421
+ assert_equal 55, accounts(:signals37, :reload).credit_limit
1289
1422
 
1290
1423
  accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3)
1291
1424
  assert_equal 59, accounts(:signals37, :reload).credit_limit
1292
1425
  end
1293
-
1426
+
1294
1427
  def test_decrement_attribute
1295
1428
  assert_equal 50, accounts(:signals37).credit_limit
1296
1429
 
1297
1430
  accounts(:signals37).decrement!(:credit_limit)
1298
1431
  assert_equal 49, accounts(:signals37, :reload).credit_limit
1299
-
1432
+
1300
1433
  accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
1301
1434
  assert_equal 47, accounts(:signals37, :reload).credit_limit
1302
1435
  end
1303
-
1436
+
1304
1437
  def test_decrement_attribute_by
1305
1438
  assert_equal 50, accounts(:signals37).credit_limit
1306
1439
  accounts(:signals37).decrement! :credit_limit, 5
1307
- assert_equal 45, accounts(:signals37, :reload).credit_limit
1440
+ assert_equal 45, accounts(:signals37, :reload).credit_limit
1308
1441
 
1309
1442
  accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3)
1310
1443
  assert_equal 41, accounts(:signals37, :reload).credit_limit
1311
1444
  end
1312
-
1445
+
1313
1446
  def test_toggle_attribute
1314
1447
  assert !topics(:first).approved?
1315
1448
  topics(:first).toggle!(:approved)
@@ -1386,17 +1519,17 @@ class BasicsTest < Test::Unit::TestCase
1386
1519
 
1387
1520
  def test_count_with_join
1388
1521
  res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1389
-
1522
+
1390
1523
  res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1391
1524
  assert_equal res, res2
1392
-
1525
+
1393
1526
  res3 = nil
1394
1527
  assert_nothing_raised do
1395
1528
  res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1396
1529
  :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1397
1530
  end
1398
1531
  assert_equal res, res3
1399
-
1532
+
1400
1533
  res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1401
1534
  res5 = nil
1402
1535
  assert_nothing_raised do
@@ -1405,7 +1538,7 @@ class BasicsTest < Test::Unit::TestCase
1405
1538
  :select => "p.id")
1406
1539
  end
1407
1540
 
1408
- assert_equal res4, res5
1541
+ assert_equal res4, res5
1409
1542
 
1410
1543
  unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
1411
1544
  res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
@@ -1419,15 +1552,15 @@ class BasicsTest < Test::Unit::TestCase
1419
1552
  assert_equal res6, res7
1420
1553
  end
1421
1554
  end
1422
-
1423
- def test_clear_association_cache_stored
1555
+
1556
+ def test_clear_association_cache_stored
1424
1557
  firm = Firm.find(1)
1425
1558
  assert_kind_of Firm, firm
1426
1559
 
1427
1560
  firm.clear_association_cache
1428
1561
  assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
1429
1562
  end
1430
-
1563
+
1431
1564
  def test_clear_association_cache_new_record
1432
1565
  firm = Firm.new
1433
1566
  client_stored = Client.find(3)
@@ -1455,31 +1588,31 @@ class BasicsTest < Test::Unit::TestCase
1455
1588
  assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1456
1589
  assert_equal 3, scoped_developers.size
1457
1590
  end
1458
-
1591
+
1459
1592
  def test_scoped_find_limit_offset
1460
1593
  scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
1461
1594
  Developer.find(:all, :order => 'id')
1462
- end
1595
+ end
1463
1596
  assert !scoped_developers.include?(developers(:david))
1464
1597
  assert !scoped_developers.include?(developers(:jamis))
1465
1598
  assert_equal 3, scoped_developers.size
1466
-
1599
+
1467
1600
  # Test without scoped find conditions to ensure we get the whole thing
1468
1601
  developers = Developer.find(:all, :order => 'id')
1469
1602
  assert_equal Developer.count, developers.size
1470
1603
  end
1471
1604
 
1472
- def test_scoped_find_order
1473
- # Test order in scope
1605
+ def test_scoped_find_order
1606
+ # Test order in scope
1474
1607
  scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
1475
1608
  Developer.find(:all)
1476
- end
1609
+ end
1477
1610
  assert_equal 'Jamis', scoped_developers.first.name
1478
1611
  assert scoped_developers.include?(developers(:jamis))
1479
1612
  # Test scope without order and order in find
1480
1613
  scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
1481
1614
  Developer.find(:all, :order => 'salary DESC')
1482
- end
1615
+ end
1483
1616
  # Test scope order + find order, find has priority
1484
1617
  scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
1485
1618
  Developer.find(:all, :order => 'salary ASC')
@@ -1510,6 +1643,41 @@ class BasicsTest < Test::Unit::TestCase
1510
1643
  end
1511
1644
  end
1512
1645
 
1646
+ def test_find_last
1647
+ last = Developer.find :last
1648
+ assert_equal last, Developer.find(:first, :order => 'id desc')
1649
+ end
1650
+
1651
+ def test_last
1652
+ assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
1653
+ end
1654
+
1655
+ def test_all_with_conditions
1656
+ assert_equal Developer.find(:all, :order => 'id desc'), Developer.all(:order => 'id desc')
1657
+ end
1658
+
1659
+ def test_find_ordered_last
1660
+ last = Developer.find :last, :order => 'developers.salary ASC'
1661
+ assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
1662
+ end
1663
+
1664
+ def test_find_reverse_ordered_last
1665
+ last = Developer.find :last, :order => 'developers.salary DESC'
1666
+ assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
1667
+ end
1668
+
1669
+ def test_find_multiple_ordered_last
1670
+ last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
1671
+ assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
1672
+ end
1673
+
1674
+ def test_find_scoped_ordered_last
1675
+ last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do
1676
+ Developer.find(:last)
1677
+ end
1678
+ assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
1679
+ end
1680
+
1513
1681
  def test_abstract_class
1514
1682
  assert !ActiveRecord::Base.abstract_class?
1515
1683
  assert LoosePerson.abstract_class?
@@ -1561,7 +1729,7 @@ class BasicsTest < Test::Unit::TestCase
1561
1729
  old_class = LooseDescendant
1562
1730
  Object.send :remove_const, :LooseDescendant
1563
1731
 
1564
- descendant = old_class.create!
1732
+ descendant = old_class.create! :first_name => 'bob'
1565
1733
  assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1566
1734
  ensure
1567
1735
  unless Object.const_defined?(:LooseDescendant)
@@ -1626,18 +1794,18 @@ class BasicsTest < Test::Unit::TestCase
1626
1794
  xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
1627
1795
  assert_equal "<topic>", xml.first(7)
1628
1796
  assert !xml.include?(%(<title>The First Topic</title>))
1629
- assert xml.include?(%(<author-name>David</author-name>))
1797
+ assert xml.include?(%(<author-name>David</author-name>))
1630
1798
 
1631
1799
  xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
1632
1800
  assert !xml.include?(%(<title>The First Topic</title>))
1633
- assert !xml.include?(%(<author-name>David</author-name>))
1801
+ assert !xml.include?(%(<author-name>David</author-name>))
1634
1802
  end
1635
1803
 
1636
1804
  def test_to_xml_including_has_many_association
1637
1805
  xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
1638
1806
  assert_equal "<topic>", xml.first(7)
1639
1807
  assert xml.include?(%(<replies type="array"><reply>))
1640
- assert xml.include?(%(<title>The Second Topic's of the day</title>))
1808
+ assert xml.include?(%(<title>The Second Topic of the day</title>))
1641
1809
  end
1642
1810
 
1643
1811
  def test_array_to_xml_including_has_many_association
@@ -1650,7 +1818,7 @@ class BasicsTest < Test::Unit::TestCase
1650
1818
  assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
1651
1819
  assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
1652
1820
  end
1653
-
1821
+
1654
1822
  def test_array_to_xml_including_has_one_association
1655
1823
  xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
1656
1824
  assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
@@ -1671,7 +1839,7 @@ class BasicsTest < Test::Unit::TestCase
1671
1839
  xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
1672
1840
  assert xml.include?("<firm>")
1673
1841
  end
1674
-
1842
+
1675
1843
  def test_to_xml_including_multiple_associations
1676
1844
  xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
1677
1845
  assert_equal "<firm>", xml.first(6)
@@ -1681,21 +1849,21 @@ class BasicsTest < Test::Unit::TestCase
1681
1849
 
1682
1850
  def test_to_xml_including_multiple_associations_with_options
1683
1851
  xml = companies(:first_firm).to_xml(
1684
- :indent => 0, :skip_instruct => true,
1852
+ :indent => 0, :skip_instruct => true,
1685
1853
  :include => { :clients => { :only => :name } }
1686
1854
  )
1687
-
1855
+
1688
1856
  assert_equal "<firm>", xml.first(6)
1689
1857
  assert xml.include?(%(<client><name>Summit</name></client>))
1690
1858
  assert xml.include?(%(<clients type="array"><client>))
1691
1859
  end
1692
-
1860
+
1693
1861
  def test_to_xml_including_methods
1694
1862
  xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
1695
1863
  assert_equal "<company>", xml.first(9)
1696
1864
  assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
1697
1865
  end
1698
-
1866
+
1699
1867
  def test_to_xml_with_block
1700
1868
  value = "Rockin' the block"
1701
1869
  xml = Company.new.to_xml(:skip_instruct => true) do |xml|
@@ -1704,37 +1872,16 @@ class BasicsTest < Test::Unit::TestCase
1704
1872
  assert_equal "<company>", xml.first(9)
1705
1873
  assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
1706
1874
  end
1707
-
1708
- def test_except_attributes
1709
- assert_deprecated do
1710
- assert_equal(
1711
- %w( author_name type id approved replies_count bonus_time written_on content author_email_address parent_id last_read),
1712
- topics(:first).attributes(:except => :title).keys
1713
- )
1714
1875
 
1715
- assert_equal(
1716
- %w( replies_count bonus_time written_on content author_email_address parent_id last_read),
1717
- topics(:first).attributes(:except => [ :title, :id, :type, :approved, :author_name ]).keys
1718
- )
1719
- end
1720
- end
1721
-
1722
- def test_include_attributes
1723
- assert_deprecated do
1724
- assert_equal(%w( title ), topics(:first).attributes(:only => :title).keys)
1725
- assert_equal(%w( title author_name type id approved ), topics(:first).attributes(:only => [ :title, :id, :type, :approved, :author_name ]).keys)
1726
- end
1727
- end
1728
-
1729
1876
  def test_type_name_with_module_should_handle_beginning
1730
1877
  assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
1731
1878
  assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
1732
1879
  end
1733
-
1880
+
1734
1881
  def test_to_param_should_return_string
1735
1882
  assert_kind_of String, Client.find(:first).to_param
1736
1883
  end
1737
-
1884
+
1738
1885
  def test_inspect_class
1739
1886
  assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
1740
1887
  assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
@@ -1754,7 +1901,7 @@ class BasicsTest < Test::Unit::TestCase
1754
1901
  assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
1755
1902
  assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
1756
1903
  end
1757
-
1904
+
1758
1905
  def test_inspect_class_without_table
1759
1906
  assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
1760
1907
  end
@@ -1766,7 +1913,7 @@ class BasicsTest < Test::Unit::TestCase
1766
1913
  assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
1767
1914
  assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
1768
1915
  end
1769
-
1916
+
1770
1917
  def test_becomes
1771
1918
  assert_kind_of Reply, topics(:first).becomes(Reply)
1772
1919
  assert_equal "The First Topic", topics(:first).becomes(Reply).title