activerecord_authorails 1.0.0

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 (270) hide show
  1. data/CHANGELOG +3043 -0
  2. data/README +360 -0
  3. data/RUNNING_UNIT_TESTS +64 -0
  4. data/Rakefile +226 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/associations.rb +87 -0
  7. data/examples/shared_setup.rb +15 -0
  8. data/examples/validation.rb +85 -0
  9. data/install.rb +30 -0
  10. data/lib/active_record.rb +85 -0
  11. data/lib/active_record/acts/list.rb +244 -0
  12. data/lib/active_record/acts/nested_set.rb +211 -0
  13. data/lib/active_record/acts/tree.rb +89 -0
  14. data/lib/active_record/aggregations.rb +191 -0
  15. data/lib/active_record/associations.rb +1637 -0
  16. data/lib/active_record/associations/association_collection.rb +190 -0
  17. data/lib/active_record/associations/association_proxy.rb +158 -0
  18. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  19. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  20. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
  21. data/lib/active_record/associations/has_many_association.rb +210 -0
  22. data/lib/active_record/associations/has_many_through_association.rb +247 -0
  23. data/lib/active_record/associations/has_one_association.rb +80 -0
  24. data/lib/active_record/attribute_methods.rb +75 -0
  25. data/lib/active_record/base.rb +2164 -0
  26. data/lib/active_record/calculations.rb +270 -0
  27. data/lib/active_record/callbacks.rb +367 -0
  28. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
  29. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
  34. data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
  35. data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
  36. data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
  38. data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
  39. data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
  40. data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
  41. data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
  42. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
  43. data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
  44. data/lib/active_record/deprecated_associations.rb +104 -0
  45. data/lib/active_record/deprecated_finders.rb +44 -0
  46. data/lib/active_record/fixtures.rb +628 -0
  47. data/lib/active_record/locking/optimistic.rb +106 -0
  48. data/lib/active_record/locking/pessimistic.rb +77 -0
  49. data/lib/active_record/migration.rb +394 -0
  50. data/lib/active_record/observer.rb +178 -0
  51. data/lib/active_record/query_cache.rb +64 -0
  52. data/lib/active_record/reflection.rb +222 -0
  53. data/lib/active_record/schema.rb +58 -0
  54. data/lib/active_record/schema_dumper.rb +149 -0
  55. data/lib/active_record/timestamp.rb +51 -0
  56. data/lib/active_record/transactions.rb +136 -0
  57. data/lib/active_record/validations.rb +843 -0
  58. data/lib/active_record/vendor/db2.rb +362 -0
  59. data/lib/active_record/vendor/mysql.rb +1214 -0
  60. data/lib/active_record/vendor/simple.rb +693 -0
  61. data/lib/active_record/version.rb +9 -0
  62. data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
  63. data/lib/active_record/wrappings.rb +58 -0
  64. data/lib/active_record/xml_serialization.rb +308 -0
  65. data/test/aaa_create_tables_test.rb +59 -0
  66. data/test/abstract_unit.rb +77 -0
  67. data/test/active_schema_test_mysql.rb +31 -0
  68. data/test/adapter_test.rb +87 -0
  69. data/test/adapter_test_sqlserver.rb +81 -0
  70. data/test/aggregations_test.rb +95 -0
  71. data/test/all.sh +8 -0
  72. data/test/ar_schema_test.rb +33 -0
  73. data/test/association_inheritance_reload.rb +14 -0
  74. data/test/associations/callbacks_test.rb +126 -0
  75. data/test/associations/cascaded_eager_loading_test.rb +138 -0
  76. data/test/associations/eager_test.rb +393 -0
  77. data/test/associations/extension_test.rb +42 -0
  78. data/test/associations/join_model_test.rb +497 -0
  79. data/test/associations_test.rb +1809 -0
  80. data/test/attribute_methods_test.rb +49 -0
  81. data/test/base_test.rb +1586 -0
  82. data/test/binary_test.rb +37 -0
  83. data/test/calculations_test.rb +219 -0
  84. data/test/callbacks_test.rb +377 -0
  85. data/test/class_inheritable_attributes_test.rb +32 -0
  86. data/test/column_alias_test.rb +17 -0
  87. data/test/connection_test_firebird.rb +8 -0
  88. data/test/connections/native_db2/connection.rb +25 -0
  89. data/test/connections/native_firebird/connection.rb +26 -0
  90. data/test/connections/native_frontbase/connection.rb +27 -0
  91. data/test/connections/native_mysql/connection.rb +24 -0
  92. data/test/connections/native_openbase/connection.rb +21 -0
  93. data/test/connections/native_oracle/connection.rb +27 -0
  94. data/test/connections/native_postgresql/connection.rb +23 -0
  95. data/test/connections/native_sqlite/connection.rb +34 -0
  96. data/test/connections/native_sqlite3/connection.rb +34 -0
  97. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  98. data/test/connections/native_sqlserver/connection.rb +23 -0
  99. data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
  100. data/test/connections/native_sybase/connection.rb +23 -0
  101. data/test/copy_table_sqlite.rb +64 -0
  102. data/test/datatype_test_postgresql.rb +52 -0
  103. data/test/default_test_firebird.rb +16 -0
  104. data/test/defaults_test.rb +60 -0
  105. data/test/deprecated_associations_test.rb +396 -0
  106. data/test/deprecated_finder_test.rb +151 -0
  107. data/test/empty_date_time_test.rb +25 -0
  108. data/test/finder_test.rb +504 -0
  109. data/test/fixtures/accounts.yml +28 -0
  110. data/test/fixtures/author.rb +99 -0
  111. data/test/fixtures/author_favorites.yml +4 -0
  112. data/test/fixtures/authors.yml +7 -0
  113. data/test/fixtures/auto_id.rb +4 -0
  114. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  115. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  116. data/test/fixtures/bad_fixtures/blank_line +3 -0
  117. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  118. data/test/fixtures/bad_fixtures/missing_value +1 -0
  119. data/test/fixtures/binary.rb +2 -0
  120. data/test/fixtures/categories.yml +14 -0
  121. data/test/fixtures/categories/special_categories.yml +9 -0
  122. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  123. data/test/fixtures/categories_ordered.yml +7 -0
  124. data/test/fixtures/categories_posts.yml +23 -0
  125. data/test/fixtures/categorization.rb +5 -0
  126. data/test/fixtures/categorizations.yml +17 -0
  127. data/test/fixtures/category.rb +20 -0
  128. data/test/fixtures/column_name.rb +3 -0
  129. data/test/fixtures/comment.rb +23 -0
  130. data/test/fixtures/comments.yml +59 -0
  131. data/test/fixtures/companies.yml +55 -0
  132. data/test/fixtures/company.rb +107 -0
  133. data/test/fixtures/company_in_module.rb +59 -0
  134. data/test/fixtures/computer.rb +3 -0
  135. data/test/fixtures/computers.yml +4 -0
  136. data/test/fixtures/course.rb +3 -0
  137. data/test/fixtures/courses.yml +7 -0
  138. data/test/fixtures/customer.rb +55 -0
  139. data/test/fixtures/customers.yml +17 -0
  140. data/test/fixtures/db_definitions/db2.drop.sql +32 -0
  141. data/test/fixtures/db_definitions/db2.sql +231 -0
  142. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  143. data/test/fixtures/db_definitions/db22.sql +5 -0
  144. data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
  145. data/test/fixtures/db_definitions/firebird.sql +304 -0
  146. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  148. data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
  149. data/test/fixtures/db_definitions/frontbase.sql +268 -0
  150. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  151. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  152. data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
  153. data/test/fixtures/db_definitions/mysql.sql +234 -0
  154. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  156. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  157. data/test/fixtures/db_definitions/openbase.sql +302 -0
  158. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  159. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  160. data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
  161. data/test/fixtures/db_definitions/oracle.sql +325 -0
  162. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  163. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  164. data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
  165. data/test/fixtures/db_definitions/postgresql.sql +263 -0
  166. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  167. data/test/fixtures/db_definitions/postgresql2.sql +5 -0
  168. data/test/fixtures/db_definitions/schema.rb +60 -0
  169. data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
  170. data/test/fixtures/db_definitions/sqlite.sql +215 -0
  171. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  172. data/test/fixtures/db_definitions/sqlite2.sql +5 -0
  173. data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
  174. data/test/fixtures/db_definitions/sqlserver.sql +243 -0
  175. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  176. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  177. data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
  178. data/test/fixtures/db_definitions/sybase.sql +218 -0
  179. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  180. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  181. data/test/fixtures/default.rb +2 -0
  182. data/test/fixtures/developer.rb +52 -0
  183. data/test/fixtures/developers.yml +21 -0
  184. data/test/fixtures/developers_projects.yml +17 -0
  185. data/test/fixtures/developers_projects/david_action_controller +3 -0
  186. data/test/fixtures/developers_projects/david_active_record +3 -0
  187. data/test/fixtures/developers_projects/jamis_active_record +2 -0
  188. data/test/fixtures/edge.rb +5 -0
  189. data/test/fixtures/edges.yml +6 -0
  190. data/test/fixtures/entrant.rb +3 -0
  191. data/test/fixtures/entrants.yml +14 -0
  192. data/test/fixtures/fk_test_has_fk.yml +3 -0
  193. data/test/fixtures/fk_test_has_pk.yml +2 -0
  194. data/test/fixtures/flowers.jpg +0 -0
  195. data/test/fixtures/funny_jokes.yml +10 -0
  196. data/test/fixtures/joke.rb +6 -0
  197. data/test/fixtures/keyboard.rb +3 -0
  198. data/test/fixtures/legacy_thing.rb +3 -0
  199. data/test/fixtures/legacy_things.yml +3 -0
  200. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  201. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  202. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  203. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  204. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  205. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  206. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  207. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  208. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  209. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  210. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  211. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  212. data/test/fixtures/mixed_case_monkey.rb +3 -0
  213. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  214. data/test/fixtures/mixin.rb +63 -0
  215. data/test/fixtures/mixins.yml +127 -0
  216. data/test/fixtures/movie.rb +5 -0
  217. data/test/fixtures/movies.yml +7 -0
  218. data/test/fixtures/naked/csv/accounts.csv +1 -0
  219. data/test/fixtures/naked/yml/accounts.yml +1 -0
  220. data/test/fixtures/naked/yml/companies.yml +1 -0
  221. data/test/fixtures/naked/yml/courses.yml +1 -0
  222. data/test/fixtures/order.rb +4 -0
  223. data/test/fixtures/people.yml +3 -0
  224. data/test/fixtures/person.rb +4 -0
  225. data/test/fixtures/post.rb +58 -0
  226. data/test/fixtures/posts.yml +48 -0
  227. data/test/fixtures/project.rb +27 -0
  228. data/test/fixtures/projects.yml +7 -0
  229. data/test/fixtures/reader.rb +4 -0
  230. data/test/fixtures/readers.yml +4 -0
  231. data/test/fixtures/reply.rb +37 -0
  232. data/test/fixtures/subject.rb +4 -0
  233. data/test/fixtures/subscriber.rb +6 -0
  234. data/test/fixtures/subscribers/first +2 -0
  235. data/test/fixtures/subscribers/second +2 -0
  236. data/test/fixtures/tag.rb +7 -0
  237. data/test/fixtures/tagging.rb +6 -0
  238. data/test/fixtures/taggings.yml +18 -0
  239. data/test/fixtures/tags.yml +7 -0
  240. data/test/fixtures/task.rb +3 -0
  241. data/test/fixtures/tasks.yml +7 -0
  242. data/test/fixtures/topic.rb +25 -0
  243. data/test/fixtures/topics.yml +22 -0
  244. data/test/fixtures/vertex.rb +9 -0
  245. data/test/fixtures/vertices.yml +4 -0
  246. data/test/fixtures_test.rb +401 -0
  247. data/test/inheritance_test.rb +205 -0
  248. data/test/lifecycle_test.rb +137 -0
  249. data/test/locking_test.rb +190 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +768 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_nested_set_test.rb +196 -0
  254. data/test/mixin_test.rb +550 -0
  255. data/test/modules_test.rb +34 -0
  256. data/test/multiple_db_test.rb +60 -0
  257. data/test/pk_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +159 -0
  260. data/test/schema_authorization_test_postgresql.rb +75 -0
  261. data/test/schema_dumper_test.rb +96 -0
  262. data/test/schema_test_postgresql.rb +64 -0
  263. data/test/synonym_test_oracle.rb +17 -0
  264. data/test/table_name_test_sqlserver.rb +23 -0
  265. data/test/threaded_connections_test.rb +48 -0
  266. data/test/transactions_test.rb +230 -0
  267. data/test/unconnected_test.rb +32 -0
  268. data/test/validations_test.rb +1097 -0
  269. data/test/xml_serialization_test.rb +125 -0
  270. metadata +365 -0
