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,262 @@
1
+ require "cases/helper"
2
+ require 'models/developer'
3
+ require 'models/project'
4
+ require 'models/company'
5
+ require 'models/topic'
6
+ require 'models/reply'
7
+ require 'models/computer'
8
+ require 'models/customer'
9
+ require 'models/order'
10
+ require 'models/categorization'
11
+ require 'models/category'
12
+ require 'models/post'
13
+ require 'models/author'
14
+ require 'models/comment'
15
+ require 'models/tag'
16
+ require 'models/tagging'
17
+ require 'models/person'
18
+ require 'models/reader'
19
+ require 'models/parrot'
20
+ require 'models/pirate'
21
+ require 'models/treasure'
22
+ require 'models/price_estimate'
23
+ require 'models/club'
24
+ require 'models/member'
25
+ require 'models/membership'
26
+ require 'models/sponsor'
27
+
28
+ class AssociationsTest < ActiveRecord::TestCase
29
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects,
30
+ :computers, :people, :readers
31
+
32
+ def test_include_with_order_works
33
+ assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
34
+ assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)}
35
+ end
36
+
37
+ def test_bad_collection_keys
38
+ assert_raise(ArgumentError, 'ActiveRecord should have barked on bad collection keys') do
39
+ Class.new(ActiveRecord::Base).has_many(:wheels, :name => 'wheels')
40
+ end
41
+ end
42
+
43
+ def test_should_construct_new_finder_sql_after_create
44
+ person = Person.new :first_name => 'clark'
45
+ assert_equal [], person.readers.find(:all)
46
+ person.save!
47
+ reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
48
+ assert person.readers.find(reader.id)
49
+ end
50
+
51
+ def test_force_reload
52
+ firm = Firm.new("name" => "A New Firm, Inc")
53
+ firm.save
54
+ firm.clients.each {|c|} # forcing to load all clients
55
+ assert firm.clients.empty?, "New firm shouldn't have client objects"
56
+ assert_equal 0, firm.clients.size, "New firm should have 0 clients"
57
+
58
+ client = Client.new("name" => "TheClient.com", "firm_id" => firm.id)
59
+ client.save
60
+
61
+ assert firm.clients.empty?, "New firm should have cached no client objects"
62
+ assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"
63
+
64
+ assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
65
+ assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
66
+ end
67
+
68
+ def test_storing_in_pstore
69
+ require "tmpdir"
70
+ store_filename = File.join(Dir.tmpdir, "ar-pstore-association-test")
71
+ File.delete(store_filename) if File.exist?(store_filename)
72
+ require "pstore"
73
+ apple = Firm.create("name" => "Apple")
74
+ natural = Client.new("name" => "Natural Company")
75
+ apple.clients << natural
76
+
77
+ db = PStore.new(store_filename)
78
+ db.transaction do
79
+ db["apple"] = apple
80
+ end
81
+
82
+ db = PStore.new(store_filename)
83
+ db.transaction do
84
+ assert_equal "Natural Company", db["apple"].clients.first.name
85
+ end
86
+ end
87
+ end
88
+
89
+ class AssociationProxyTest < ActiveRecord::TestCase
90
+ fixtures :authors, :posts, :categorizations, :categories, :developers, :projects, :developers_projects
91
+
92
+ def test_proxy_accessors
93
+ welcome = posts(:welcome)
94
+ assert_equal welcome, welcome.author.proxy_owner
95
+ assert_equal welcome.class.reflect_on_association(:author), welcome.author.proxy_reflection
96
+ welcome.author.class # force load target
97
+ assert_equal welcome.author, welcome.author.proxy_target
98
+
99
+ david = authors(:david)
100
+ assert_equal david, david.posts.proxy_owner
101
+ assert_equal david.class.reflect_on_association(:posts), david.posts.proxy_reflection
102
+ david.posts.class # force load target
103
+ assert_equal david.posts, david.posts.proxy_target
104
+
105
+ assert_equal david, david.posts_with_extension.testing_proxy_owner
106
+ assert_equal david.class.reflect_on_association(:posts_with_extension), david.posts_with_extension.testing_proxy_reflection
107
+ david.posts_with_extension.class # force load target
108
+ assert_equal david.posts_with_extension, david.posts_with_extension.testing_proxy_target
109
+ end
110
+
111
+ def test_push_does_not_load_target
112
+ david = authors(:david)
113
+
114
+ david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
115
+ assert !david.posts.loaded?
116
+ assert david.posts.include?(post)
117
+ end
118
+
119
+ def test_push_has_many_through_does_not_load_target
120
+ david = authors(:david)
121
+
122
+ david.categories << categories(:technology)
123
+ assert !david.categories.loaded?
124
+ assert david.categories.include?(categories(:technology))
125
+ end
126
+
127
+ def test_push_followed_by_save_does_not_load_target
128
+ david = authors(:david)
129
+
130
+ david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
131
+ assert !david.posts.loaded?
132
+ david.save
133
+ assert !david.posts.loaded?
134
+ assert david.posts.include?(post)
135
+ end
136
+
137
+ def test_push_does_not_lose_additions_to_new_record
138
+ josh = Author.new(:name => "Josh")
139
+ josh.posts << Post.new(:title => "New on Edge", :body => "More cool stuff!")
140
+ assert josh.posts.loaded?
141
+ assert_equal 1, josh.posts.size
142
+ end
143
+
144
+ def test_save_on_parent_does_not_load_target
145
+ david = developers(:david)
146
+
147
+ assert !david.projects.loaded?
148
+ david.update_attribute(:created_at, Time.now)
149
+ assert !david.projects.loaded?
150
+ end
151
+
152
+ def test_inspect_does_not_reload_a_not_yet_loaded_target
153
+ andreas = Developer.new :name => 'Andreas', :log => 'new developer added'
154
+ assert !andreas.audit_logs.loaded?
155
+ assert_match(/message: "new developer added"/, andreas.audit_logs.inspect)
156
+ end
157
+
158
+ def test_save_on_parent_saves_children
159
+ developer = Developer.create :name => "Bryan", :salary => 50_000
160
+ assert_equal 1, developer.reload.audit_logs.size
161
+ end
162
+
163
+ def test_create_via_association_with_block
164
+ post = authors(:david).posts.create(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
165
+ assert_equal post.title, "New on Edge"
166
+ assert_equal post.body, "More cool stuff!"
167
+ end
168
+
169
+ def test_create_with_bang_via_association_with_block
170
+ post = authors(:david).posts.create!(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
171
+ assert_equal post.title, "New on Edge"
172
+ assert_equal post.body, "More cool stuff!"
173
+ end
174
+
175
+ def test_failed_reload_returns_nil
176
+ p = setup_dangling_association
177
+ assert_nil p.author.reload
178
+ end
179
+
180
+ def test_failed_reset_returns_nil
181
+ p = setup_dangling_association
182
+ assert_nil p.author.reset
183
+ end
184
+
185
+ def test_reload_returns_assocition
186
+ david = developers(:david)
187
+ assert_nothing_raised do
188
+ assert_equal david.projects, david.projects.reload.reload
189
+ end
190
+ end
191
+
192
+ def setup_dangling_association
193
+ josh = Author.create(:name => "Josh")
194
+ p = Post.create(:title => "New on Edge", :body => "More cool stuff!", :author => josh)
195
+ josh.destroy
196
+ p
197
+ end
198
+ end
199
+
200
+ class OverridingAssociationsTest < ActiveRecord::TestCase
201
+ class Person < ActiveRecord::Base; end
202
+ class DifferentPerson < ActiveRecord::Base; end
203
+
204
+ class PeopleList < ActiveRecord::Base
205
+ has_and_belongs_to_many :has_and_belongs_to_many, :before_add => :enlist
206
+ has_many :has_many, :before_add => :enlist
207
+ belongs_to :belongs_to
208
+ has_one :has_one
209
+ end
210
+
211
+ class DifferentPeopleList < PeopleList
212
+ # Different association with the same name, callbacks should be omitted here.
213
+ has_and_belongs_to_many :has_and_belongs_to_many, :class_name => 'DifferentPerson'
214
+ has_many :has_many, :class_name => 'DifferentPerson'
215
+ belongs_to :belongs_to, :class_name => 'DifferentPerson'
216
+ has_one :has_one, :class_name => 'DifferentPerson'
217
+ end
218
+
219
+ def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
220
+ # redeclared association on AR descendant should not inherit callbacks from superclass
221
+ callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
222
+ assert_equal([:enlist], callbacks)
223
+ callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
224
+ assert_equal([], callbacks)
225
+ end
226
+
227
+ def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited
228
+ # redeclared association on AR descendant should not inherit callbacks from superclass
229
+ callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_many)
230
+ assert_equal([:enlist], callbacks)
231
+ callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_many)
232
+ assert_equal([], callbacks)
233
+ end
234
+
235
+ def test_habtm_association_redefinition_reflections_should_differ_and_not_inherited
236
+ assert_not_equal(
237
+ PeopleList.reflect_on_association(:has_and_belongs_to_many),
238
+ DifferentPeopleList.reflect_on_association(:has_and_belongs_to_many)
239
+ )
240
+ end
241
+
242
+ def test_has_many_association_redefinition_reflections_should_differ_and_not_inherited
243
+ assert_not_equal(
244
+ PeopleList.reflect_on_association(:has_many),
245
+ DifferentPeopleList.reflect_on_association(:has_many)
246
+ )
247
+ end
248
+
249
+ def test_belongs_to_association_redefinition_reflections_should_differ_and_not_inherited
250
+ assert_not_equal(
251
+ PeopleList.reflect_on_association(:belongs_to),
252
+ DifferentPeopleList.reflect_on_association(:belongs_to)
253
+ )
254
+ end
255
+
256
+ def test_has_one_association_redefinition_reflections_should_differ_and_not_inherited
257
+ assert_not_equal(
258
+ PeopleList.reflect_on_association(:has_one),
259
+ DifferentPeopleList.reflect_on_association(:has_one)
260
+ )
261
+ end
262
+ end
@@ -0,0 +1,305 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+ require 'models/minimalistic'
4
+
5
+ class AttributeMethodsTest < ActiveRecord::TestCase
6
+ fixtures :topics
7
+ def setup
8
+ @old_suffixes = ActiveRecord::Base.send(:attribute_method_suffixes).dup
9
+ @target = Class.new(ActiveRecord::Base)
10
+ @target.table_name = 'topics'
11
+ end
12
+
13
+ def teardown
14
+ ActiveRecord::Base.send(:attribute_method_suffixes).clear
15
+ ActiveRecord::Base.attribute_method_suffix *@old_suffixes
16
+ end
17
+
18
+ def test_match_attribute_method_query_returns_match_data
19
+ assert_not_nil md = @target.match_attribute_method?('title=')
20
+ assert_equal 'title', md.pre_match
21
+ assert_equal ['='], md.captures
22
+
23
+ %w(_hello_world ist! _maybe?).each do |suffix|
24
+ @target.class_eval "def attribute#{suffix}(*args) args end"
25
+ @target.attribute_method_suffix suffix
26
+
27
+ assert_not_nil md = @target.match_attribute_method?("title#{suffix}")
28
+ assert_equal 'title', md.pre_match
29
+ assert_equal [suffix], md.captures
30
+ end
31
+ end
32
+
33
+ def test_declared_attribute_method_affects_respond_to_and_method_missing
34
+ topic = @target.new(:title => 'Budget')
35
+ assert topic.respond_to?('title')
36
+ assert_equal 'Budget', topic.title
37
+ assert !topic.respond_to?('title_hello_world')
38
+ assert_raise(NoMethodError) { topic.title_hello_world }
39
+
40
+ %w(_hello_world _it! _candidate= able?).each do |suffix|
41
+ @target.class_eval "def attribute#{suffix}(*args) args end"
42
+ @target.attribute_method_suffix suffix
43
+
44
+ meth = "title#{suffix}"
45
+ assert topic.respond_to?(meth)
46
+ assert_equal ['title'], topic.send(meth)
47
+ assert_equal ['title', 'a'], topic.send(meth, 'a')
48
+ assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
49
+ end
50
+ end
51
+
52
+ def test_should_unserialize_attributes_for_frozen_records
53
+ myobj = {:value1 => :value2}
54
+ topic = Topic.create("content" => myobj)
55
+ topic.freeze
56
+ assert_equal myobj, topic.content
57
+ end
58
+
59
+ def test_typecast_attribute_from_select_to_false
60
+ topic = Topic.create(:title => 'Budget')
61
+ topic = Topic.find(:first, :select => "topics.*, 1=2 as is_test")
62
+ assert !topic.is_test?
63
+ end
64
+
65
+ def test_typecast_attribute_from_select_to_true
66
+ topic = Topic.create(:title => 'Budget')
67
+ topic = Topic.find(:first, :select => "topics.*, 2=2 as is_test")
68
+ assert topic.is_test?
69
+ end
70
+
71
+ def test_kernel_methods_not_implemented_in_activerecord
72
+ %w(test name display y).each do |method|
73
+ assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
74
+ end
75
+ end
76
+
77
+ def test_primary_key_implemented
78
+ assert Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
79
+ end
80
+
81
+ def test_defined_kernel_methods_implemented_in_model
82
+ %w(test name display y).each do |method|
83
+ klass = Class.new ActiveRecord::Base
84
+ klass.class_eval "def #{method}() 'defined #{method}' end"
85
+ assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
86
+ end
87
+ end
88
+
89
+ def test_defined_kernel_methods_implemented_in_model_abstract_subclass
90
+ %w(test name display y).each do |method|
91
+ abstract = Class.new ActiveRecord::Base
92
+ abstract.class_eval "def #{method}() 'defined #{method}' end"
93
+ abstract.abstract_class = true
94
+ klass = Class.new abstract
95
+ assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
96
+ end
97
+ end
98
+
99
+ def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
100
+ %w(save create_or_update).each do |method|
101
+ klass = Class.new ActiveRecord::Base
102
+ klass.class_eval "def #{method}() 'defined #{method}' end"
103
+ assert_raise ActiveRecord::DangerousAttributeError do
104
+ klass.instance_method_already_implemented?(method)
105
+ end
106
+ end
107
+ end
108
+
109
+ def test_only_time_related_columns_are_meant_to_be_cached_by_default
110
+ expected = %w(datetime timestamp time date).sort
111
+ assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
112
+ end
113
+
114
+ def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
115
+ default_attributes = Topic.cached_attributes
116
+ Topic.cache_attributes :replies_count
117
+ expected = default_attributes + ["replies_count"]
118
+ assert_equal expected.sort, Topic.cached_attributes.sort
119
+ Topic.instance_variable_set "@cached_attributes", nil
120
+ end
121
+
122
+ def test_time_related_columns_are_actually_cached
123
+ column_types = %w(datetime timestamp time date).map(&:to_sym)
124
+ column_names = Topic.columns.select{|c| column_types.include?(c.type) }.map(&:name)
125
+
126
+ assert_equal column_names.sort, Topic.cached_attributes.sort
127
+ assert_equal time_related_columns_on_topic.sort, Topic.cached_attributes.sort
128
+ end
129
+
130
+ def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
131
+ t = topics(:first)
132
+ cache = t.instance_variable_get "@attributes_cache"
133
+
134
+ assert_not_nil cache
135
+ assert cache.empty?
136
+
137
+ all_columns = Topic.columns.map(&:name)
138
+ cached_columns = time_related_columns_on_topic
139
+ uncached_columns = all_columns - cached_columns
140
+
141
+ all_columns.each do |attr_name|
142
+ attribute_gets_cached = Topic.cache_attribute?(attr_name)
143
+ val = t.send attr_name unless attr_name == "type"
144
+ if attribute_gets_cached
145
+ assert cached_columns.include?(attr_name)
146
+ assert_equal val, cache[attr_name]
147
+ else
148
+ assert uncached_columns.include?(attr_name)
149
+ assert !cache.include?(attr_name)
150
+ end
151
+ end
152
+ end
153
+
154
+ def test_time_attributes_are_retrieved_in_current_time_zone
155
+ in_time_zone "Pacific Time (US & Canada)" do
156
+ utc_time = Time.utc(2008, 1, 1)
157
+ record = @target.new
158
+ record[:written_on] = utc_time
159
+ assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
160
+ assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
161
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
162
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
163
+ end
164
+ end
165
+
166
+ def test_setting_time_zone_aware_attribute_to_utc
167
+ in_time_zone "Pacific Time (US & Canada)" do
168
+ utc_time = Time.utc(2008, 1, 1)
169
+ record = @target.new
170
+ record.written_on = utc_time
171
+ assert_equal utc_time, record.written_on
172
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
173
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
174
+ end
175
+ end
176
+
177
+ def test_setting_time_zone_aware_attribute_in_other_time_zone
178
+ utc_time = Time.utc(2008, 1, 1)
179
+ cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
180
+ in_time_zone "Pacific Time (US & Canada)" do
181
+ record = @target.new
182
+ record.written_on = cst_time
183
+ assert_equal utc_time, record.written_on
184
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
185
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
186
+ end
187
+ end
188
+
189
+ def test_setting_time_zone_aware_attribute_with_string
190
+ utc_time = Time.utc(2008, 1, 1)
191
+ (-11..13).each do |timezone_offset|
192
+ time_string = utc_time.in_time_zone(timezone_offset).to_s
193
+ in_time_zone "Pacific Time (US & Canada)" do
194
+ record = @target.new
195
+ record.written_on = time_string
196
+ assert_equal Time.zone.parse(time_string), record.written_on
197
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
198
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
199
+ end
200
+ end
201
+ end
202
+
203
+ def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
204
+ in_time_zone "Pacific Time (US & Canada)" do
205
+ record = @target.new
206
+ record.written_on = ' '
207
+ assert_nil record.written_on
208
+ end
209
+ end
210
+
211
+ def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
212
+ time_string = 'Tue Jan 01 00:00:00 2008'
213
+ (-11..13).each do |timezone_offset|
214
+ in_time_zone timezone_offset do
215
+ record = @target.new
216
+ record.written_on = time_string
217
+ assert_equal Time.zone.parse(time_string), record.written_on
218
+ assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
219
+ assert_equal Time.utc(2008, 1, 1), record.written_on.time
220
+ end
221
+ end
222
+ end
223
+
224
+ def test_setting_time_zone_aware_attribute_in_current_time_zone
225
+ utc_time = Time.utc(2008, 1, 1)
226
+ in_time_zone "Pacific Time (US & Canada)" do
227
+ record = @target.new
228
+ record.written_on = utc_time.in_time_zone
229
+ assert_equal utc_time, record.written_on
230
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
231
+ assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
232
+ end
233
+ end
234
+
235
+ def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
236
+ Topic.skip_time_zone_conversion_for_attributes = [:field_a]
237
+ Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
238
+
239
+ assert_equal [:field_a], Topic.skip_time_zone_conversion_for_attributes
240
+ assert_equal [:field_b], Minimalistic.skip_time_zone_conversion_for_attributes
241
+ end
242
+
243
+ def test_read_attributes_respect_access_control
244
+ privatize("title")
245
+
246
+ topic = @target.new(:title => "The pros and cons of programming naked.")
247
+ assert !topic.respond_to?(:title)
248
+ exception = assert_raise(NoMethodError) { topic.title }
249
+ assert_equal "Attempt to call private method", exception.message
250
+ assert_equal "I'm private", topic.send(:title)
251
+ end
252
+
253
+ def test_write_attributes_respect_access_control
254
+ privatize("title=(value)")
255
+
256
+ topic = @target.new
257
+ assert !topic.respond_to?(:title=)
258
+ exception = assert_raise(NoMethodError) { topic.title = "Pants"}
259
+ assert_equal "Attempt to call private method", exception.message
260
+ topic.send(:title=, "Very large pants")
261
+ end
262
+
263
+ def test_question_attributes_respect_access_control
264
+ privatize("title?")
265
+
266
+ topic = @target.new(:title => "Isaac Newton's pants")
267
+ assert !topic.respond_to?(:title?)
268
+ exception = assert_raise(NoMethodError) { topic.title? }
269
+ assert_equal "Attempt to call private method", exception.message
270
+ assert topic.send(:title?)
271
+ end
272
+
273
+ def test_bulk_update_respects_access_control
274
+ privatize("title=(value)")
275
+
276
+ assert_raise(ActiveRecord::UnknownAttributeError) { topic = @target.new(:title => "Rants about pants") }
277
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
278
+ end
279
+
280
+ private
281
+ def time_related_columns_on_topic
282
+ Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
283
+ end
284
+
285
+ def in_time_zone(zone)
286
+ old_zone = Time.zone
287
+ old_tz = ActiveRecord::Base.time_zone_aware_attributes
288
+
289
+ Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
290
+ ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
291
+ yield
292
+ ensure
293
+ Time.zone = old_zone
294
+ ActiveRecord::Base.time_zone_aware_attributes = old_tz
295
+ end
296
+
297
+ def privatize(method_signature)
298
+ @target.class_eval <<-private_method
299
+ private
300
+ def #{method_signature}
301
+ "I'm private"
302
+ end
303
+ private_method
304
+ end
305
+ end