activerecord_csi 2.3.5.p6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. data/CHANGELOG +5858 -0
  2. data/README +351 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/Rakefile +270 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/performance.rb +162 -0
  7. data/install.rb +30 -0
  8. data/lib/active_record/aggregations.rb +261 -0
  9. data/lib/active_record/association_preload.rb +389 -0
  10. data/lib/active_record/associations/association_collection.rb +475 -0
  11. data/lib/active_record/associations/association_proxy.rb +278 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +76 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +53 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +122 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +266 -0
  17. data/lib/active_record/associations/has_one_association.rb +133 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +37 -0
  19. data/lib/active_record/associations.rb +2241 -0
  20. data/lib/active_record/attribute_methods.rb +388 -0
  21. data/lib/active_record/autosave_association.rb +364 -0
  22. data/lib/active_record/base.rb +3171 -0
  23. data/lib/active_record/batches.rb +81 -0
  24. data/lib/active_record/calculations.rb +311 -0
  25. data/lib/active_record/callbacks.rb +360 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +371 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +139 -0
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +289 -0
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +94 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +722 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +434 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +241 -0
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +630 -0
  35. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1113 -0
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  37. data/lib/active_record/connection_adapters/sqlite_adapter.rb +453 -0
  38. data/lib/active_record/dirty.rb +183 -0
  39. data/lib/active_record/dynamic_finder_match.rb +41 -0
  40. data/lib/active_record/dynamic_scope_match.rb +25 -0
  41. data/lib/active_record/fixtures.rb +996 -0
  42. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  43. data/lib/active_record/locale/en.yml +58 -0
  44. data/lib/active_record/locking/optimistic.rb +148 -0
  45. data/lib/active_record/locking/pessimistic.rb +55 -0
  46. data/lib/active_record/migration.rb +566 -0
  47. data/lib/active_record/named_scope.rb +192 -0
  48. data/lib/active_record/nested_attributes.rb +392 -0
  49. data/lib/active_record/observer.rb +197 -0
  50. data/lib/active_record/query_cache.rb +33 -0
  51. data/lib/active_record/reflection.rb +320 -0
  52. data/lib/active_record/schema.rb +51 -0
  53. data/lib/active_record/schema_dumper.rb +182 -0
  54. data/lib/active_record/serialization.rb +101 -0
  55. data/lib/active_record/serializers/json_serializer.rb +91 -0
  56. data/lib/active_record/serializers/xml_serializer.rb +357 -0
  57. data/lib/active_record/session_store.rb +326 -0
  58. data/lib/active_record/test_case.rb +66 -0
  59. data/lib/active_record/timestamp.rb +71 -0
  60. data/lib/active_record/transactions.rb +235 -0
  61. data/lib/active_record/validations.rb +1135 -0
  62. data/lib/active_record/version.rb +9 -0
  63. data/lib/active_record.rb +84 -0
  64. data/lib/activerecord.rb +2 -0
  65. data/test/assets/example.log +1 -0
  66. data/test/assets/flowers.jpg +0 -0
  67. data/test/cases/aaa_create_tables_test.rb +24 -0
  68. data/test/cases/active_schema_test_mysql.rb +100 -0
  69. data/test/cases/active_schema_test_postgresql.rb +24 -0
  70. data/test/cases/adapter_test.rb +145 -0
  71. data/test/cases/aggregations_test.rb +167 -0
  72. data/test/cases/ar_schema_test.rb +32 -0
  73. data/test/cases/associations/belongs_to_associations_test.rb +425 -0
  74. data/test/cases/associations/callbacks_test.rb +161 -0
  75. data/test/cases/associations/cascaded_eager_loading_test.rb +131 -0
  76. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  77. data/test/cases/associations/eager_load_nested_include_test.rb +130 -0
  78. data/test/cases/associations/eager_singularization_test.rb +145 -0
  79. data/test/cases/associations/eager_test.rb +834 -0
  80. data/test/cases/associations/extension_test.rb +62 -0
  81. data/test/cases/associations/habtm_join_table_test.rb +56 -0
  82. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +822 -0
  83. data/test/cases/associations/has_many_associations_test.rb +1134 -0
  84. data/test/cases/associations/has_many_through_associations_test.rb +346 -0
  85. data/test/cases/associations/has_one_associations_test.rb +330 -0
  86. data/test/cases/associations/has_one_through_associations_test.rb +209 -0
  87. data/test/cases/associations/inner_join_association_test.rb +93 -0
  88. data/test/cases/associations/join_model_test.rb +712 -0
  89. data/test/cases/associations_test.rb +262 -0
  90. data/test/cases/attribute_methods_test.rb +305 -0
  91. data/test/cases/autosave_association_test.rb +1142 -0
  92. data/test/cases/base_test.rb +2154 -0
  93. data/test/cases/batches_test.rb +61 -0
  94. data/test/cases/binary_test.rb +30 -0
  95. data/test/cases/calculations_test.rb +348 -0
  96. data/test/cases/callbacks_observers_test.rb +38 -0
  97. data/test/cases/callbacks_test.rb +438 -0
  98. data/test/cases/class_inheritable_attributes_test.rb +32 -0
  99. data/test/cases/column_alias_test.rb +17 -0
  100. data/test/cases/column_definition_test.rb +70 -0
  101. data/test/cases/connection_pool_test.rb +25 -0
  102. data/test/cases/connection_test_firebird.rb +8 -0
  103. data/test/cases/connection_test_mysql.rb +64 -0
  104. data/test/cases/copy_table_test_sqlite.rb +80 -0
  105. data/test/cases/database_statements_test.rb +12 -0
  106. data/test/cases/datatype_test_postgresql.rb +204 -0
  107. data/test/cases/date_time_test.rb +37 -0
  108. data/test/cases/default_test_firebird.rb +16 -0
  109. data/test/cases/defaults_test.rb +111 -0
  110. data/test/cases/deprecated_finder_test.rb +30 -0
  111. data/test/cases/dirty_test.rb +316 -0
  112. data/test/cases/finder_respond_to_test.rb +76 -0
  113. data/test/cases/finder_test.rb +1066 -0
  114. data/test/cases/fixtures_test.rb +656 -0
  115. data/test/cases/helper.rb +68 -0
  116. data/test/cases/i18n_test.rb +46 -0
  117. data/test/cases/inheritance_test.rb +262 -0
  118. data/test/cases/invalid_date_test.rb +24 -0
  119. data/test/cases/json_serialization_test.rb +205 -0
  120. data/test/cases/lifecycle_test.rb +193 -0
  121. data/test/cases/locking_test.rb +304 -0
  122. data/test/cases/method_scoping_test.rb +704 -0
  123. data/test/cases/migration_test.rb +1523 -0
  124. data/test/cases/migration_test_firebird.rb +124 -0
  125. data/test/cases/mixin_test.rb +96 -0
  126. data/test/cases/modules_test.rb +81 -0
  127. data/test/cases/multiple_db_test.rb +85 -0
  128. data/test/cases/named_scope_test.rb +361 -0
  129. data/test/cases/nested_attributes_test.rb +581 -0
  130. data/test/cases/pk_test.rb +119 -0
  131. data/test/cases/pooled_connections_test.rb +103 -0
  132. data/test/cases/query_cache_test.rb +123 -0
  133. data/test/cases/readonly_test.rb +107 -0
  134. data/test/cases/reflection_test.rb +194 -0
  135. data/test/cases/reload_models_test.rb +22 -0
  136. data/test/cases/repair_helper.rb +50 -0
  137. data/test/cases/reserved_word_test_mysql.rb +176 -0
  138. data/test/cases/sanitize_test.rb +25 -0
  139. data/test/cases/schema_authorization_test_postgresql.rb +75 -0
  140. data/test/cases/schema_dumper_test.rb +211 -0
  141. data/test/cases/schema_test_postgresql.rb +178 -0
  142. data/test/cases/serialization_test.rb +47 -0
  143. data/test/cases/synonym_test_oracle.rb +17 -0
  144. data/test/cases/timestamp_test.rb +75 -0
  145. data/test/cases/transactions_test.rb +522 -0
  146. data/test/cases/unconnected_test.rb +32 -0
  147. data/test/cases/validations_i18n_test.rb +955 -0
  148. data/test/cases/validations_test.rb +1640 -0
  149. data/test/cases/xml_serialization_test.rb +240 -0
  150. data/test/config.rb +5 -0
  151. data/test/connections/jdbc_jdbcderby/connection.rb +18 -0
  152. data/test/connections/jdbc_jdbch2/connection.rb +18 -0
  153. data/test/connections/jdbc_jdbchsqldb/connection.rb +18 -0
  154. data/test/connections/jdbc_jdbcmysql/connection.rb +26 -0
  155. data/test/connections/jdbc_jdbcpostgresql/connection.rb +26 -0
  156. data/test/connections/jdbc_jdbcsqlite3/connection.rb +25 -0
  157. data/test/connections/native_db2/connection.rb +25 -0
  158. data/test/connections/native_firebird/connection.rb +26 -0
  159. data/test/connections/native_frontbase/connection.rb +27 -0
  160. data/test/connections/native_mysql/connection.rb +25 -0
  161. data/test/connections/native_openbase/connection.rb +21 -0
  162. data/test/connections/native_oracle/connection.rb +27 -0
  163. data/test/connections/native_postgresql/connection.rb +25 -0
  164. data/test/connections/native_sqlite/connection.rb +25 -0
  165. data/test/connections/native_sqlite3/connection.rb +25 -0
  166. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  167. data/test/connections/native_sybase/connection.rb +23 -0
  168. data/test/fixtures/accounts.yml +29 -0
  169. data/test/fixtures/all/developers.yml +0 -0
  170. data/test/fixtures/all/people.csv +0 -0
  171. data/test/fixtures/all/tasks.yml +0 -0
  172. data/test/fixtures/author_addresses.yml +5 -0
  173. data/test/fixtures/author_favorites.yml +4 -0
  174. data/test/fixtures/authors.yml +9 -0
  175. data/test/fixtures/binaries.yml +132 -0
  176. data/test/fixtures/books.yml +7 -0
  177. data/test/fixtures/categories/special_categories.yml +9 -0
  178. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  179. data/test/fixtures/categories.yml +14 -0
  180. data/test/fixtures/categories_ordered.yml +7 -0
  181. data/test/fixtures/categories_posts.yml +23 -0
  182. data/test/fixtures/categorizations.yml +17 -0
  183. data/test/fixtures/clubs.yml +6 -0
  184. data/test/fixtures/comments.yml +59 -0
  185. data/test/fixtures/companies.yml +56 -0
  186. data/test/fixtures/computers.yml +4 -0
  187. data/test/fixtures/courses.yml +7 -0
  188. data/test/fixtures/customers.yml +26 -0
  189. data/test/fixtures/developers.yml +21 -0
  190. data/test/fixtures/developers_projects.yml +17 -0
  191. data/test/fixtures/edges.yml +6 -0
  192. data/test/fixtures/entrants.yml +14 -0
  193. data/test/fixtures/fixture_database.sqlite3 +0 -0
  194. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  195. data/test/fixtures/fk_test_has_fk.yml +3 -0
  196. data/test/fixtures/fk_test_has_pk.yml +2 -0
  197. data/test/fixtures/funny_jokes.yml +10 -0
  198. data/test/fixtures/items.yml +4 -0
  199. data/test/fixtures/jobs.yml +7 -0
  200. data/test/fixtures/legacy_things.yml +3 -0
  201. data/test/fixtures/mateys.yml +4 -0
  202. data/test/fixtures/member_types.yml +6 -0
  203. data/test/fixtures/members.yml +6 -0
  204. data/test/fixtures/memberships.yml +20 -0
  205. data/test/fixtures/minimalistics.yml +2 -0
  206. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  207. data/test/fixtures/mixins.yml +29 -0
  208. data/test/fixtures/movies.yml +7 -0
  209. data/test/fixtures/naked/csv/accounts.csv +1 -0
  210. data/test/fixtures/naked/yml/accounts.yml +1 -0
  211. data/test/fixtures/naked/yml/companies.yml +1 -0
  212. data/test/fixtures/naked/yml/courses.yml +1 -0
  213. data/test/fixtures/organizations.yml +5 -0
  214. data/test/fixtures/owners.yml +7 -0
  215. data/test/fixtures/parrots.yml +27 -0
  216. data/test/fixtures/parrots_pirates.yml +7 -0
  217. data/test/fixtures/people.yml +15 -0
  218. data/test/fixtures/pets.yml +14 -0
  219. data/test/fixtures/pirates.yml +9 -0
  220. data/test/fixtures/posts.yml +52 -0
  221. data/test/fixtures/price_estimates.yml +7 -0
  222. data/test/fixtures/projects.yml +7 -0
  223. data/test/fixtures/readers.yml +9 -0
  224. data/test/fixtures/references.yml +17 -0
  225. data/test/fixtures/reserved_words/distinct.yml +5 -0
  226. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  227. data/test/fixtures/reserved_words/group.yml +14 -0
  228. data/test/fixtures/reserved_words/select.yml +8 -0
  229. data/test/fixtures/reserved_words/values.yml +7 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/sponsors.yml +9 -0
  232. data/test/fixtures/subscribers.yml +7 -0
  233. data/test/fixtures/subscriptions.yml +12 -0
  234. data/test/fixtures/taggings.yml +28 -0
  235. data/test/fixtures/tags.yml +7 -0
  236. data/test/fixtures/tasks.yml +7 -0
  237. data/test/fixtures/topics.yml +42 -0
  238. data/test/fixtures/toys.yml +4 -0
  239. data/test/fixtures/treasures.yml +10 -0
  240. data/test/fixtures/vertices.yml +4 -0
  241. data/test/fixtures/warehouse-things.yml +3 -0
  242. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  243. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  244. data/test/migrations/duplicate/1_people_have_last_names.rb +9 -0
  245. data/test/migrations/duplicate/2_we_need_reminders.rb +12 -0
  246. data/test/migrations/duplicate/3_foo.rb +7 -0
  247. data/test/migrations/duplicate/3_innocent_jointable.rb +12 -0
  248. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  249. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  250. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +12 -0
  251. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +9 -0
  252. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +12 -0
  253. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +9 -0
  254. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  255. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  256. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  257. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  258. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  259. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  260. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  261. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  262. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  263. data/test/models/author.rb +146 -0
  264. data/test/models/auto_id.rb +4 -0
  265. data/test/models/binary.rb +2 -0
  266. data/test/models/bird.rb +3 -0
  267. data/test/models/book.rb +4 -0
  268. data/test/models/categorization.rb +5 -0
  269. data/test/models/category.rb +34 -0
  270. data/test/models/citation.rb +6 -0
  271. data/test/models/club.rb +13 -0
  272. data/test/models/column_name.rb +3 -0
  273. data/test/models/comment.rb +29 -0
  274. data/test/models/company.rb +171 -0
  275. data/test/models/company_in_module.rb +61 -0
  276. data/test/models/computer.rb +3 -0
  277. data/test/models/contact.rb +16 -0
  278. data/test/models/contract.rb +5 -0
  279. data/test/models/course.rb +3 -0
  280. data/test/models/customer.rb +73 -0
  281. data/test/models/default.rb +2 -0
  282. data/test/models/developer.rb +101 -0
  283. data/test/models/edge.rb +5 -0
  284. data/test/models/entrant.rb +3 -0
  285. data/test/models/essay.rb +3 -0
  286. data/test/models/event.rb +3 -0
  287. data/test/models/guid.rb +2 -0
  288. data/test/models/item.rb +7 -0
  289. data/test/models/job.rb +5 -0
  290. data/test/models/joke.rb +3 -0
  291. data/test/models/keyboard.rb +3 -0
  292. data/test/models/legacy_thing.rb +3 -0
  293. data/test/models/matey.rb +4 -0
  294. data/test/models/member.rb +12 -0
  295. data/test/models/member_detail.rb +5 -0
  296. data/test/models/member_type.rb +3 -0
  297. data/test/models/membership.rb +9 -0
  298. data/test/models/minimalistic.rb +2 -0
  299. data/test/models/mixed_case_monkey.rb +3 -0
  300. data/test/models/movie.rb +5 -0
  301. data/test/models/order.rb +4 -0
  302. data/test/models/organization.rb +6 -0
  303. data/test/models/owner.rb +5 -0
  304. data/test/models/parrot.rb +16 -0
  305. data/test/models/person.rb +16 -0
  306. data/test/models/pet.rb +5 -0
  307. data/test/models/pirate.rb +70 -0
  308. data/test/models/post.rb +100 -0
  309. data/test/models/price_estimate.rb +3 -0
  310. data/test/models/project.rb +30 -0
  311. data/test/models/reader.rb +4 -0
  312. data/test/models/reference.rb +4 -0
  313. data/test/models/reply.rb +46 -0
  314. data/test/models/ship.rb +10 -0
  315. data/test/models/ship_part.rb +5 -0
  316. data/test/models/sponsor.rb +4 -0
  317. data/test/models/subject.rb +4 -0
  318. data/test/models/subscriber.rb +8 -0
  319. data/test/models/subscription.rb +4 -0
  320. data/test/models/tag.rb +7 -0
  321. data/test/models/tagging.rb +10 -0
  322. data/test/models/task.rb +3 -0
  323. data/test/models/topic.rb +80 -0
  324. data/test/models/toy.rb +6 -0
  325. data/test/models/treasure.rb +8 -0
  326. data/test/models/vertex.rb +9 -0
  327. data/test/models/warehouse_thing.rb +5 -0
  328. data/test/schema/mysql_specific_schema.rb +24 -0
  329. data/test/schema/postgresql_specific_schema.rb +114 -0
  330. data/test/schema/schema.rb +493 -0
  331. data/test/schema/schema2.rb +6 -0
  332. data/test/schema/sqlite_specific_schema.rb +25 -0
  333. metadata +420 -0