@@ -0,0 +1,205 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/company'
3
+ require 'fixtures/project'
4
+ require 'fixtures/subscriber'
5
+
6
+ class InheritanceTest < Test::Unit::TestCase
7
+ fixtures :companies, :projects, :subscribers, :accounts
8
+
9
+ def test_a_bad_type_column
10
+ #SQLServer need to turn Identity Insert On before manually inserting into the Identity column
11
+ if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
12
+ Company.connection.execute "SET IDENTITY_INSERT companies ON"
13
+ end
14
+ Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
15
+
16
+ #We then need to turn it back Off before continuing.
17
+ if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
18
+ Company.connection.execute "SET IDENTITY_INSERT companies OFF"
19
+ end
20
+ assert_raises(ActiveRecord::SubclassNotFound) { Company.find(100) }
21
+ end
22
+
23
+ def test_inheritance_find
24
+ assert Company.find(1).kind_of?(Firm), "37signals should be a firm"
25
+ assert Firm.find(1).kind_of?(Firm), "37signals should be a firm"
26
+ assert Company.find(2).kind_of?(Client), "Summit should be a client"
27
+ assert Client.find(2).kind_of?(Client), "Summit should be a client"
28
+ end
29
+
30
+ def test_alt_inheritance_find
31
+ switch_to_alt_inheritance_column
32
+ test_inheritance_find
33
+ switch_to_default_inheritance_column
34
+ end
35
+
36
+ def test_inheritance_find_all
37
+ companies = Company.find(:all, :order => 'id')
38
+ assert companies[0].kind_of?(Firm), "37signals should be a firm"
39
+ assert companies[1].kind_of?(Client), "Summit should be a client"
40
+ end
41
+
42
+ def test_alt_inheritance_find_all
43
+ switch_to_alt_inheritance_column
44
+ test_inheritance_find_all
45
+ switch_to_default_inheritance_column
46
+ end
47
+
48
+ def test_inheritance_save
49
+ firm = Firm.new
50
+ firm.name = "Next Angle"
51
+ firm.save
52
+
53
+ next_angle = Company.find(firm.id)
54
+ assert next_angle.kind_of?(Firm), "Next Angle should be a firm"
55
+ end
56
+
57
+ def test_alt_inheritance_save
58
+ switch_to_alt_inheritance_column
59
+ test_inheritance_save
60
+ switch_to_default_inheritance_column
61
+ end
62
+
63
+ def test_inheritance_condition
64
+ assert_equal 9, Company.count
65
+ assert_equal 2, Firm.count
66
+ assert_equal 3, Client.count
67
+ end
68
+
69
+ def test_alt_inheritance_condition
70
+ switch_to_alt_inheritance_column
71
+ test_inheritance_condition
72
+ switch_to_default_inheritance_column
73
+ end
74
+
75
+ def test_finding_incorrect_type_data
76
+ assert_raises(ActiveRecord::RecordNotFound) { Firm.find(2) }
77
+ assert_nothing_raised { Firm.find(1) }
78
+ end
79
+
80
+ def test_alt_finding_incorrect_type_data
81
+ switch_to_alt_inheritance_column
82
+ test_finding_incorrect_type_data
83
+ switch_to_default_inheritance_column
84
+ end
85
+
86
+ def test_update_all_within_inheritance
87
+ Client.update_all "name = 'I am a client'"
88
+ assert_equal "I am a client", Client.find(:all).first.name
89
+ assert_equal "37signals", Firm.find(:all).first.name
90
+ end
91
+
92
+ def test_alt_update_all_within_inheritance
93
+ switch_to_alt_inheritance_column
94
+ test_update_all_within_inheritance
95
+ switch_to_default_inheritance_column
96
+ end
97
+
98
+ def test_destroy_all_within_inheritance
99
+ Client.destroy_all
100
+ assert_equal 0, Client.count
101
+ assert_equal 2, Firm.count
102
+ end
103
+
104
+ def test_alt_destroy_all_within_inheritance
105
+ switch_to_alt_inheritance_column
106
+ test_destroy_all_within_inheritance
107
+ switch_to_default_inheritance_column
108
+ end
109
+
110
+ def test_find_first_within_inheritance
111
+ assert_kind_of Firm, Company.find(:first, :conditions => "name = '37signals'")
112
+ assert_kind_of Firm, Firm.find(:first, :conditions => "name = '37signals'")
113
+ assert_nil Client.find(:first, :conditions => "name = '37signals'")
114
+ end
115
+
116
+ def test_alt_find_first_within_inheritance
117
+ switch_to_alt_inheritance_column
118
+ test_find_first_within_inheritance
119
+ switch_to_default_inheritance_column
120
+ end
121
+
122
+ def test_complex_inheritance
123
+ very_special_client = VerySpecialClient.create("name" => "veryspecial")
124
+ assert_equal very_special_client, VerySpecialClient.find(:first, :conditions => "name = 'veryspecial'")
125
+ assert_equal very_special_client, SpecialClient.find(:first, :conditions => "name = 'veryspecial'")
126
+ assert_equal very_special_client, Company.find(:first, :conditions => "name = 'veryspecial'")
127
+ assert_equal very_special_client, Client.find(:first, :conditions => "name = 'veryspecial'")
128
+ assert_equal 1, Client.find(:all, :conditions => "name = 'Summit'").size
129
+ assert_equal very_special_client, Client.find(very_special_client.id)
130
+ end
131
+
132
+ def test_alt_complex_inheritance
133
+ switch_to_alt_inheritance_column
134
+ test_complex_inheritance
135
+ switch_to_default_inheritance_column
136
+ end
137
+
138
+ def test_eager_load_belongs_to_something_inherited
139
+ account = Account.find(1, :include => :firm)
140
+ assert_not_nil account.instance_variable_get("@firm"), "nil proves eager load failed"
141
+ end
142
+
143
+ def test_alt_eager_loading
144
+ switch_to_alt_inheritance_column
145
+ test_eager_load_belongs_to_something_inherited
146
+ switch_to_default_inheritance_column
147
+ ActiveRecord::Base.logger.debug "cocksucker"
148
+ end
149
+
150
+ def test_inheritance_without_mapping
151
+ assert_kind_of SpecialSubscriber, SpecialSubscriber.find("webster132")
152
+ assert_nothing_raised { s = SpecialSubscriber.new("name" => "And breaaaaathe!"); s.id = 'roger'; s.save }
153
+ end
154
+
155
+ private
156
+ def switch_to_alt_inheritance_column
157
+ # we don't want misleading test results, so get rid of the values in the type column
158
+ Company.find(:all, :order => 'id').each do |c|
159
+ c['type'] = nil
160
+ c.save
161
+ end
162
+ [ Company, Firm, Client].each { |klass| klass.reset_column_information }
163
+ Company.set_inheritance_column('ruby_type')
164
+ end
165
+ def switch_to_default_inheritance_column
166
+ [ Company, Firm, Client].each { |klass| klass.reset_column_information }
167
+ Company.set_inheritance_column('type')
168
+ end
169
+ end
170
+
171
+
172
+ class InheritanceComputeTypeTest < Test::Unit::TestCase
173
+ fixtures :companies
174
+
175
+ def setup
176
+ Dependencies.log_activity = true
177
+ end
178
+
179
+ def teardown
180
+ Dependencies.log_activity = false
181
+ self.class.const_remove :FirmOnTheFly rescue nil
182
+ Firm.const_remove :FirmOnTheFly rescue nil
183
+ end
184
+
185
+ def test_instantiation_doesnt_try_to_require_corresponding_file
186
+ foo = Firm.find(:first).clone
187
+ foo.ruby_type = foo.type = 'FirmOnTheFly'
188
+ foo.save!
189
+
190
+ # Should fail without FirmOnTheFly in the type condition.
191
+ assert_raise(ActiveRecord::RecordNotFound) { Firm.find(foo.id) }
192
+
193
+ # Nest FirmOnTheFly in the test case where Dependencies won't see it.
194
+ self.class.const_set :FirmOnTheFly, Class.new(Firm)
195
+ assert_raise(ActiveRecord::SubclassNotFound) { Firm.find(foo.id) }
196
+
197
+ # Nest FirmOnTheFly in Firm where Dependencies will see it.
198
+ # This is analogous to nesting models in a migration.
199
+ Firm.const_set :FirmOnTheFly, Class.new(Firm)
200
+
201
+ # And instantiate will find the existing constant rather than trying
202
+ # to require firm_on_the_fly.
203
+ assert_nothing_raised { assert_kind_of Firm::FirmOnTheFly, Firm.find(foo.id) }
204
+ end
205
+ end
@@ -0,0 +1,137 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/topic'
3
+ require 'fixtures/developer'
4
+ require 'fixtures/reply'
5
+
6
+ class Topic; def after_find() end end
7
+ class Developer; def after_find() end end
8
+ class SpecialDeveloper < Developer; end
9
+
10
+ class TopicManualObserver
11
+ include Singleton
12
+
13
+ attr_reader :action, :object, :callbacks
14
+
15
+ def initialize
16
+ Topic.add_observer(self)
17
+ @callbacks = []
18
+ end
19
+
20
+ def update(callback_method, object)
21
+ @callbacks << { "callback_method" => callback_method, "object" => object }
22
+ end
23
+
24
+ def has_been_notified?
25
+ !@callbacks.empty?
26
+ end
27
+ end
28
+
29
+ class TopicaObserver < ActiveRecord::Observer
30
+ def self.observed_class() Topic end
31
+
32
+ attr_reader :topic
33
+
34
+ def after_find(topic)
35
+ @topic = topic
36
+ end
37
+ end
38
+
39
+ class TopicObserver < ActiveRecord::Observer
40
+ attr_reader :topic
41
+
42
+ def after_find(topic)
43
+ @topic = topic
44
+ end
45
+ end
46
+
47
+ class MultiObserver < ActiveRecord::Observer
48
+ attr_reader :record
49
+
50
+ def self.observed_class() [ Topic, Developer ] end
51
+
52
+ cattr_reader :last_inherited
53
+ @@last_inherited = nil
54
+
55
+ def observed_class_inherited_with_testing(subclass)
56
+ observed_class_inherited_without_testing(subclass)
57
+ @@last_inherited = subclass
58
+ end
59
+
60
+ alias_method_chain :observed_class_inherited, :testing
61
+
62
+ def after_find(record)
63
+ @record = record
64
+ end
65
+ end
66
+
67
+ class LifecycleTest < Test::Unit::TestCase
68
+ fixtures :topics, :developers
69
+
70
+ def test_before_destroy
71
+ assert_equal 2, Topic.count
72
+ Topic.find(1).destroy
73
+ assert_equal 0, Topic.count
74
+ end
75
+
76
+ def test_after_save
77
+ ActiveRecord::Base.observers = :topic_manual_observer
78
+ ActiveRecord::Base.instantiate_observers
79
+
80
+ topic = Topic.find(1)
81
+ topic.title = "hello"
82
+ topic.save
83
+
84
+ assert TopicManualObserver.instance.has_been_notified?
85
+ assert_equal :after_save, TopicManualObserver.instance.callbacks.last["callback_method"]
86
+ end
87
+
88
+ def test_observer_update_on_save
89
+ ActiveRecord::Base.observers = TopicManualObserver
90
+ ActiveRecord::Base.instantiate_observers
91
+
92
+ topic = Topic.find(1)
93
+ assert TopicManualObserver.instance.has_been_notified?
94
+ assert_equal :after_find, TopicManualObserver.instance.callbacks.first["callback_method"]
95
+ end
96
+
97
+ def test_auto_observer
98
+ topic_observer = TopicaObserver.instance
99
+
100
+ topic = Topic.find(1)
101
+ assert_equal topic.title, topic_observer.topic.title
102
+ end
103
+
104
+ def test_inferred_auto_observer
105
+ topic_observer = TopicObserver.instance
106
+
107
+ topic = Topic.find(1)
108
+ assert_equal topic.title, topic_observer.topic.title
109
+ end
110
+
111
+ def test_observing_two_classes
112
+ multi_observer = MultiObserver.instance
113
+
114
+ topic = Topic.find(1)
115
+ assert_equal topic.title, multi_observer.record.title
116
+
117
+ developer = Developer.find(1)
118
+ assert_equal developer.name, multi_observer.record.name
119
+ end
120
+
121
+ def test_observing_subclasses
122
+ multi_observer = MultiObserver.instance
123
+
124
+ developer = SpecialDeveloper.find(1)
125
+ assert_equal developer.name, multi_observer.record.name
126
+
127
+ klass = Class.new(Developer)
128
+ assert_equal klass, multi_observer.last_inherited
129
+
130
+ developer = klass.find(1)
131
+ assert_equal developer.name, multi_observer.record.name
132
+ end
133
+
134
+ def test_invalid_observer
135
+ assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
136
+ end
137
+ end
@@ -0,0 +1,190 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/person'
3
+ require 'fixtures/legacy_thing'
4
+
5
+ class LockWithoutDefault < ActiveRecord::Base; end
6
+
7
+ class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
8
+ set_table_name :lock_without_defaults_cust
9
+ set_locking_column :custom_lock_version
10
+ end
11
+
12
+ class OptimisticLockingTest < Test::Unit::TestCase
13
+ fixtures :people, :legacy_things
14
+
15
+ def test_lock_existing
16
+ p1 = Person.find(1)
17
+ p2 = Person.find(1)
18
+ assert_equal 0, p1.lock_version
19
+ assert_equal 0, p2.lock_version
20
+
21
+ p1.save!
22
+ assert_equal 1, p1.lock_version
23
+ assert_equal 0, p2.lock_version
24
+
25
+ assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
26
+ end
27
+
28
+ def test_lock_new
29
+ p1 = Person.new(:first_name => 'anika')
30
+ assert_equal 0, p1.lock_version
31
+
32
+ p1.save!
33
+ p2 = Person.find(p1.id)
34
+ assert_equal 0, p1.lock_version
35
+ assert_equal 0, p2.lock_version
36
+
37
+ p1.save!
38
+ assert_equal 1, p1.lock_version
39
+ assert_equal 0, p2.lock_version
40
+
41
+ assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
42
+ end
43
+
44
+ def test_lock_column_name_existing
45
+ t1 = LegacyThing.find(1)
46
+ t2 = LegacyThing.find(1)
47
+ assert_equal 0, t1.version
48
+ assert_equal 0, t2.version
49
+
50
+ t1.save!
51
+ assert_equal 1, t1.version
52
+ assert_equal 0, t2.version
53
+
54
+ assert_raises(ActiveRecord::StaleObjectError) { t2.save! }
55
+ end
56
+
57
+ def test_lock_column_is_mass_assignable
58
+ p1 = Person.create(:first_name => 'bianca')
59
+ assert_equal 0, p1.lock_version
60
+ assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
61
+
62
+ p1.save!
63
+ assert_equal 1, p1.lock_version
64
+ assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
65
+ end
66
+
67
+ def test_lock_without_default_sets_version_to_zero
68
+ t1 = LockWithoutDefault.new
69
+ assert_equal 0, t1.lock_version
70
+ end
71
+
72
+ def test_lock_with_custom_column_without_default_sets_version_to_zero
73
+ t1 = LockWithCustomColumnWithoutDefault.new
74
+ assert_equal 0, t1.custom_lock_version
75
+ end
76
+ end
77
+
78
+
79
+ # TODO: test against the generated SQL since testing locking behavior itself
80
+ # is so cumbersome. Will deadlock Ruby threads if the underlying db.execute
81
+ # blocks, so separate script called by Kernel#system is needed.
82
+ # (See exec vs. async_exec in the PostgreSQL adapter.)
83
+
84
+ # TODO: The SQL Server and Sybase adapters currently have no support for pessimistic locking
85
+
86
+ unless current_adapter?(:SQLServerAdapter, :SybaseAdapter)
87
+ class PessimisticLockingTest < Test::Unit::TestCase
88
+ self.use_transactional_fixtures = false
89
+ fixtures :people, :readers
90
+
91
+ def setup
92
+ # Avoid introspection queries during tests.
93
+ Person.columns; Reader.columns
94
+
95
+ @allow_concurrency = ActiveRecord::Base.allow_concurrency
96
+ ActiveRecord::Base.allow_concurrency = true
97
+ end
98
+
99
+ def teardown
100
+ ActiveRecord::Base.allow_concurrency = @allow_concurrency
101
+ end
102
+
103
+ # Test typical find.
104
+ def test_sane_find_with_lock
105
+ assert_nothing_raised do
106
+ Person.transaction do
107
+ Person.find 1, :lock => true
108
+ end
109
+ end
110
+ end
111
+
112
+ # Test scoped lock.
113
+ def test_sane_find_with_scoped_lock
114
+ assert_nothing_raised do
115
+ Person.transaction do
116
+ Person.with_scope(:find => { :lock => true }) do
117
+ Person.find 1
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ # PostgreSQL protests SELECT ... FOR UPDATE on an outer join.
124
+ unless current_adapter?(:PostgreSQLAdapter)
125
+ # Test locked eager find.
126
+ def test_eager_find_with_lock
127
+ assert_nothing_raised do
128
+ Person.transaction do
129
+ Person.find 1, :include => :readers, :lock => true
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ # Locking a record reloads it.
136
+ def test_sane_lock_method
137
+ assert_nothing_raised do
138
+ Person.transaction do
139
+ person = Person.find 1
140
+ old, person.first_name = person.first_name, 'fooman'
141
+ person.lock!
142
+ assert_equal old, person.first_name
143
+ end
144
+ end
145
+ end
146
+
147
+ if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
148
+ def test_no_locks_no_wait
149
+ first, second = duel { Person.find 1 }
150
+ assert first.end > second.end
151
+ end
152
+
153
+ def test_second_lock_waits
154
+ assert [0.2, 1, 5].any? { |zzz|
155
+ first, second = duel(zzz) { Person.find 1, :lock => true }
156
+ second.end > first.end
157
+ }
158
+ end
159
+
160
+ protected
161
+ def duel(zzz = 5)
162
+ t0, t1, t2, t3 = nil, nil, nil, nil
163
+
164
+ a = Thread.new do
165
+ t0 = Time.now
166
+ Person.transaction do
167
+ yield
168
+ sleep zzz # block thread 2 for zzz seconds
169
+ end
170
+ t1 = Time.now
171
+ end
172
+
173
+ b = Thread.new do
174
+ sleep zzz / 2.0 # ensure thread 1 tx starts first
175
+ t2 = Time.now
176
+ Person.transaction { yield }
177
+ t3 = Time.now
178
+ end
179
+
180
+ a.join
181
+ b.join
182
+
183
+ assert t1 > t0 + zzz
184
+ assert t2 > t0
185
+ assert t3 > t2
186
+ [t0.to_f..t1.to_f, t2.to_f..t3.to_f]
187
+ end
188
+ end
189
+ end
190
+ end