@@ -0,0 +1,2154 @@
1
+ require "cases/helper"
2
+ require 'models/post'
3
+ require 'models/author'
4
+ require 'models/topic'
5
+ require 'models/reply'
6
+ require 'models/category'
7
+ require 'models/company'
8
+ require 'models/customer'
9
+ require 'models/developer'
10
+ require 'models/project'
11
+ require 'models/default'
12
+ require 'models/auto_id'
13
+ require 'models/column_name'
14
+ require 'models/subscriber'
15
+ require 'models/keyboard'
16
+ require 'models/comment'
17
+ require 'models/minimalistic'
18
+ require 'models/warehouse_thing'
19
+ require 'models/parrot'
20
+ require 'rexml/document'
21
+
22
+ class Category < ActiveRecord::Base; end
23
+ class Categorization < ActiveRecord::Base; end
24
+ class Smarts < ActiveRecord::Base; end
25
+ class CreditCard < ActiveRecord::Base
26
+ class PinNumber < ActiveRecord::Base
27
+ class CvvCode < ActiveRecord::Base; end
28
+ class SubCvvCode < CvvCode; end
29
+ end
30
+ class SubPinNumber < PinNumber; end
31
+ class Brand < Category; end
32
+ end
33
+ class MasterCreditCard < ActiveRecord::Base; end
34
+ class Post < ActiveRecord::Base; end
35
+ class Computer < ActiveRecord::Base; end
36
+ class NonExistentTable < ActiveRecord::Base; end
37
+ class TestOracleDefault < ActiveRecord::Base; end
38
+
39
+ class LoosePerson < ActiveRecord::Base
40
+ self.table_name = 'people'
41
+ self.abstract_class = true
42
+ attr_protected :credit_rating, :administrator
43
+ end
44
+
45
+ class LooseDescendant < LoosePerson
46
+ attr_protected :phone_number
47
+ end
48
+
49
+ class LooseDescendantSecond< LoosePerson
50
+ attr_protected :phone_number
51
+ attr_protected :name
52
+ end
53
+
54
+ class TightPerson < ActiveRecord::Base
55
+ self.table_name = 'people'
56
+ attr_accessible :name, :address
57
+ end
58
+
59
+ class TightDescendant < TightPerson
60
+ attr_accessible :phone_number
61
+ end
62
+
63
+ class ReadonlyTitlePost < Post
64
+ attr_readonly :title
65
+ end
66
+
67
+ class Booleantest < ActiveRecord::Base; end
68
+
69
+ class Task < ActiveRecord::Base
70
+ attr_protected :starting
71
+ end
72
+
73
+ class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
74
+ self.table_name = 'topics'
75
+ attr_accessible :author_name
76
+ attr_protected :content
77
+ end
78
+
79
+ class BasicsTest < ActiveRecord::TestCase
80
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
81
+
82
+ def test_table_exists
83
+ assert !NonExistentTable.table_exists?
84
+ assert Topic.table_exists?
85
+ end
86
+
87
+ def test_set_attributes
88
+ topic = Topic.find(1)
89
+ topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
90
+ topic.save
91
+ assert_equal("Budget", topic.title)
92
+ assert_equal("Jason", topic.author_name)
93
+ assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
94
+ end
95
+
96
+ def test_integers_as_nil
97
+ test = AutoId.create('value' => '')
98
+ assert_nil AutoId.find(test.id).value
99
+ end
100
+
101
+ def test_set_attributes_with_block
102
+ topic = Topic.new do |t|
103
+ t.title = "Budget"
104
+ t.author_name = "Jason"
105
+ end
106
+
107
+ assert_equal("Budget", topic.title)
108
+ assert_equal("Jason", topic.author_name)
109
+ end
110
+
111
+ def test_respond_to?
112
+ topic = Topic.find(1)
113
+ assert topic.respond_to?("title")
114
+ assert topic.respond_to?("title?")
115
+ assert topic.respond_to?("title=")
116
+ assert topic.respond_to?(:title)
117
+ assert topic.respond_to?(:title?)
118
+ assert topic.respond_to?(:title=)
119
+ assert topic.respond_to?("author_name")
120
+ assert topic.respond_to?("attribute_names")
121
+ assert !topic.respond_to?("nothingness")
122
+ assert !topic.respond_to?(:nothingness)
123
+ end
124
+
125
+ def test_array_content
126
+ topic = Topic.new
127
+ topic.content = %w( one two three )
128
+ topic.save
129
+
130
+ assert_equal(%w( one two three ), Topic.find(topic.id).content)
131
+ end
132
+
133
+ def test_read_attributes_before_type_cast
134
+ category = Category.new({:name=>"Test categoty", :type => nil})
135
+ category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil}
136
+ assert_equal category_attrs , category.attributes_before_type_cast
137
+ end
138
+
139
+ if current_adapter?(:MysqlAdapter)
140
+ def test_read_attributes_before_type_cast_on_boolean
141
+ bool = Booleantest.create({ "value" => false })
142
+ assert_equal "0", bool.reload.attributes_before_type_cast["value"]
143
+ end
144
+ end
145
+
146
+ def test_read_attributes_before_type_cast_on_datetime
147
+ developer = Developer.find(:first)
148
+ assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
149
+ end
150
+
151
+ def test_hash_content
152
+ topic = Topic.new
153
+ topic.content = { "one" => 1, "two" => 2 }
154
+ topic.save
155
+
156
+ assert_equal 2, Topic.find(topic.id).content["two"]
157
+
158
+ topic.content_will_change!
159
+ topic.content["three"] = 3
160
+ topic.save
161
+
162
+ assert_equal 3, Topic.find(topic.id).content["three"]
163
+ end
164
+
165
+ def test_update_array_content
166
+ topic = Topic.new
167
+ topic.content = %w( one two three )
168
+
169
+ topic.content.push "four"
170
+ assert_equal(%w( one two three four ), topic.content)
171
+
172
+ topic.save
173
+
174
+ topic = Topic.find(topic.id)
175
+ topic.content << "five"
176
+ assert_equal(%w( one two three four five ), topic.content)
177
+ end
178
+
179
+ def test_case_sensitive_attributes_hash
180
+ # DB2 is not case-sensitive
181
+ return true if current_adapter?(:DB2Adapter)
182
+
183
+ assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
184
+ end
185
+
186
+ def test_create
187
+ topic = Topic.new
188
+ topic.title = "New Topic"
189
+ topic.save
190
+ topic_reloaded = Topic.find(topic.id)
191
+ assert_equal("New Topic", topic_reloaded.title)
192
+ end
193
+
194
+ def test_save!
195
+ topic = Topic.new(:title => "New Topic")
196
+ assert topic.save!
197
+
198
+ reply = Reply.new
199
+ assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
200
+ end
201
+
202
+ def test_save_null_string_attributes
203
+ topic = Topic.find(1)
204
+ topic.attributes = { "title" => "null", "author_name" => "null" }
205
+ topic.save!
206
+ topic.reload
207
+ assert_equal("null", topic.title)
208
+ assert_equal("null", topic.author_name)
209
+ end
210
+
211
+ def test_save_nil_string_attributes
212
+ topic = Topic.find(1)
213
+ topic.title = nil
214
+ topic.save!
215
+ topic.reload
216
+ assert_nil topic.title
217
+ end
218
+
219
+ def test_save_for_record_with_only_primary_key
220
+ minimalistic = Minimalistic.new
221
+ assert_nothing_raised { minimalistic.save }
222
+ end
223
+
224
+ def test_save_for_record_with_only_primary_key_that_is_provided
225
+ assert_nothing_raised { Minimalistic.create!(:id => 2) }
226
+ end
227
+
228
+ def test_hashes_not_mangled
229
+ new_topic = { :title => "New Topic" }
230
+ new_topic_values = { :title => "AnotherTopic" }
231
+
232
+ topic = Topic.new(new_topic)
233
+ assert_equal new_topic[:title], topic.title
234
+
235
+ topic.attributes= new_topic_values
236
+ assert_equal new_topic_values[:title], topic.title
237
+ end
238
+
239
+ def test_create_many
240
+ topics = Topic.create([ { "title" => "first" }, { "title" => "second" }])
241
+ assert_equal 2, topics.size
242
+ assert_equal "first", topics.first.title
243
+ end
244
+
245
+ def test_create_columns_not_equal_attributes
246
+ topic = Topic.new
247
+ topic.title = 'Another New Topic'
248
+ topic.send :write_attribute, 'does_not_exist', 'test'
249
+ assert_nothing_raised { topic.save }
250
+ end
251
+
252
+ def test_create_through_factory
253
+ topic = Topic.create("title" => "New Topic")
254
+ topicReloaded = Topic.find(topic.id)
255
+ assert_equal(topic, topicReloaded)
256
+ end
257
+
258
+ def test_create_through_factory_with_block
259
+ topic = Topic.create("title" => "New Topic") do |t|
260
+ t.author_name = "David"
261
+ end
262
+ topicReloaded = Topic.find(topic.id)
263
+ assert_equal("New Topic", topic.title)
264
+ assert_equal("David", topic.author_name)
265
+ end
266
+
267
+ def test_create_many_through_factory_with_block
268
+ topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t|
269
+ t.author_name = "David"
270
+ end
271
+ assert_equal 2, topics.size
272
+ topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id)
273
+ assert_equal "first", topic1.title
274
+ assert_equal "David", topic1.author_name
275
+ assert_equal "second", topic2.title
276
+ assert_equal "David", topic2.author_name
277
+ end
278
+
279
+ def test_update
280
+ topic = Topic.new
281
+ topic.title = "Another New Topic"
282
+ topic.written_on = "2003-12-12 23:23:00"
283
+ topic.save
284
+ topicReloaded = Topic.find(topic.id)
285
+ assert_equal("Another New Topic", topicReloaded.title)
286
+
287
+ topicReloaded.title = "Updated topic"
288
+ topicReloaded.save
289
+
290
+ topicReloadedAgain = Topic.find(topic.id)
291
+
292
+ assert_equal("Updated topic", topicReloadedAgain.title)
293
+ end
294
+
295
+ def test_update_columns_not_equal_attributes
296
+ topic = Topic.new
297
+ topic.title = "Still another topic"
298
+ topic.save
299
+
300
+ topicReloaded = Topic.find(topic.id)
301
+ topicReloaded.title = "A New Topic"
302
+ topicReloaded.send :write_attribute, 'does_not_exist', 'test'
303
+ assert_nothing_raised { topicReloaded.save }
304
+ end
305
+
306
+ def test_update_for_record_with_only_primary_key
307
+ minimalistic = minimalistics(:first)
308
+ assert_nothing_raised { minimalistic.save }
309
+ end
310
+
311
+ def test_write_attribute
312
+ topic = Topic.new
313
+ topic.send(:write_attribute, :title, "Still another topic")
314
+ assert_equal "Still another topic", topic.title
315
+
316
+ topic.send(:write_attribute, "title", "Still another topic: part 2")
317
+ assert_equal "Still another topic: part 2", topic.title
318
+ end
319
+
320
+ def test_read_attribute
321
+ topic = Topic.new
322
+ topic.title = "Don't change the topic"
323
+ assert_equal "Don't change the topic", topic.send(:read_attribute, "title")
324
+ assert_equal "Don't change the topic", topic["title"]
325
+
326
+ assert_equal "Don't change the topic", topic.send(:read_attribute, :title)
327
+ assert_equal "Don't change the topic", topic[:title]
328
+ end
329
+
330
+ def test_read_attribute_when_false
331
+ topic = topics(:first)
332
+ topic.approved = false
333
+ assert !topic.approved?, "approved should be false"
334
+ topic.approved = "false"
335
+ assert !topic.approved?, "approved should be false"
336
+ end
337
+
338
+ def test_read_attribute_when_true
339
+ topic = topics(:first)
340
+ topic.approved = true
341
+ assert topic.approved?, "approved should be true"
342
+ topic.approved = "true"
343
+ assert topic.approved?, "approved should be true"
344
+ end
345
+
346
+ def test_read_write_boolean_attribute
347
+ topic = Topic.new
348
+ # puts ""
349
+ # puts "New Topic"
350
+ # puts topic.inspect
351
+ topic.approved = "false"
352
+ # puts "Expecting false"
353
+ # puts topic.inspect
354
+ assert !topic.approved?, "approved should be false"
355
+ topic.approved = "false"
356
+ # puts "Expecting false"
357
+ # puts topic.inspect
358
+ assert !topic.approved?, "approved should be false"
359
+ topic.approved = "true"
360
+ # puts "Expecting true"
361
+ # puts topic.inspect
362
+ assert topic.approved?, "approved should be true"
363
+ topic.approved = "true"
364
+ # puts "Expecting true"
365
+ # puts topic.inspect
366
+ assert topic.approved?, "approved should be true"
367
+ # puts ""
368
+ end
369
+
370
+ def test_query_attribute_string
371
+ [nil, "", " "].each do |value|
372
+ assert_equal false, Topic.new(:author_name => value).author_name?
373
+ end
374
+
375
+ assert_equal true, Topic.new(:author_name => "Name").author_name?
376
+ end
377
+
378
+ def test_query_attribute_number
379
+ [nil, 0, "0"].each do |value|
380
+ assert_equal false, Developer.new(:salary => value).salary?
381
+ end
382
+
383
+ assert_equal true, Developer.new(:salary => 1).salary?
384
+ assert_equal true, Developer.new(:salary => "1").salary?
385
+ end
386
+
387
+ def test_query_attribute_boolean
388
+ [nil, "", false, "false", "f", 0].each do |value|
389
+ assert_equal false, Topic.new(:approved => value).approved?
390
+ end
391
+
392
+ [true, "true", "1", 1].each do |value|
393
+ assert_equal true, Topic.new(:approved => value).approved?
394
+ end
395
+ end
396
+
397
+ def test_query_attribute_with_custom_fields
398
+ object = Company.find_by_sql(<<-SQL).first
399
+ SELECT c1.*, c2.ruby_type as string_value, c2.rating as int_value
400
+ FROM companies c1, companies c2
401
+ WHERE c1.firm_id = c2.id
402
+ AND c1.id = 2
403
+ SQL
404
+
405
+ assert_equal "Firm", object.string_value
406
+ assert object.string_value?
407
+
408
+ object.string_value = " "
409
+ assert !object.string_value?
410
+
411
+ assert_equal 1, object.int_value.to_i
412
+ assert object.int_value?
413
+
414
+ object.int_value = "0"
415
+ assert !object.int_value?
416
+ end
417
+
418
+
419
+ def test_reader_for_invalid_column_names
420
+ Topic.send(:define_read_method, "mumub-jumbo".to_sym, "mumub-jumbo", nil)
421
+ assert !Topic.generated_methods.include?("mumub-jumbo")
422
+ end
423
+
424
+ def test_non_attribute_access_and_assignment
425
+ topic = Topic.new
426
+ assert !topic.respond_to?("mumbo")
427
+ assert_raise(NoMethodError) { topic.mumbo }
428
+ assert_raise(NoMethodError) { topic.mumbo = 5 }
429
+ end
430
+
431
+ def test_preserving_date_objects
432
+ if current_adapter?(:SybaseAdapter, :OracleAdapter)
433
+ # Sybase ctlib does not (yet?) support the date type; use datetime instead.
434
+ # Oracle treats all dates/times as Time.
435
+ assert_kind_of(
436
+ Time, Topic.find(1).last_read,
437
+ "The last_read attribute should be of the Time class"
438
+ )
439
+ else
440
+ assert_kind_of(
441
+ Date, Topic.find(1).last_read,
442
+ "The last_read attribute should be of the Date class"
443
+ )
444
+ end
445
+ end
446
+
447
+ def test_preserving_time_objects
448
+ assert_kind_of(
449
+ Time, Topic.find(1).bonus_time,
450
+ "The bonus_time attribute should be of the Time class"
451
+ )
452
+
453
+ assert_kind_of(
454
+ Time, Topic.find(1).written_on,
455
+ "The written_on attribute should be of the Time class"
456
+ )
457
+
458
+ # For adapters which support microsecond resolution.
459
+ if current_adapter?(:PostgreSQLAdapter)
460
+ assert_equal 11, Topic.find(1).written_on.sec
461
+ assert_equal 223300, Topic.find(1).written_on.usec
462
+ assert_equal 9900, Topic.find(2).written_on.usec
463
+ end
464
+ end
465
+
466
+ def test_custom_mutator
467
+ topic = Topic.find(1)
468
+ # This mutator is protected in the class definition
469
+ topic.send(:approved=, true)
470
+ assert topic.instance_variable_get("@custom_approved")
471
+ end
472
+
473
+ def test_delete
474
+ topic = Topic.find(1)
475
+ assert_equal topic, topic.delete, 'topic.delete did not return self'
476
+ assert topic.frozen?, 'topic not frozen after delete'
477
+ assert topic.destroyed?, 'topic not marked as being destroyed'
478
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
479
+ end
480
+
481
+ def test_delete_doesnt_run_callbacks
482
+ Topic.find(1).delete
483
+ assert_not_nil Topic.find(2)
484
+ end
485
+
486
+ def test_destroy
487
+ topic = Topic.find(1)
488
+ assert_equal topic, topic.destroy, 'topic.destroy did not return self'
489
+ assert topic.frozen?, 'topic not frozen after destroy'
490
+ assert topic.destroyed?, 'topic not marked as being destroyed'
491
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
492
+ end
493
+
494
+ def test_record_not_found_exception
495
+ assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
496
+ end
497
+
498
+ def test_initialize_with_attributes
499
+ topic = Topic.new({
500
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
501
+ })
502
+
503
+ assert_equal("initialized from attributes", topic.title)
504
+ end
505
+
506
+ def test_initialize_with_invalid_attribute
507
+ begin
508
+ topic = Topic.new({ "title" => "test",
509
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
510
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
511
+ assert_equal(1, ex.errors.size)
512
+ assert_equal("last_read", ex.errors[0].attribute)
513
+ end
514
+ end
515
+
516
+ def test_load
517
+ topics = Topic.find(:all, :order => 'id')
518
+ assert_equal(4, topics.size)
519
+ assert_equal(topics(:first).title, topics.first.title)
520
+ end
521
+
522
+ def test_load_with_condition
523
+ topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
524
+
525
+ assert_equal(1, topics.size)
526
+ assert_equal(topics(:second).title, topics.first.title)
527
+ end
528
+
529
+ def test_table_name_guesses
530
+ classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
531
+
532
+ assert_equal "topics", Topic.table_name
533
+
534
+ assert_equal "categories", Category.table_name
535
+ assert_equal "smarts", Smarts.table_name
536
+ assert_equal "credit_cards", CreditCard.table_name
537
+ assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
538
+ assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
539
+ assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
540
+ assert_equal "categories", CreditCard::Brand.table_name
541
+ assert_equal "master_credit_cards", MasterCreditCard.table_name
542
+
543
+ ActiveRecord::Base.pluralize_table_names = false
544
+ classes.each(&:reset_table_name)
545
+
546
+ assert_equal "category", Category.table_name
547
+ assert_equal "smarts", Smarts.table_name
548
+ assert_equal "credit_card", CreditCard.table_name
549
+ assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
550
+ assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
551
+ assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
552
+ assert_equal "category", CreditCard::Brand.table_name
553
+ assert_equal "master_credit_card", MasterCreditCard.table_name
554
+
555
+ ActiveRecord::Base.pluralize_table_names = true
556
+ classes.each(&:reset_table_name)
557
+
558
+ ActiveRecord::Base.table_name_prefix = "test_"
559
+ Category.reset_table_name
560
+ assert_equal "test_categories", Category.table_name
561
+ ActiveRecord::Base.table_name_suffix = "_test"
562
+ Category.reset_table_name
563
+ assert_equal "test_categories_test", Category.table_name
564
+ ActiveRecord::Base.table_name_prefix = ""
565
+ Category.reset_table_name
566
+ assert_equal "categories_test", Category.table_name
567
+ ActiveRecord::Base.table_name_suffix = ""
568
+ Category.reset_table_name
569
+ assert_equal "categories", Category.table_name
570
+
571
+ ActiveRecord::Base.pluralize_table_names = false
572
+ ActiveRecord::Base.table_name_prefix = "test_"
573
+ Category.reset_table_name
574
+ assert_equal "test_category", Category.table_name
575
+ ActiveRecord::Base.table_name_suffix = "_test"
576
+ Category.reset_table_name
577
+ assert_equal "test_category_test", Category.table_name
578
+ ActiveRecord::Base.table_name_prefix = ""
579
+ Category.reset_table_name
580
+ assert_equal "category_test", Category.table_name
581
+ ActiveRecord::Base.table_name_suffix = ""
582
+ Category.reset_table_name
583
+ assert_equal "category", Category.table_name
584
+
585
+ ActiveRecord::Base.pluralize_table_names = true
586
+ classes.each(&:reset_table_name)
587
+ end
588
+
589
+ def test_destroy_all
590
+ original_count = Topic.count
591
+ topics_by_mary = Topic.count(:conditions => mary = "author_name = 'Mary'")
592
+
593
+ Topic.destroy_all mary
594
+ assert_equal original_count - topics_by_mary, Topic.count
595
+ end
596
+
597
+ def test_destroy_many
598
+ assert_equal 3, Client.count
599
+ Client.destroy([2, 3])
600
+ assert_equal 1, Client.count
601
+ end
602
+
603
+ def test_delete_many
604
+ original_count = Topic.count
605
+ Topic.delete(deleting = [1, 2])
606
+ assert_equal original_count - deleting.size, Topic.count
607
+ end
608
+
609
+ def test_boolean_attributes
610
+ assert ! Topic.find(1).approved?
611
+ assert Topic.find(2).approved?
612
+ end
613
+
614
+ def test_increment_counter
615
+ Topic.increment_counter("replies_count", 1)
616
+ assert_equal 2, Topic.find(1).replies_count
617
+
618
+ Topic.increment_counter("replies_count", 1)
619
+ assert_equal 3, Topic.find(1).replies_count
620
+ end
621
+
622
+ def test_decrement_counter
623
+ Topic.decrement_counter("replies_count", 2)
624
+ assert_equal -1, Topic.find(2).replies_count
625
+
626
+ Topic.decrement_counter("replies_count", 2)
627
+ assert_equal -2, Topic.find(2).replies_count
628
+ end
629
+
630
+ def test_update_counter
631
+ category = categories(:general)
632
+ assert_nil category.categorizations_count
633
+ assert_equal 2, category.categorizations.count
634
+
635
+ Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
636
+ category.reload
637
+ assert_not_nil category.categorizations_count
638
+ assert_equal 2, category.categorizations_count
639
+
640
+ Category.update_counters(category.id, "categorizations_count" => category.categorizations.count)
641
+ category.reload
642
+ assert_not_nil category.categorizations_count
643
+ assert_equal 4, category.categorizations_count
644
+
645
+ category_2 = categories(:technology)
646
+ count_1, count_2 = (category.categorizations_count || 0), (category_2.categorizations_count || 0)
647
+ Category.update_counters([category.id, category_2.id], "categorizations_count" => 2)
648
+ category.reload; category_2.reload
649
+ assert_equal count_1 + 2, category.categorizations_count
650
+ assert_equal count_2 + 2, category_2.categorizations_count
651
+ end
652
+
653
+ def test_update_all
654
+ assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'")
655
+ assert_equal "bulk updated!", Topic.find(1).content
656
+ assert_equal "bulk updated!", Topic.find(2).content
657
+
658
+ assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!'])
659
+ assert_equal "bulk updated again!", Topic.find(1).content
660
+ assert_equal "bulk updated again!", Topic.find(2).content
661
+
662
+ assert_equal Topic.count, Topic.update_all(['content = ?', nil])
663
+ assert_nil Topic.find(1).content
664
+ end
665
+
666
+ def test_update_all_with_hash
667
+ assert_not_nil Topic.find(1).last_read
668
+ assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil)
669
+ assert_equal "bulk updated with hash!", Topic.find(1).content
670
+ assert_equal "bulk updated with hash!", Topic.find(2).content
671
+ assert_nil Topic.find(1).last_read
672
+ assert_nil Topic.find(2).last_read
673
+ end
674
+
675
+ def test_update_all_with_non_standard_table_name
676
+ assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1])
677
+ assert_equal 0, WarehouseThing.find(1).value
678
+ end
679
+
680
+ if current_adapter?(:MysqlAdapter)
681
+ def test_update_all_with_order_and_limit
682
+ assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
683
+ end
684
+ end
685
+
686
+ def test_update_all_ignores_order_without_limit_from_association
687
+ author = authors(:david)
688
+ assert_nothing_raised do
689
+ assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
690
+ end
691
+ end
692
+
693
+ def test_update_all_with_order_and_limit_updates_subset_only
694
+ author = authors(:david)
695
+ assert_nothing_raised do
696
+ assert_equal 1, author.posts_sorted_by_id_limited.size
697
+ assert_equal 2, author.posts_sorted_by_id_limited.find(:all, :limit => 2).size
698
+ assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ])
699
+ assert_equal "bulk update!", posts(:welcome).body
700
+ assert_not_equal "bulk update!", posts(:thinking).body
701
+ end
702
+ end
703
+
704
+ def test_update_many
705
+ topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
706
+ updated = Topic.update(topic_data.keys, topic_data.values)
707
+
708
+ assert_equal 2, updated.size
709
+ assert_equal "1 updated", Topic.find(1).content
710
+ assert_equal "2 updated", Topic.find(2).content
711
+ end
712
+
713
+ def test_delete_all
714
+ assert Topic.count > 0
715
+
716
+ assert_equal Topic.count, Topic.delete_all
717
+ end
718
+
719
+ def test_update_by_condition
720
+ Topic.update_all "content = 'bulk updated!'", ["approved = ?", true]
721
+ assert_equal "Have a nice day", Topic.find(1).content
722
+ assert_equal "bulk updated!", Topic.find(2).content
723
+ end
724
+
725
+ def test_attribute_present
726
+ t = Topic.new
727
+ t.title = "hello there!"
728
+ t.written_on = Time.now
729
+ assert t.attribute_present?("title")
730
+ assert t.attribute_present?("written_on")
731
+ assert !t.attribute_present?("content")
732
+ end
733
+
734
+ def test_attribute_keys_on_new_instance
735
+ t = Topic.new
736
+ assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
737
+ assert_raise(NoMethodError) { t.title2 }
738
+ end
739
+
740
+ def test_class_name
741
+ assert_equal "Firm", ActiveRecord::Base.class_name("firms")
742
+ assert_equal "Category", ActiveRecord::Base.class_name("categories")
743
+ assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")
744
+
745
+ ActiveRecord::Base.pluralize_table_names = false
746
+ assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
747
+ ActiveRecord::Base.pluralize_table_names = true
748
+
749
+ ActiveRecord::Base.table_name_prefix = "test_"
750
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
751
+ ActiveRecord::Base.table_name_suffix = "_tests"
752
+ assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
753
+ ActiveRecord::Base.table_name_prefix = ""
754
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
755
+ ActiveRecord::Base.table_name_suffix = ""
756
+ assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
757
+ end
758
+
759
+ def test_null_fields
760
+ assert_nil Topic.find(1).parent_id
761
+ assert_nil Topic.create("title" => "Hey you").parent_id
762
+ end
763
+
764
+ def test_default_values
765
+ topic = Topic.new
766
+ assert topic.approved?
767
+ assert_nil topic.written_on
768
+ assert_nil topic.bonus_time
769
+ assert_nil topic.last_read
770
+
771
+ topic.save
772
+
773
+ topic = Topic.find(topic.id)
774
+ assert topic.approved?
775
+ assert_nil topic.last_read
776
+
777
+ # Oracle has some funky default handling, so it requires a bit of
778
+ # extra testing. See ticket #2788.
779
+ if current_adapter?(:OracleAdapter)
780
+ test = TestOracleDefault.new
781
+ assert_equal "X", test.test_char
782
+ assert_equal "hello", test.test_string
783
+ assert_equal 3, test.test_int
784
+ end
785
+ end
786
+
787
+ # Oracle, and Sybase do not have a TIME datatype.
788
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
789
+ def test_utc_as_time_zone
790
+ Topic.default_timezone = :utc
791
+ attributes = { "bonus_time" => "5:42:00AM" }
792
+ topic = Topic.find(1)
793
+ topic.attributes = attributes
794
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
795
+ Topic.default_timezone = :local
796
+ end
797
+
798
+ def test_utc_as_time_zone_and_new
799
+ Topic.default_timezone = :utc
800
+ attributes = { "bonus_time(1i)"=>"2000",
801
+ "bonus_time(2i)"=>"1",
802
+ "bonus_time(3i)"=>"1",
803
+ "bonus_time(4i)"=>"10",
804
+ "bonus_time(5i)"=>"35",
805
+ "bonus_time(6i)"=>"50" }
806
+ topic = Topic.new(attributes)
807
+ assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
808
+ Topic.default_timezone = :local
809
+ end
810
+ end
811
+
812
+ def test_default_values_on_empty_strings
813
+ topic = Topic.new
814
+ topic.approved = nil
815
+ topic.last_read = nil
816
+
817
+ topic.save
818
+
819
+ topic = Topic.find(topic.id)
820
+ assert_nil topic.last_read
821
+
822
+ # Sybase adapter does not allow nulls in boolean columns
823
+ if current_adapter?(:SybaseAdapter)
824
+ assert topic.approved == false
825
+ else
826
+ assert_nil topic.approved
827
+ end
828
+ end
829
+
830
+ def test_equality
831
+ assert_equal Topic.find(1), Topic.find(2).topic
832
+ end
833
+
834
+ def test_equality_of_new_records
835
+ assert_not_equal Topic.new, Topic.new
836
+ end
837
+
838
+ def test_hashing
839
+ assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
840
+ end
841
+
842
+ def test_delete_new_record
843
+ client = Client.new
844
+ client.delete
845
+ assert client.frozen?
846
+ end
847
+
848
+ def test_delete_record_with_associations
849
+ client = Client.find(3)
850
+ client.delete
851
+ assert client.frozen?
852
+ assert_kind_of Firm, client.firm
853
+ assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" }
854
+ end
855
+
856
+ def test_destroy_new_record
857
+ client = Client.new
858
+ client.destroy
859
+ assert client.frozen?
860
+ end
861
+
862
+ def test_destroy_record_with_associations
863
+ client = Client.find(3)
864
+ client.destroy
865
+ assert client.frozen?
866
+ assert_kind_of Firm, client.firm
867
+ assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" }
868
+ end
869
+
870
+ def test_update_attribute
871
+ assert !Topic.find(1).approved?
872
+ Topic.find(1).update_attribute("approved", true)
873
+ assert Topic.find(1).approved?
874
+
875
+ Topic.find(1).update_attribute(:approved, false)
876
+ assert !Topic.find(1).approved?
877
+ end
878
+
879
+ def test_update_attributes
880
+ topic = Topic.find(1)
881
+ assert !topic.approved?
882
+ assert_equal "The First Topic", topic.title
883
+
884
+ topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
885
+ topic.reload
886
+ assert topic.approved?
887
+ assert_equal "The First Topic Updated", topic.title
888
+
889
+ topic.update_attributes(:approved => false, :title => "The First Topic")
890
+ topic.reload
891
+ assert !topic.approved?
892
+ assert_equal "The First Topic", topic.title
893
+ end
894
+
895
+ def test_update_attributes!
896
+ reply = Reply.find(2)
897
+ assert_equal "The Second Topic of the day", reply.title
898
+ assert_equal "Have a nice day", reply.content
899
+
900
+ reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening")
901
+ reply.reload
902
+ assert_equal "The Second Topic of the day updated", reply.title
903
+ assert_equal "Have a nice evening", reply.content
904
+
905
+ reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day")
906
+ reply.reload
907
+ assert_equal "The Second Topic of the day", reply.title
908
+ assert_equal "Have a nice day", reply.content
909
+
910
+ assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
911
+ end
912
+
913
+ def test_mass_assignment_should_raise_exception_if_accessible_and_protected_attribute_writers_are_both_used
914
+ topic = TopicWithProtectedContentAndAccessibleAuthorName.new
915
+ assert_raise(RuntimeError) { topic.attributes = { "author_name" => "me" } }
916
+ assert_raise(RuntimeError) { topic.attributes = { "content" => "stuff" } }
917
+ end
918
+
919
+ def test_mass_assignment_protection
920
+ firm = Firm.new
921
+ firm.attributes = { "name" => "Next Angle", "rating" => 5 }
922
+ assert_equal 1, firm.rating
923
+ end
924
+
925
+ def test_mass_assignment_protection_against_class_attribute_writers
926
+ [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :colorize_logging,
927
+ :default_timezone, :schema_format, :lock_optimistically, :record_timestamps].each do |method|
928
+ assert Task.respond_to?(method)
929
+ assert Task.respond_to?("#{method}=")
930
+ assert Task.new.respond_to?(method)
931
+ assert !Task.new.respond_to?("#{method}=")
932
+ end
933
+ end
934
+
935
+ def test_customized_primary_key_remains_protected
936
+ subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
937
+ assert_nil subscriber.id
938
+
939
+ keyboard = Keyboard.new(:key_number => 9, :name => 'nice try')
940
+ assert_nil keyboard.id
941
+ end
942
+
943
+ def test_customized_primary_key_remains_protected_when_referred_to_as_id
944
+ subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try')
945
+ assert_nil subscriber.id
946
+
947
+ keyboard = Keyboard.new(:id => 9, :name => 'nice try')
948
+ assert_nil keyboard.id
949
+ end
950
+
951
+ def test_mass_assigning_invalid_attribute
952
+ firm = Firm.new
953
+
954
+ assert_raise(ActiveRecord::UnknownAttributeError) do
955
+ firm.attributes = { "id" => 5, "type" => "Client", "i_dont_even_exist" => 20 }
956
+ end
957
+ end
958
+
959
+ def test_mass_assignment_protection_on_defaults
960
+ firm = Firm.new
961
+ firm.attributes = { "id" => 5, "type" => "Client" }
962
+ assert_nil firm.id
963
+ assert_equal "Firm", firm[:type]
964
+ end
965
+
966
+ def test_mass_assignment_accessible
967
+ reply = Reply.new("title" => "hello", "content" => "world", "approved" => true)
968
+ reply.save
969
+
970
+ assert reply.approved?
971
+
972
+ reply.approved = false
973
+ reply.save
974
+
975
+ assert !reply.approved?
976
+ end
977
+
978
+ def test_mass_assignment_protection_inheritance
979
+ assert_nil LoosePerson.accessible_attributes
980
+ assert_equal Set.new([ 'credit_rating', 'administrator' ]), LoosePerson.protected_attributes
981
+
982
+ assert_nil LooseDescendant.accessible_attributes
983
+ assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number' ]), LooseDescendant.protected_attributes
984
+
985
+ assert_nil LooseDescendantSecond.accessible_attributes
986
+ assert_equal Set.new([ 'credit_rating', 'administrator', 'phone_number', 'name' ]), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections'
987
+
988
+ assert_nil TightPerson.protected_attributes
989
+ assert_equal Set.new([ 'name', 'address' ]), TightPerson.accessible_attributes
990
+
991
+ assert_nil TightDescendant.protected_attributes
992
+ assert_equal Set.new([ 'name', 'address', 'phone_number' ]), TightDescendant.accessible_attributes
993
+ end
994
+
995
+ def test_readonly_attributes
996
+ assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
997
+
998
+ post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
999
+ post.reload
1000
+ assert_equal "cannot change this", post.title
1001
+
1002
+ post.update_attributes(:title => "try to change", :body => "changed")
1003
+ post.reload
1004
+ assert_equal "cannot change this", post.title
1005
+ assert_equal "changed", post.body
1006
+ end
1007
+
1008
+ def test_multiparameter_attributes_on_date
1009
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
1010
+ topic = Topic.find(1)
1011
+ topic.attributes = attributes
1012
+ # note that extra #to_date call allows test to pass for Oracle, which
1013
+ # treats dates/times the same
1014
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
1015
+ end
1016
+
1017
+ def test_multiparameter_attributes_on_date_with_empty_year
1018
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
1019
+ topic = Topic.find(1)
1020
+ topic.attributes = attributes
1021
+ # note that extra #to_date call allows test to pass for Oracle, which
1022
+ # treats dates/times the same
1023
+ assert_date_from_db Date.new(1, 6, 24), topic.last_read.to_date
1024
+ end
1025
+
1026
+ def test_multiparameter_attributes_on_date_with_empty_month
1027
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
1028
+ topic = Topic.find(1)
1029
+ topic.attributes = attributes
1030
+ # note that extra #to_date call allows test to pass for Oracle, which
1031
+ # treats dates/times the same
1032
+ assert_date_from_db Date.new(2004, 1, 24), topic.last_read.to_date
1033
+ end
1034
+
1035
+ def test_multiparameter_attributes_on_date_with_empty_day
1036
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
1037
+ topic = Topic.find(1)
1038
+ topic.attributes = attributes
1039
+ # note that extra #to_date call allows test to pass for Oracle, which
1040
+ # treats dates/times the same
1041
+ assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date
1042
+ end
1043
+
1044
+ def test_multiparameter_attributes_on_date_with_empty_day_and_year
1045
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
1046
+ topic = Topic.find(1)
1047
+ topic.attributes = attributes
1048
+ # note that extra #to_date call allows test to pass for Oracle, which
1049
+ # treats dates/times the same
1050
+ assert_date_from_db Date.new(1, 6, 1), topic.last_read.to_date
1051
+ end
1052
+
1053
+ def test_multiparameter_attributes_on_date_with_empty_day_and_month
1054
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
1055
+ topic = Topic.find(1)
1056
+ topic.attributes = attributes
1057
+ # note that extra #to_date call allows test to pass for Oracle, which
1058
+ # treats dates/times the same
1059
+ assert_date_from_db Date.new(2004, 1, 1), topic.last_read.to_date
1060
+ end
1061
+
1062
+ def test_multiparameter_attributes_on_date_with_empty_year_and_month
1063
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
1064
+ topic = Topic.find(1)
1065
+ topic.attributes = attributes
1066
+ # note that extra #to_date call allows test to pass for Oracle, which
1067
+ # treats dates/times the same
1068
+ assert_date_from_db Date.new(1, 1, 24), topic.last_read.to_date
1069
+ end
1070
+
1071
+ def test_multiparameter_attributes_on_date_with_all_empty
1072
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
1073
+ topic = Topic.find(1)
1074
+ topic.attributes = attributes
1075
+ assert_nil topic.last_read
1076
+ end
1077
+
1078
+ def test_multiparameter_attributes_on_time
1079
+ attributes = {
1080
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1081
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1082
+ }
1083
+ topic = Topic.find(1)
1084
+ topic.attributes = attributes
1085
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1086
+ end
1087
+
1088
+ def test_multiparameter_attributes_on_time_with_old_date
1089
+ attributes = {
1090
+ "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
1091
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1092
+ }
1093
+ topic = Topic.find(1)
1094
+ topic.attributes = attributes
1095
+ # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
1096
+ assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
1097
+ end
1098
+
1099
+ def test_multiparameter_attributes_on_time_with_utc
1100
+ ActiveRecord::Base.default_timezone = :utc
1101
+ attributes = {
1102
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1103
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1104
+ }
1105
+ topic = Topic.find(1)
1106
+ topic.attributes = attributes
1107
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
1108
+ ensure
1109
+ ActiveRecord::Base.default_timezone = :local
1110
+ end
1111
+
1112
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
1113
+ ActiveRecord::Base.time_zone_aware_attributes = true
1114
+ ActiveRecord::Base.default_timezone = :utc
1115
+ Time.zone = ActiveSupport::TimeZone[-28800]
1116
+ attributes = {
1117
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1118
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1119
+ }
1120
+ topic = Topic.find(1)
1121
+ topic.attributes = attributes
1122
+ assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
1123
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
1124
+ assert_equal Time.zone, topic.written_on.time_zone
1125
+ ensure
1126
+ ActiveRecord::Base.time_zone_aware_attributes = false
1127
+ ActiveRecord::Base.default_timezone = :local
1128
+ Time.zone = nil
1129
+ end
1130
+
1131
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
1132
+ ActiveRecord::Base.time_zone_aware_attributes = false
1133
+ Time.zone = ActiveSupport::TimeZone[-28800]
1134
+ attributes = {
1135
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1136
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1137
+ }
1138
+ topic = Topic.find(1)
1139
+ topic.attributes = attributes
1140
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1141
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
1142
+ ensure
1143
+ Time.zone = nil
1144
+ end
1145
+
1146
+ def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
1147
+ ActiveRecord::Base.time_zone_aware_attributes = true
1148
+ ActiveRecord::Base.default_timezone = :utc
1149
+ Time.zone = ActiveSupport::TimeZone[-28800]
1150
+ Topic.skip_time_zone_conversion_for_attributes = [:written_on]
1151
+ attributes = {
1152
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1153
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
1154
+ }
1155
+ topic = Topic.find(1)
1156
+ topic.attributes = attributes
1157
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
1158
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
1159
+ ensure
1160
+ ActiveRecord::Base.time_zone_aware_attributes = false
1161
+ ActiveRecord::Base.default_timezone = :local
1162
+ Time.zone = nil
1163
+ Topic.skip_time_zone_conversion_for_attributes = []
1164
+ end
1165
+
1166
+ def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
1167
+ ActiveRecord::Base.time_zone_aware_attributes = true
1168
+ ActiveRecord::Base.default_timezone = :utc
1169
+ Time.zone = ActiveSupport::TimeZone[-28800]
1170
+ attributes = {
1171
+ "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
1172
+ "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
1173
+ }
1174
+ topic = Topic.find(1)
1175
+ topic.attributes = attributes
1176
+ assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
1177
+ assert topic.bonus_time.utc?
1178
+ ensure
1179
+ ActiveRecord::Base.time_zone_aware_attributes = false
1180
+ ActiveRecord::Base.default_timezone = :local
1181
+ Time.zone = nil
1182
+ end
1183
+
1184
+ def test_multiparameter_attributes_on_time_with_empty_seconds
1185
+ attributes = {
1186
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
1187
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
1188
+ }
1189
+ topic = Topic.find(1)
1190
+ topic.attributes = attributes
1191
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
1192
+ end
1193
+
1194
+ def test_multiparameter_mass_assignment_protector
1195
+ task = Task.new
1196
+ time = Time.mktime(2000, 1, 1, 1)
1197
+ task.starting = time
1198
+ attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" }
1199
+ task.attributes = attributes
1200
+ assert_equal time, task.starting
1201
+ end
1202
+
1203
+ def test_multiparameter_assignment_of_aggregation
1204
+ customer = Customer.new
1205
+ address = Address.new("The Street", "The City", "The Country")
1206
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
1207
+ customer.attributes = attributes
1208
+ assert_equal address, customer.address
1209
+ end
1210
+
1211
+ def test_attributes_on_dummy_time
1212
+ # Oracle, and Sybase do not have a TIME datatype.
1213
+ return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
1214
+
1215
+ attributes = {
1216
+ "bonus_time" => "5:42:00AM"
1217
+ }
1218
+ topic = Topic.find(1)
1219
+ topic.attributes = attributes
1220
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
1221
+ end
1222
+
1223
+ def test_boolean
1224
+ b_nil = Booleantest.create({ "value" => nil })
1225
+ nil_id = b_nil.id
1226
+ b_false = Booleantest.create({ "value" => false })
1227
+ false_id = b_false.id
1228
+ b_true = Booleantest.create({ "value" => true })
1229
+ true_id = b_true.id
1230
+
1231
+ b_nil = Booleantest.find(nil_id)
1232
+ assert_nil b_nil.value
1233
+ b_false = Booleantest.find(false_id)
1234
+ assert !b_false.value?
1235
+ b_true = Booleantest.find(true_id)
1236
+ assert b_true.value?
1237
+ end
1238
+
1239
+ def test_boolean_cast_from_string
1240
+ b_blank = Booleantest.create({ "value" => "" })
1241
+ blank_id = b_blank.id
1242
+ b_false = Booleantest.create({ "value" => "0" })
1243
+ false_id = b_false.id
1244
+ b_true = Booleantest.create({ "value" => "1" })
1245
+ true_id = b_true.id
1246
+
1247
+ b_blank = Booleantest.find(blank_id)
1248
+ assert_nil b_blank.value
1249
+ b_false = Booleantest.find(false_id)
1250
+ assert !b_false.value?
1251
+ b_true = Booleantest.find(true_id)
1252
+ assert b_true.value?
1253
+ end
1254
+
1255
+ def test_new_record_returns_boolean
1256
+ assert_equal Topic.new.new_record?, true
1257
+ assert_equal Topic.find(1).new_record?, false
1258
+ end
1259
+
1260
+ def test_clone
1261
+ topic = Topic.find(1)
1262
+ cloned_topic = nil
1263
+ assert_nothing_raised { cloned_topic = topic.clone }
1264
+ assert_equal topic.title, cloned_topic.title
1265
+ assert cloned_topic.new_record?
1266
+
1267
+ # test if the attributes have been cloned
1268
+ topic.title = "a"
1269
+ cloned_topic.title = "b"
1270
+ assert_equal "a", topic.title
1271
+ assert_equal "b", cloned_topic.title
1272
+
1273
+ # test if the attribute values have been cloned
1274
+ topic.title = {"a" => "b"}
1275
+ cloned_topic = topic.clone
1276
+ cloned_topic.title["a"] = "c"
1277
+ assert_equal "b", topic.title["a"]
1278
+
1279
+ #test if attributes set as part of after_initialize are cloned correctly
1280
+ assert_equal topic.author_email_address, cloned_topic.author_email_address
1281
+
1282
+ # test if saved clone object differs from original
1283
+ cloned_topic.save
1284
+ assert !cloned_topic.new_record?
1285
+ assert cloned_topic.id != topic.id
1286
+ end
1287
+
1288
+ def test_clone_with_aggregate_of_same_name_as_attribute
1289
+ dev = DeveloperWithAggregate.find(1)
1290
+ assert_kind_of DeveloperSalary, dev.salary
1291
+
1292
+ clone = nil
1293
+ assert_nothing_raised { clone = dev.clone }
1294
+ assert_kind_of DeveloperSalary, clone.salary
1295
+ assert_equal dev.salary.amount, clone.salary.amount
1296
+ assert clone.new_record?
1297
+
1298
+ # test if the attributes have been cloned
1299
+ original_amount = clone.salary.amount
1300
+ dev.salary.amount = 1
1301
+ assert_equal original_amount, clone.salary.amount
1302
+
1303
+ assert clone.save
1304
+ assert !clone.new_record?
1305
+ assert clone.id != dev.id
1306
+ end
1307
+
1308
+ def test_clone_preserves_subtype
1309
+ clone = nil
1310
+ assert_nothing_raised { clone = Company.find(3).clone }
1311
+ assert_kind_of Client, clone
1312
+ end
1313
+
1314
+ def test_bignum
1315
+ company = Company.find(1)
1316
+ company.rating = 2147483647
1317
+ company.save
1318
+ company = Company.find(1)
1319
+ assert_equal 2147483647, company.rating
1320
+ end
1321
+
1322
+ # TODO: extend defaults tests to other databases!
1323
+ if current_adapter?(:PostgreSQLAdapter)
1324
+ def test_default
1325
+ default = Default.new
1326
+
1327
+ # fixed dates / times
1328
+ assert_equal Date.new(2004, 1, 1), default.fixed_date
1329
+ assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
1330
+
1331
+ # char types
1332
+ assert_equal 'Y', default.char1
1333
+ assert_equal 'a varchar field', default.char2
1334
+ assert_equal 'a text field', default.char3
1335
+ end
1336
+
1337
+ class Geometric < ActiveRecord::Base; end
1338
+ def test_geometric_content
1339
+
1340
+ # accepted format notes:
1341
+ # ()'s aren't required
1342
+ # values can be a mix of float or integer
1343
+
1344
+ g = Geometric.new(
1345
+ :a_point => '(5.0, 6.1)',
1346
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1347
+ :a_line_segment => '(2.0, 3), (5.5, 7.0)',
1348
+ :a_box => '2.0, 3, 5.5, 7.0',
1349
+ :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
1350
+ :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
1351
+ :a_circle => '<(5.3, 10.4), 2>'
1352
+ )
1353
+
1354
+ assert g.save
1355
+
1356
+ # Reload and check that we have all the geometric attributes.
1357
+ h = Geometric.find(g.id)
1358
+
1359
+ assert_equal '(5,6.1)', h.a_point
1360
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1361
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1362
+ assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
1363
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1364
+ assert_equal '<(5.3,10.4),2>', h.a_circle
1365
+
1366
+ # use a geometric function to test for an open path
1367
+ objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
1368
+ assert_equal objs[0].isopen, 't'
1369
+
1370
+ # test alternate formats when defining the geometric types
1371
+
1372
+ g = Geometric.new(
1373
+ :a_point => '5.0, 6.1',
1374
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1375
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
1376
+ :a_box => '(2.0, 3), (5.5, 7.0)',
1377
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
1378
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
1379
+ :a_circle => '((5.3, 10.4), 2)'
1380
+ )
1381
+
1382
+ assert g.save
1383
+
1384
+ # Reload and check that we have all the geometric attributes.
1385
+ h = Geometric.find(g.id)
1386
+
1387
+ assert_equal '(5,6.1)', h.a_point
1388
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1389
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1390
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
1391
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1392
+ assert_equal '<(5.3,10.4),2>', h.a_circle
1393
+
1394
+ # use a geometric function to test for an closed path
1395
+ objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
1396
+ assert_equal objs[0].isclosed, 't'
1397
+ end
1398
+ end
1399
+
1400
+ class NumericData < ActiveRecord::Base
1401
+ self.table_name = 'numeric_data'
1402
+ end
1403
+
1404
+ def test_numeric_fields
1405
+ m = NumericData.new(
1406
+ :bank_balance => 1586.43,
1407
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1408
+ :world_population => 6000000000,
1409
+ :my_house_population => 3
1410
+ )
1411
+ assert m.save
1412
+
1413
+ m1 = NumericData.find(m.id)
1414
+ assert_not_nil m1
1415
+
1416
+ # As with migration_test.rb, we should make world_population >= 2**62
1417
+ # to cover 64-bit platforms and test it is a Bignum, but the main thing
1418
+ # is that it's an Integer.
1419
+ assert_kind_of Integer, m1.world_population
1420
+ assert_equal 6000000000, m1.world_population
1421
+
1422
+ assert_kind_of Fixnum, m1.my_house_population
1423
+ assert_equal 3, m1.my_house_population
1424
+
1425
+ assert_kind_of BigDecimal, m1.bank_balance
1426
+ assert_equal BigDecimal("1586.43"), m1.bank_balance
1427
+
1428
+ assert_kind_of BigDecimal, m1.big_bank_balance
1429
+ assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
1430
+ end
1431
+
1432
+ def test_auto_id
1433
+ auto = AutoId.new
1434
+ auto.save
1435
+ assert (auto.id > 0)
1436
+ end
1437
+
1438
+ def quote_column_name(name)
1439
+ "<#{name}>"
1440
+ end
1441
+
1442
+ def test_quote_keys
1443
+ ar = AutoId.new
1444
+ source = {"foo" => "bar", "baz" => "quux"}
1445
+ actual = ar.send(:quote_columns, self, source)
1446
+ inverted = actual.invert
1447
+ assert_equal("<foo>", inverted["bar"])
1448
+ assert_equal("<baz>", inverted["quux"])
1449
+ end
1450
+
1451
+ def test_sql_injection_via_find
1452
+ assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1453
+ Topic.find("123456 OR id > 0")
1454
+ end
1455
+ end
1456
+
1457
+ def test_column_name_properly_quoted
1458
+ col_record = ColumnName.new
1459
+ col_record.references = 40
1460
+ assert col_record.save
1461
+ col_record.references = 41
1462
+ assert col_record.save
1463
+ assert_not_nil c2 = ColumnName.find(col_record.id)
1464
+ assert_equal(41, c2.references)
1465
+ end
1466
+
1467
+ def test_quoting_arrays
1468
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1469
+ assert_equal topics(:first).replies.size, replies.size
1470
+
1471
+ replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1472
+ assert_equal 0, replies.size
1473
+ end
1474
+
1475
+ MyObject = Struct.new :attribute1, :attribute2
1476
+
1477
+ def test_serialized_attribute
1478
+ myobj = MyObject.new('value1', 'value2')
1479
+ topic = Topic.create("content" => myobj)
1480
+ Topic.serialize("content", MyObject)
1481
+ assert_equal(myobj, topic.content)
1482
+ end
1483
+
1484
+ def test_serialized_time_attribute
1485
+ myobj = Time.local(2008,1,1,1,0)
1486
+ topic = Topic.create("content" => myobj).reload
1487
+ assert_equal(myobj, topic.content)
1488
+ end
1489
+
1490
+ def test_serialized_string_attribute
1491
+ myobj = "Yes"
1492
+ topic = Topic.create("content" => myobj).reload
1493
+ assert_equal(myobj, topic.content)
1494
+ end
1495
+
1496
+ def test_nil_serialized_attribute_with_class_constraint
1497
+ myobj = MyObject.new('value1', 'value2')
1498
+ topic = Topic.new
1499
+ assert_nil topic.content
1500
+ end
1501
+
1502
+ def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
1503
+ myobj = MyObject.new('value1', 'value2')
1504
+ topic = Topic.new(:content => myobj)
1505
+ assert topic.save
1506
+ Topic.serialize(:content, Hash)
1507
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
1508
+ ensure
1509
+ Topic.serialize(:content)
1510
+ end
1511
+
1512
+ def test_serialized_attribute_with_class_constraint
1513
+ settings = { "color" => "blue" }
1514
+ Topic.serialize(:content, Hash)
1515
+ topic = Topic.new(:content => settings)
1516
+ assert topic.save
1517
+ assert_equal(settings, Topic.find(topic.id).content)
1518
+ ensure
1519
+ Topic.serialize(:content)
1520
+ end
1521
+
1522
+ def test_quote
1523
+ author_name = "\\ \001 ' \n \\n \""
1524
+ topic = Topic.create('author_name' => author_name)
1525
+ assert_equal author_name, Topic.find(topic.id).author_name
1526
+ end
1527
+
1528
+ if RUBY_VERSION < '1.9'
1529
+ def test_quote_chars
1530
+ with_kcode('UTF8') do
1531
+ str = 'The Narrator'
1532
+ topic = Topic.create(:author_name => str)
1533
+ assert_equal str, topic.author_name
1534
+
1535
+ assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
1536
+ topic = Topic.find_by_author_name(str.mb_chars)
1537
+
1538
+ assert_kind_of Topic, topic
1539
+ assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1540
+ end
1541
+ end
1542
+ end
1543
+
1544
+ def test_class_level_destroy
1545
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1546
+ Topic.find(1).replies << should_be_destroyed_reply
1547
+
1548
+ Topic.destroy(1)
1549
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1550
+ assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
1551
+ end
1552
+
1553
+ def test_class_level_delete
1554
+ should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
1555
+ Topic.find(1).replies << should_be_destroyed_reply
1556
+
1557
+ Topic.delete(1)
1558
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
1559
+ assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
1560
+ end
1561
+
1562
+ def test_increment_attribute
1563
+ assert_equal 50, accounts(:signals37).credit_limit
1564
+ accounts(:signals37).increment! :credit_limit
1565
+ assert_equal 51, accounts(:signals37, :reload).credit_limit
1566
+
1567
+ accounts(:signals37).increment(:credit_limit).increment!(:credit_limit)
1568
+ assert_equal 53, accounts(:signals37, :reload).credit_limit
1569
+ end
1570
+
1571
+ def test_increment_nil_attribute
1572
+ assert_nil topics(:first).parent_id
1573
+ topics(:first).increment! :parent_id
1574
+ assert_equal 1, topics(:first).parent_id
1575
+ end
1576
+
1577
+ def test_increment_attribute_by
1578
+ assert_equal 50, accounts(:signals37).credit_limit
1579
+ accounts(:signals37).increment! :credit_limit, 5
1580
+ assert_equal 55, accounts(:signals37, :reload).credit_limit
1581
+
1582
+ accounts(:signals37).increment(:credit_limit, 1).increment!(:credit_limit, 3)
1583
+ assert_equal 59, accounts(:signals37, :reload).credit_limit
1584
+ end
1585
+
1586
+ def test_decrement_attribute
1587
+ assert_equal 50, accounts(:signals37).credit_limit
1588
+
1589
+ accounts(:signals37).decrement!(:credit_limit)
1590
+ assert_equal 49, accounts(:signals37, :reload).credit_limit
1591
+
1592
+ accounts(:signals37).decrement(:credit_limit).decrement!(:credit_limit)
1593
+ assert_equal 47, accounts(:signals37, :reload).credit_limit
1594
+ end
1595
+
1596
+ def test_decrement_attribute_by
1597
+ assert_equal 50, accounts(:signals37).credit_limit
1598
+ accounts(:signals37).decrement! :credit_limit, 5
1599
+ assert_equal 45, accounts(:signals37, :reload).credit_limit
1600
+
1601
+ accounts(:signals37).decrement(:credit_limit, 1).decrement!(:credit_limit, 3)
1602
+ assert_equal 41, accounts(:signals37, :reload).credit_limit
1603
+ end
1604
+
1605
+ def test_toggle_attribute
1606
+ assert !topics(:first).approved?
1607
+ topics(:first).toggle!(:approved)
1608
+ assert topics(:first).approved?
1609
+ topic = topics(:first)
1610
+ topic.toggle(:approved)
1611
+ assert !topic.approved?
1612
+ topic.reload
1613
+ assert topic.approved?
1614
+ end
1615
+
1616
+ def test_reload
1617
+ t1 = Topic.find(1)
1618
+ t2 = Topic.find(1)
1619
+ t1.title = "something else"
1620
+ t1.save
1621
+ t2.reload
1622
+ assert_equal t1.title, t2.title
1623
+ end
1624
+
1625
+ def test_define_attr_method_with_value
1626
+ k = Class.new( ActiveRecord::Base )
1627
+ k.send(:define_attr_method, :table_name, "foo")
1628
+ assert_equal "foo", k.table_name
1629
+ end
1630
+
1631
+ def test_define_attr_method_with_block
1632
+ k = Class.new( ActiveRecord::Base )
1633
+ k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1634
+ assert_equal "sys_id", k.primary_key
1635
+ end
1636
+
1637
+ def test_set_table_name_with_value
1638
+ k = Class.new( ActiveRecord::Base )
1639
+ k.table_name = "foo"
1640
+ assert_equal "foo", k.table_name
1641
+ k.set_table_name "bar"
1642
+ assert_equal "bar", k.table_name
1643
+ end
1644
+
1645
+ def test_set_table_name_with_block
1646
+ k = Class.new( ActiveRecord::Base )
1647
+ k.set_table_name { "ks" }
1648
+ assert_equal "ks", k.table_name
1649
+ end
1650
+
1651
+ def test_set_primary_key_with_value
1652
+ k = Class.new( ActiveRecord::Base )
1653
+ k.primary_key = "foo"
1654
+ assert_equal "foo", k.primary_key
1655
+ k.set_primary_key "bar"
1656
+ assert_equal "bar", k.primary_key
1657
+ end
1658
+
1659
+ def test_set_primary_key_with_block
1660
+ k = Class.new( ActiveRecord::Base )
1661
+ k.set_primary_key { "sys_" + original_primary_key }
1662
+ assert_equal "sys_id", k.primary_key
1663
+ end
1664
+
1665
+ def test_set_inheritance_column_with_value
1666
+ k = Class.new( ActiveRecord::Base )
1667
+ k.inheritance_column = "foo"
1668
+ assert_equal "foo", k.inheritance_column
1669
+ k.set_inheritance_column "bar"
1670
+ assert_equal "bar", k.inheritance_column
1671
+ end
1672
+
1673
+ def test_set_inheritance_column_with_block
1674
+ k = Class.new( ActiveRecord::Base )
1675
+ k.set_inheritance_column { original_inheritance_column + "_id" }
1676
+ assert_equal "type_id", k.inheritance_column
1677
+ end
1678
+
1679
+ def test_count_with_join
1680
+ res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1681
+
1682
+ res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1683
+ assert_equal res, res2
1684
+
1685
+ res3 = nil
1686
+ assert_nothing_raised do
1687
+ res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1688
+ :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1689
+ end
1690
+ assert_equal res, res3
1691
+
1692
+ 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"
1693
+ res5 = nil
1694
+ assert_nothing_raised do
1695
+ res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1696
+ :joins => "p, comments co",
1697
+ :select => "p.id")
1698
+ end
1699
+
1700
+ assert_equal res4, res5
1701
+
1702
+ unless current_adapter?(:SQLite2Adapter, :DeprecatedSQLiteAdapter)
1703
+ 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"
1704
+ res7 = nil
1705
+ assert_nothing_raised do
1706
+ res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1707
+ :joins => "p, comments co",
1708
+ :select => "p.id",
1709
+ :distinct => true)
1710
+ end
1711
+ assert_equal res6, res7
1712
+ end
1713
+ end
1714
+
1715
+ def test_clear_association_cache_stored
1716
+ firm = Firm.find(1)
1717
+ assert_kind_of Firm, firm
1718
+
1719
+ firm.clear_association_cache
1720
+ assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort
1721
+ end
1722
+
1723
+ def test_clear_association_cache_new_record
1724
+ firm = Firm.new
1725
+ client_stored = Client.find(3)
1726
+ client_new = Client.new
1727
+ client_new.name = "The Joneses"
1728
+ clients = [ client_stored, client_new ]
1729
+
1730
+ firm.clients << clients
1731
+ assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1732
+
1733
+ firm.clear_association_cache
1734
+ assert_equal clients.map(&:name).to_set, firm.clients.map(&:name).to_set
1735
+ end
1736
+
1737
+ def test_interpolate_sql
1738
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
1739
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
1740
+ assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
1741
+ end
1742
+
1743
+ def test_scoped_find_conditions
1744
+ scoped_developers = Developer.with_scope(:find => { :conditions => 'salary > 90000' }) do
1745
+ Developer.find(:all, :conditions => 'id < 5')
1746
+ end
1747
+ assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1748
+ assert_equal 3, scoped_developers.size
1749
+ end
1750
+
1751
+ def test_scoped_find_limit_offset
1752
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :offset => 2 }) do
1753
+ Developer.find(:all, :order => 'id')
1754
+ end
1755
+ assert !scoped_developers.include?(developers(:david))
1756
+ assert !scoped_developers.include?(developers(:jamis))
1757
+ assert_equal 3, scoped_developers.size
1758
+
1759
+ # Test without scoped find conditions to ensure we get the whole thing
1760
+ developers = Developer.find(:all, :order => 'id')
1761
+ assert_equal Developer.count, developers.size
1762
+ end
1763
+
1764
+ def test_scoped_find_order
1765
+ # Test order in scope
1766
+ scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
1767
+ Developer.find(:all)
1768
+ end
1769
+ assert_equal 'Jamis', scoped_developers.first.name
1770
+ assert scoped_developers.include?(developers(:jamis))
1771
+ # Test scope without order and order in find
1772
+ scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
1773
+ Developer.find(:all, :order => 'salary DESC')
1774
+ end
1775
+ # Test scope order + find order, find has priority
1776
+ scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
1777
+ Developer.find(:all, :order => 'salary ASC')
1778
+ end
1779
+ assert scoped_developers.include?(developers(:poor_jamis))
1780
+ assert scoped_developers.include?(developers(:david))
1781
+ assert scoped_developers.include?(developers(:dev_10))
1782
+ # Test without scoped find conditions to ensure we get the right thing
1783
+ developers = Developer.find(:all, :order => 'id', :limit => 1)
1784
+ assert scoped_developers.include?(developers(:david))
1785
+ end
1786
+
1787
+ def test_scoped_find_limit_offset_including_has_many_association
1788
+ topics = Topic.with_scope(:find => {:limit => 1, :offset => 1, :include => :replies}) do
1789
+ Topic.find(:all, :order => "topics.id")
1790
+ end
1791
+ assert_equal 1, topics.size
1792
+ assert_equal 2, topics.first.id
1793
+ end
1794
+
1795
+ def test_scoped_find_order_including_has_many_association
1796
+ developers = Developer.with_scope(:find => { :order => 'developers.salary DESC', :include => :projects }) do
1797
+ Developer.find(:all)
1798
+ end
1799
+ assert developers.size >= 2
1800
+ for i in 1...developers.size
1801
+ assert developers[i-1].salary >= developers[i].salary
1802
+ end
1803
+ end
1804
+
1805
+ def test_scoped_find_with_group_and_having
1806
+ developers = Developer.with_scope(:find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
1807
+ Developer.find(:all)
1808
+ end
1809
+ assert_equal 3, developers.size
1810
+ end
1811
+
1812
+ def test_find_last
1813
+ last = Developer.find :last
1814
+ assert_equal last, Developer.find(:first, :order => 'id desc')
1815
+ end
1816
+
1817
+ def test_last
1818
+ assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
1819
+ end
1820
+
1821
+ def test_all_with_conditions
1822
+ assert_equal Developer.find(:all, :order => 'id desc'), Developer.all(:order => 'id desc')
1823
+ end
1824
+
1825
+ def test_find_ordered_last
1826
+ last = Developer.find :last, :order => 'developers.salary ASC'
1827
+ assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
1828
+ end
1829
+
1830
+ def test_find_reverse_ordered_last
1831
+ last = Developer.find :last, :order => 'developers.salary DESC'
1832
+ assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
1833
+ end
1834
+
1835
+ def test_find_multiple_ordered_last
1836
+ last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
1837
+ assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
1838
+ end
1839
+
1840
+ def test_find_symbol_ordered_last
1841
+ last = Developer.find :last, :order => :salary
1842
+ assert_equal last, Developer.find(:all, :order => :salary).last
1843
+ end
1844
+
1845
+ def test_find_scoped_ordered_last
1846
+ last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do
1847
+ Developer.find(:last)
1848
+ end
1849
+ assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
1850
+ end
1851
+
1852
+ def test_abstract_class
1853
+ assert !ActiveRecord::Base.abstract_class?
1854
+ assert LoosePerson.abstract_class?
1855
+ assert !LooseDescendant.abstract_class?
1856
+ end
1857
+
1858
+ def test_base_class
1859
+ assert_equal LoosePerson, LoosePerson.base_class
1860
+ assert_equal LooseDescendant, LooseDescendant.base_class
1861
+ assert_equal TightPerson, TightPerson.base_class
1862
+ assert_equal TightPerson, TightDescendant.base_class
1863
+
1864
+ assert_equal Post, Post.base_class
1865
+ assert_equal Post, SpecialPost.base_class
1866
+ assert_equal Post, StiPost.base_class
1867
+ assert_equal SubStiPost, SubStiPost.base_class
1868
+ end
1869
+
1870
+ def test_descends_from_active_record
1871
+ # Tries to call Object.abstract_class?
1872
+ assert_raise(NoMethodError) do
1873
+ ActiveRecord::Base.descends_from_active_record?
1874
+ end
1875
+
1876
+ # Abstract subclass of AR::Base.
1877
+ assert LoosePerson.descends_from_active_record?
1878
+
1879
+ # Concrete subclass of an abstract class.
1880
+ assert LooseDescendant.descends_from_active_record?
1881
+
1882
+ # Concrete subclass of AR::Base.
1883
+ assert TightPerson.descends_from_active_record?
1884
+
1885
+ # Concrete subclass of a concrete class but has no type column.
1886
+ assert TightDescendant.descends_from_active_record?
1887
+
1888
+ # Concrete subclass of AR::Base.
1889
+ assert Post.descends_from_active_record?
1890
+
1891
+ # Abstract subclass of a concrete class which has a type column.
1892
+ # This is pathological, as you'll never have Sub < Abstract < Concrete.
1893
+ assert !StiPost.descends_from_active_record?
1894
+
1895
+ # Concrete subclasses an abstract class which has a type column.
1896
+ assert !SubStiPost.descends_from_active_record?
1897
+ end
1898
+
1899
+ def test_find_on_abstract_base_class_doesnt_use_type_condition
1900
+ old_class = LooseDescendant
1901
+ Object.send :remove_const, :LooseDescendant
1902
+
1903
+ descendant = old_class.create! :first_name => 'bob'
1904
+ assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1905
+ ensure
1906
+ unless Object.const_defined?(:LooseDescendant)
1907
+ Object.const_set :LooseDescendant, old_class
1908
+ end
1909
+ end
1910
+
1911
+ def test_assert_queries
1912
+ query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1913
+ assert_queries(2) { 2.times { query.call } }
1914
+ assert_queries 1, &query
1915
+ assert_no_queries { assert true }
1916
+ end
1917
+
1918
+ def test_to_xml
1919
+ xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
1920
+ bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
1921
+ written_on_in_current_timezone = topics(:first).written_on.xmlschema
1922
+ last_read_in_current_timezone = topics(:first).last_read.xmlschema
1923
+
1924
+ assert_equal "topic", xml.root.name
1925
+ assert_equal "The First Topic" , xml.elements["//title"].text
1926
+ assert_equal "David" , xml.elements["//author-name"].text
1927
+
1928
+ assert_equal "1", xml.elements["//id"].text
1929
+ assert_equal "integer" , xml.elements["//id"].attributes['type']
1930
+
1931
+ assert_equal "1", xml.elements["//replies-count"].text
1932
+ assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
1933
+
1934
+ assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
1935
+ assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
1936
+
1937
+ assert_equal "--- Have a nice day\n" , xml.elements["//content"].text
1938
+ assert_equal "yaml" , xml.elements["//content"].attributes['type']
1939
+
1940
+ assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
1941
+
1942
+ assert_equal nil, xml.elements["//parent-id"].text
1943
+ assert_equal "integer", xml.elements["//parent-id"].attributes['type']
1944
+ assert_equal "true", xml.elements["//parent-id"].attributes['nil']
1945
+
1946
+ if current_adapter?(:SybaseAdapter, :OracleAdapter)
1947
+ assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
1948
+ assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
1949
+ else
1950
+ assert_equal "2004-04-15", xml.elements["//last-read"].text
1951
+ assert_equal "date" , xml.elements["//last-read"].attributes['type']
1952
+ end
1953
+
1954
+ # Oracle and DB2 don't have true boolean or time-only fields
1955
+ unless current_adapter?(:OracleAdapter, :DB2Adapter)
1956
+ assert_equal "false", xml.elements["//approved"].text
1957
+ assert_equal "boolean" , xml.elements["//approved"].attributes['type']
1958
+
1959
+ assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
1960
+ assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
1961
+ end
1962
+ end
1963
+
1964
+ def test_to_xml_skipping_attributes
1965
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
1966
+ assert_equal "<topic>", xml.first(7)
1967
+ assert !xml.include?(%(<title>The First Topic</title>))
1968
+ assert xml.include?(%(<author-name>David</author-name>))
1969
+
1970
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
1971
+ assert !xml.include?(%(<title>The First Topic</title>))
1972
+ assert !xml.include?(%(<author-name>David</author-name>))
1973
+ end
1974
+
1975
+ def test_to_xml_including_has_many_association
1976
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
1977
+ assert_equal "<topic>", xml.first(7)
1978
+ assert xml.include?(%(<replies type="array"><reply>))
1979
+ assert xml.include?(%(<title>The Second Topic of the day</title>))
1980
+ end
1981
+
1982
+ def test_array_to_xml_including_has_many_association
1983
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
1984
+ assert xml.include?(%(<replies type="array"><reply>))
1985
+ end
1986
+
1987
+ def test_array_to_xml_including_methods
1988
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
1989
+ assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
1990
+ assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
1991
+ end
1992
+
1993
+ def test_array_to_xml_including_has_one_association
1994
+ xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
1995
+ assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
1996
+ assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
1997
+ end
1998
+
1999
+ def test_array_to_xml_including_belongs_to_association
2000
+ xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
2001
+ assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
2002
+ assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
2003
+ assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
2004
+ end
2005
+
2006
+ def test_to_xml_including_belongs_to_association
2007
+ xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
2008
+ assert !xml.include?("<firm>")
2009
+
2010
+ xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
2011
+ assert xml.include?("<firm>")
2012
+ end
2013
+
2014
+ def test_to_xml_including_multiple_associations
2015
+ xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
2016
+ assert_equal "<firm>", xml.first(6)
2017
+ assert xml.include?(%(<account>))
2018
+ assert xml.include?(%(<clients type="array"><client>))
2019
+ end
2020
+
2021
+ def test_to_xml_including_multiple_associations_with_options
2022
+ xml = companies(:first_firm).to_xml(
2023
+ :indent => 0, :skip_instruct => true,
2024
+ :include => { :clients => { :only => :name } }
2025
+ )
2026
+
2027
+ assert_equal "<firm>", xml.first(6)
2028
+ assert xml.include?(%(<client><name>Summit</name></client>))
2029
+ assert xml.include?(%(<clients type="array"><client>))
2030
+ end
2031
+
2032
+ def test_to_xml_including_methods
2033
+ xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
2034
+ assert_equal "<company>", xml.first(9)
2035
+ assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
2036
+ end
2037
+
2038
+ def test_to_xml_with_block
2039
+ value = "Rockin' the block"
2040
+ xml = Company.new.to_xml(:skip_instruct => true) do |xml|
2041
+ xml.tag! "arbitrary-element", value
2042
+ end
2043
+ assert_equal "<company>", xml.first(9)
2044
+ assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
2045
+ end
2046
+
2047
+ def test_type_name_with_module_should_handle_beginning
2048
+ assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
2049
+ assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
2050
+ end
2051
+
2052
+ def test_to_param_should_return_string
2053
+ assert_kind_of String, Client.find(:first).to_param
2054
+ end
2055
+
2056
+ def test_inspect_class
2057
+ assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
2058
+ assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
2059
+ assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
2060
+ end
2061
+
2062
+ def test_inspect_instance
2063
+ topic = topics(:first)
2064
+ assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil>), topic.inspect
2065
+ end
2066
+
2067
+ def test_inspect_new_instance
2068
+ assert_match /Topic id: nil/, Topic.new.inspect
2069
+ end
2070
+
2071
+ def test_inspect_limited_select_instance
2072
+ assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
2073
+ assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
2074
+ end
2075
+
2076
+ def test_inspect_class_without_table
2077
+ assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
2078
+ end
2079
+
2080
+ def test_attribute_for_inspect
2081
+ t = topics(:first)
2082
+ t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
2083
+
2084
+ assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
2085
+ assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
2086
+ end
2087
+
2088
+ def test_becomes
2089
+ assert_kind_of Reply, topics(:first).becomes(Reply)
2090
+ assert_equal "The First Topic", topics(:first).becomes(Reply).title
2091
+ end
2092
+
2093
+ def test_silence_sets_log_level_to_error_in_block
2094
+ original_logger = ActiveRecord::Base.logger
2095
+ log = StringIO.new
2096
+ ActiveRecord::Base.logger = Logger.new(log)
2097
+ ActiveRecord::Base.logger.level = Logger::DEBUG
2098
+ ActiveRecord::Base.silence do
2099
+ ActiveRecord::Base.logger.warn "warn"
2100
+ ActiveRecord::Base.logger.error "error"
2101
+ end
2102
+ assert_equal "error\n", log.string
2103
+ ensure
2104
+ ActiveRecord::Base.logger = original_logger
2105
+ end
2106
+
2107
+ def test_silence_sets_log_level_back_to_level_before_yield
2108
+ original_logger = ActiveRecord::Base.logger
2109
+ log = StringIO.new
2110
+ ActiveRecord::Base.logger = Logger.new(log)
2111
+ ActiveRecord::Base.logger.level = Logger::WARN
2112
+ ActiveRecord::Base.silence do
2113
+ end
2114
+ assert_equal Logger::WARN, ActiveRecord::Base.logger.level
2115
+ ensure
2116
+ ActiveRecord::Base.logger = original_logger
2117
+ end
2118
+
2119
+ def test_benchmark_with_log_level
2120
+ original_logger = ActiveRecord::Base.logger
2121
+ log = StringIO.new
2122
+ ActiveRecord::Base.logger = Logger.new(log)
2123
+ ActiveRecord::Base.logger.level = Logger::WARN
2124
+ ActiveRecord::Base.benchmark("Debug Topic Count", Logger::DEBUG) { Topic.count }
2125
+ ActiveRecord::Base.benchmark("Warn Topic Count", Logger::WARN) { Topic.count }
2126
+ ActiveRecord::Base.benchmark("Error Topic Count", Logger::ERROR) { Topic.count }
2127
+ assert_no_match /Debug Topic Count/, log.string
2128
+ assert_match /Warn Topic Count/, log.string
2129
+ assert_match /Error Topic Count/, log.string
2130
+ ensure
2131
+ ActiveRecord::Base.logger = original_logger
2132
+ end
2133
+
2134
+ def test_benchmark_with_use_silence
2135
+ original_logger = ActiveRecord::Base.logger
2136
+ log = StringIO.new
2137
+ ActiveRecord::Base.logger = Logger.new(log)
2138
+ ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, true) { ActiveRecord::Base.logger.debug "Loud" }
2139
+ ActiveRecord::Base.benchmark("Logging", Logger::DEBUG, false) { ActiveRecord::Base.logger.debug "Quiet" }
2140
+ assert_no_match /Loud/, log.string
2141
+ assert_match /Quiet/, log.string
2142
+ ensure
2143
+ ActiveRecord::Base.logger = original_logger
2144
+ end
2145
+
2146
+ def test_create_with_custom_timestamps
2147
+ custom_datetime = 1.hour.ago.beginning_of_day
2148
+
2149
+ %w(created_at created_on updated_at updated_on).each do |attribute|
2150
+ parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime)
2151
+ assert_equal custom_datetime, parrot[attribute]
2152
+ end
2153
+ end
2154
+ end