activerecord 2.0.5 → 2.1.0

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

Potentially problematic release.


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

Files changed (289) hide show
  1. data/CHANGELOG +168 -6
  2. data/README +27 -22
  3. data/RUNNING_UNIT_TESTS +7 -4
  4. data/Rakefile +22 -25
  5. data/lib/active_record.rb +8 -2
  6. data/lib/active_record/aggregations.rb +21 -12
  7. data/lib/active_record/association_preload.rb +277 -0
  8. data/lib/active_record/associations.rb +481 -295
  9. data/lib/active_record/associations/association_collection.rb +162 -37
  10. data/lib/active_record/associations/association_proxy.rb +71 -7
  11. data/lib/active_record/associations/belongs_to_association.rb +5 -3
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -6
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -64
  14. data/lib/active_record/associations/has_many_association.rb +8 -73
  15. data/lib/active_record/associations/has_many_through_association.rb +68 -117
  16. data/lib/active_record/associations/has_one_association.rb +7 -5
  17. data/lib/active_record/associations/has_one_through_association.rb +28 -0
  18. data/lib/active_record/attribute_methods.rb +69 -19
  19. data/lib/active_record/base.rb +496 -275
  20. data/lib/active_record/calculations.rb +28 -21
  21. data/lib/active_record/callbacks.rb +9 -38
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +232 -45
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +141 -27
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -13
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +57 -24
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +143 -42
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  31. data/lib/active_record/connection_adapters/sqlite_adapter.rb +18 -10
  32. data/lib/active_record/dirty.rb +158 -0
  33. data/lib/active_record/fixtures.rb +121 -156
  34. data/lib/active_record/locking/optimistic.rb +14 -11
  35. data/lib/active_record/locking/pessimistic.rb +2 -2
  36. data/lib/active_record/migration.rb +157 -77
  37. data/lib/active_record/named_scope.rb +163 -0
  38. data/lib/active_record/observer.rb +19 -5
  39. data/lib/active_record/reflection.rb +34 -14
  40. data/lib/active_record/schema.rb +7 -14
  41. data/lib/active_record/schema_dumper.rb +4 -4
  42. data/lib/active_record/serialization.rb +5 -5
  43. data/lib/active_record/serializers/json_serializer.rb +37 -28
  44. data/lib/active_record/serializers/xml_serializer.rb +52 -29
  45. data/lib/active_record/test_case.rb +36 -0
  46. data/lib/active_record/timestamp.rb +4 -4
  47. data/lib/active_record/transactions.rb +3 -3
  48. data/lib/active_record/validations.rb +182 -248
  49. data/lib/active_record/version.rb +2 -2
  50. data/test/{fixtures → assets}/example.log +0 -0
  51. data/test/{fixtures → assets}/flowers.jpg +0 -0
  52. data/test/cases/aaa_create_tables_test.rb +24 -0
  53. data/test/cases/active_schema_test_mysql.rb +95 -0
  54. data/test/cases/active_schema_test_postgresql.rb +24 -0
  55. data/test/{adapter_test.rb → cases/adapter_test.rb} +15 -14
  56. data/test/{adapter_test_sqlserver.rb → cases/adapter_test_sqlserver.rb} +95 -95
  57. data/test/{aggregations_test.rb → cases/aggregations_test.rb} +20 -20
  58. data/test/{ar_schema_test.rb → cases/ar_schema_test.rb} +6 -6
  59. data/test/cases/associations/belongs_to_associations_test.rb +412 -0
  60. data/test/{associations → cases/associations}/callbacks_test.rb +24 -10
  61. data/test/{associations → cases/associations}/cascaded_eager_loading_test.rb +18 -17
  62. data/test/cases/associations/eager_load_nested_include_test.rb +83 -0
  63. data/test/{associations → cases/associations}/eager_singularization_test.rb +5 -5
  64. data/test/{associations → cases/associations}/eager_test.rb +216 -51
  65. data/test/{associations → cases/associations}/extension_test.rb +8 -8
  66. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +684 -0
  67. data/test/cases/associations/has_many_associations_test.rb +932 -0
  68. data/test/cases/associations/has_many_through_associations_test.rb +190 -0
  69. data/test/cases/associations/has_one_associations_test.rb +323 -0
  70. data/test/cases/associations/has_one_through_associations_test.rb +74 -0
  71. data/test/{associations → cases/associations}/inner_join_association_test.rb +20 -20
  72. data/test/{associations → cases/associations}/join_model_test.rb +175 -35
  73. data/test/cases/associations_test.rb +262 -0
  74. data/test/{attribute_methods_test.rb → cases/attribute_methods_test.rb} +103 -11
  75. data/test/{base_test.rb → cases/base_test.rb} +338 -191
  76. data/test/{binary_test.rb → cases/binary_test.rb} +6 -4
  77. data/test/{calculations_test.rb → cases/calculations_test.rb} +35 -23
  78. data/test/{callbacks_test.rb → cases/callbacks_test.rb} +7 -7
  79. data/test/{class_inheritable_attributes_test.rb → cases/class_inheritable_attributes_test.rb} +3 -3
  80. data/test/{column_alias_test.rb → cases/column_alias_test.rb} +3 -3
  81. data/test/{connection_test_firebird.rb → cases/connection_test_firebird.rb} +2 -2
  82. data/test/{connection_test_mysql.rb → cases/connection_test_mysql.rb} +2 -2
  83. data/test/{copy_table_test_sqlite.rb → cases/copy_table_test_sqlite.rb} +13 -13
  84. data/test/{datatype_test_postgresql.rb → cases/datatype_test_postgresql.rb} +8 -8
  85. data/test/{date_time_test.rb → cases/date_time_test.rb} +5 -5
  86. data/test/{default_test_firebird.rb → cases/default_test_firebird.rb} +3 -3
  87. data/test/{defaults_test.rb → cases/defaults_test.rb} +8 -6
  88. data/test/{deprecated_finder_test.rb → cases/deprecated_finder_test.rb} +3 -3
  89. data/test/cases/dirty_test.rb +163 -0
  90. data/test/cases/finder_respond_to_test.rb +76 -0
  91. data/test/{finder_test.rb → cases/finder_test.rb} +266 -33
  92. data/test/{fixtures_test.rb → cases/fixtures_test.rb} +88 -72
  93. data/test/cases/helper.rb +47 -0
  94. data/test/{inheritance_test.rb → cases/inheritance_test.rb} +61 -17
  95. data/test/cases/invalid_date_test.rb +24 -0
  96. data/test/{json_serialization_test.rb → cases/json_serialization_test.rb} +36 -11
  97. data/test/{lifecycle_test.rb → cases/lifecycle_test.rb} +16 -13
  98. data/test/{locking_test.rb → cases/locking_test.rb} +17 -10
  99. data/test/{method_scoping_test.rb → cases/method_scoping_test.rb} +75 -39
  100. data/test/{migration_test.rb → cases/migration_test.rb} +420 -80
  101. data/test/{migration_test_firebird.rb → cases/migration_test_firebird.rb} +3 -3
  102. data/test/{mixin_test.rb → cases/mixin_test.rb} +7 -6
  103. data/test/{modules_test.rb → cases/modules_test.rb} +11 -6
  104. data/test/{multiple_db_test.rb → cases/multiple_db_test.rb} +5 -5
  105. data/test/cases/named_scope_test.rb +157 -0
  106. data/test/{pk_test.rb → cases/pk_test.rb} +10 -10
  107. data/test/{query_cache_test.rb → cases/query_cache_test.rb} +12 -10
  108. data/test/{readonly_test.rb → cases/readonly_test.rb} +11 -11
  109. data/test/{reflection_test.rb → cases/reflection_test.rb} +15 -14
  110. data/test/{reserved_word_test_mysql.rb → cases/reserved_word_test_mysql.rb} +4 -5
  111. data/test/{schema_authorization_test_postgresql.rb → cases/schema_authorization_test_postgresql.rb} +5 -5
  112. data/test/cases/schema_dumper_test.rb +138 -0
  113. data/test/cases/schema_test_postgresql.rb +102 -0
  114. data/test/{serialization_test.rb → cases/serialization_test.rb} +7 -7
  115. data/test/{synonym_test_oracle.rb → cases/synonym_test_oracle.rb} +5 -5
  116. data/test/{table_name_test_sqlserver.rb → cases/table_name_test_sqlserver.rb} +3 -3
  117. data/test/{threaded_connections_test.rb → cases/threaded_connections_test.rb} +7 -7
  118. data/test/{transactions_test.rb → cases/transactions_test.rb} +31 -5
  119. data/test/{unconnected_test.rb → cases/unconnected_test.rb} +2 -2
  120. data/test/{validations_test.rb → cases/validations_test.rb} +141 -39
  121. data/test/{xml_serialization_test.rb → cases/xml_serialization_test.rb} +12 -12
  122. data/test/config.rb +5 -0
  123. data/test/connections/native_db2/connection.rb +1 -1
  124. data/test/connections/native_firebird/connection.rb +1 -1
  125. data/test/connections/native_frontbase/connection.rb +1 -1
  126. data/test/connections/native_mysql/connection.rb +1 -1
  127. data/test/connections/native_openbase/connection.rb +1 -1
  128. data/test/connections/native_oracle/connection.rb +1 -1
  129. data/test/connections/native_postgresql/connection.rb +1 -3
  130. data/test/connections/native_sqlite/connection.rb +2 -2
  131. data/test/connections/native_sqlite3/connection.rb +2 -2
  132. data/test/connections/native_sqlite3/in_memory_connection.rb +3 -3
  133. data/test/connections/native_sybase/connection.rb +1 -1
  134. data/test/fixtures/author_addresses.yml +5 -0
  135. data/test/fixtures/authors.yml +2 -0
  136. data/test/fixtures/clubs.yml +6 -0
  137. data/test/fixtures/jobs.yml +7 -0
  138. data/test/fixtures/members.yml +4 -0
  139. data/test/fixtures/memberships.yml +20 -0
  140. data/test/fixtures/owners.yml +7 -0
  141. data/test/fixtures/people.yml +4 -1
  142. data/test/fixtures/pets.yml +14 -0
  143. data/test/fixtures/posts.yml +1 -0
  144. data/test/fixtures/price_estimates.yml +7 -0
  145. data/test/fixtures/readers.yml +5 -0
  146. data/test/fixtures/references.yml +17 -0
  147. data/test/fixtures/sponsors.yml +9 -0
  148. data/test/fixtures/subscribers.yml +7 -0
  149. data/test/fixtures/subscriptions.yml +12 -0
  150. data/test/fixtures/taggings.yml +4 -1
  151. data/test/fixtures/topics.yml +22 -2
  152. data/test/fixtures/warehouse-things.yml +3 -0
  153. data/test/{fixtures/migrations_with_decimal → migrations/decimal}/1_give_me_big_numbers.rb +0 -0
  154. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/1_people_have_last_names.rb +1 -1
  155. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/2_we_need_reminders.rb +1 -1
  156. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/3_foo.rb +0 -0
  157. data/test/{fixtures/migrations → migrations/duplicate}/3_innocent_jointable.rb +0 -0
  158. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  159. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  160. data/test/{fixtures/migrations_with_duplicate → migrations/interleaved/pass_1}/3_innocent_jointable.rb +0 -0
  161. data/test/{fixtures/migrations → migrations/interleaved/pass_2}/1_people_have_last_names.rb +1 -1
  162. data/test/{fixtures/migrations_with_missing_versions/4_innocent_jointable.rb → migrations/interleaved/pass_2/3_innocent_jointable.rb} +0 -0
  163. data/test/{fixtures/migrations_with_missing_versions → migrations/interleaved/pass_3}/1_people_have_last_names.rb +1 -1
  164. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  165. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  166. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/1000_people_have_middle_names.rb +1 -1
  167. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  168. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/3_we_need_reminders.rb +1 -1
  169. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  170. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  171. data/test/{fixtures/migrations → migrations/valid}/2_we_need_reminders.rb +1 -1
  172. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  173. data/test/{fixtures → models}/author.rb +28 -4
  174. data/test/{fixtures → models}/auto_id.rb +0 -0
  175. data/test/{fixtures → models}/binary.rb +0 -0
  176. data/test/{fixtures → models}/book.rb +0 -0
  177. data/test/{fixtures → models}/categorization.rb +0 -0
  178. data/test/{fixtures → models}/category.rb +8 -5
  179. data/test/{fixtures → models}/citation.rb +0 -0
  180. data/test/models/club.rb +7 -0
  181. data/test/{fixtures → models}/column_name.rb +0 -0
  182. data/test/{fixtures → models}/comment.rb +5 -3
  183. data/test/{fixtures → models}/company.rb +15 -6
  184. data/test/{fixtures → models}/company_in_module.rb +5 -3
  185. data/test/{fixtures → models}/computer.rb +0 -1
  186. data/test/{fixtures → models}/contact.rb +1 -1
  187. data/test/{fixtures → models}/course.rb +0 -0
  188. data/test/{fixtures → models}/customer.rb +8 -8
  189. data/test/{fixtures → models}/default.rb +0 -0
  190. data/test/{fixtures → models}/developer.rb +14 -10
  191. data/test/{fixtures → models}/edge.rb +0 -0
  192. data/test/{fixtures → models}/entrant.rb +0 -0
  193. data/test/models/guid.rb +2 -0
  194. data/test/{fixtures → models}/item.rb +0 -0
  195. data/test/models/job.rb +5 -0
  196. data/test/{fixtures → models}/joke.rb +0 -0
  197. data/test/{fixtures → models}/keyboard.rb +0 -0
  198. data/test/{fixtures → models}/legacy_thing.rb +0 -0
  199. data/test/{fixtures → models}/matey.rb +0 -0
  200. data/test/models/member.rb +9 -0
  201. data/test/models/membership.rb +9 -0
  202. data/test/{fixtures → models}/minimalistic.rb +0 -0
  203. data/test/{fixtures → models}/mixed_case_monkey.rb +0 -0
  204. data/test/{fixtures → models}/movie.rb +0 -0
  205. data/test/{fixtures → models}/order.rb +2 -2
  206. data/test/models/owner.rb +4 -0
  207. data/test/{fixtures → models}/parrot.rb +0 -0
  208. data/test/models/person.rb +10 -0
  209. data/test/models/pet.rb +4 -0
  210. data/test/models/pirate.rb +9 -0
  211. data/test/{fixtures → models}/post.rb +23 -2
  212. data/test/models/price_estimate.rb +3 -0
  213. data/test/{fixtures → models}/project.rb +1 -0
  214. data/test/{fixtures → models}/reader.rb +0 -0
  215. data/test/models/reference.rb +4 -0
  216. data/test/{fixtures → models}/reply.rb +7 -5
  217. data/test/{fixtures → models}/ship.rb +0 -0
  218. data/test/models/sponsor.rb +4 -0
  219. data/test/{fixtures → models}/subject.rb +0 -0
  220. data/test/{fixtures → models}/subscriber.rb +2 -0
  221. data/test/models/subscription.rb +4 -0
  222. data/test/{fixtures → models}/tag.rb +0 -0
  223. data/test/{fixtures → models}/tagging.rb +0 -0
  224. data/test/{fixtures → models}/task.rb +0 -0
  225. data/test/{fixtures → models}/topic.rb +32 -4
  226. data/test/{fixtures → models}/treasure.rb +2 -0
  227. data/test/{fixtures → models}/vertex.rb +0 -0
  228. data/test/models/warehouse_thing.rb +5 -0
  229. data/test/schema/mysql_specific_schema.rb +12 -0
  230. data/test/schema/postgresql_specific_schema.rb +103 -0
  231. data/test/schema/schema.rb +421 -0
  232. data/test/schema/schema2.rb +6 -0
  233. data/test/schema/sqlite_specific_schema.rb +25 -0
  234. data/test/schema/sqlserver_specific_schema.rb +5 -0
  235. metadata +192 -176
  236. data/test/aaa_create_tables_test.rb +0 -72
  237. data/test/abstract_unit.rb +0 -84
  238. data/test/active_schema_test_mysql.rb +0 -46
  239. data/test/all.sh +0 -8
  240. data/test/association_inheritance_reload.rb +0 -14
  241. data/test/associations_test.rb +0 -2177
  242. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +0 -1
  243. data/test/fixtures/bad_fixtures/attr_with_spaces +0 -1
  244. data/test/fixtures/bad_fixtures/blank_line +0 -3
  245. data/test/fixtures/bad_fixtures/duplicate_attributes +0 -3
  246. data/test/fixtures/bad_fixtures/missing_value +0 -1
  247. data/test/fixtures/db_definitions/db2.drop.sql +0 -33
  248. data/test/fixtures/db_definitions/db2.sql +0 -235
  249. data/test/fixtures/db_definitions/db22.drop.sql +0 -2
  250. data/test/fixtures/db_definitions/db22.sql +0 -5
  251. data/test/fixtures/db_definitions/firebird.drop.sql +0 -65
  252. data/test/fixtures/db_definitions/firebird.sql +0 -310
  253. data/test/fixtures/db_definitions/firebird2.drop.sql +0 -2
  254. data/test/fixtures/db_definitions/firebird2.sql +0 -6
  255. data/test/fixtures/db_definitions/frontbase.drop.sql +0 -33
  256. data/test/fixtures/db_definitions/frontbase.sql +0 -273
  257. data/test/fixtures/db_definitions/frontbase2.drop.sql +0 -1
  258. data/test/fixtures/db_definitions/frontbase2.sql +0 -4
  259. data/test/fixtures/db_definitions/openbase.drop.sql +0 -2
  260. data/test/fixtures/db_definitions/openbase.sql +0 -318
  261. data/test/fixtures/db_definitions/openbase2.drop.sql +0 -2
  262. data/test/fixtures/db_definitions/openbase2.sql +0 -7
  263. data/test/fixtures/db_definitions/oracle.drop.sql +0 -67
  264. data/test/fixtures/db_definitions/oracle.sql +0 -330
  265. data/test/fixtures/db_definitions/oracle2.drop.sql +0 -2
  266. data/test/fixtures/db_definitions/oracle2.sql +0 -6
  267. data/test/fixtures/db_definitions/postgresql.drop.sql +0 -44
  268. data/test/fixtures/db_definitions/postgresql.sql +0 -292
  269. data/test/fixtures/db_definitions/postgresql2.drop.sql +0 -2
  270. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  271. data/test/fixtures/db_definitions/schema.rb +0 -354
  272. data/test/fixtures/db_definitions/schema2.rb +0 -11
  273. data/test/fixtures/db_definitions/sqlite.drop.sql +0 -33
  274. data/test/fixtures/db_definitions/sqlite.sql +0 -219
  275. data/test/fixtures/db_definitions/sqlite2.drop.sql +0 -2
  276. data/test/fixtures/db_definitions/sqlite2.sql +0 -5
  277. data/test/fixtures/db_definitions/sybase.drop.sql +0 -35
  278. data/test/fixtures/db_definitions/sybase.sql +0 -222
  279. data/test/fixtures/db_definitions/sybase2.drop.sql +0 -4
  280. data/test/fixtures/db_definitions/sybase2.sql +0 -5
  281. data/test/fixtures/developers_projects/david_action_controller +0 -3
  282. data/test/fixtures/developers_projects/david_active_record +0 -3
  283. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  284. data/test/fixtures/person.rb +0 -4
  285. data/test/fixtures/pirate.rb +0 -5
  286. data/test/fixtures/subscribers/first +0 -2
  287. data/test/fixtures/subscribers/second +0 -2
  288. data/test/schema_dumper_test.rb +0 -131
  289. data/test/schema_test_postgresql.rb +0 -64
@@ -1,11 +1,11 @@
1
- require 'abstract_unit'
2
- require 'fixtures/default'
3
- require 'fixtures/entrant'
1
+ require "cases/helper"
2
+ require 'models/default'
3
+ require 'models/entrant'
4
4
 
5
- class DefaultTest < Test::Unit::TestCase
5
+ class DefaultTest < ActiveRecord::TestCase
6
6
  def test_nil_defaults_for_not_null_columns
7
7
  column_defaults =
8
- if current_adapter?(:MysqlAdapter)
8
+ if current_adapter?(:MysqlAdapter) && Mysql.client_version < 50051
9
9
  { 'id' => nil, 'name' => '', 'course_id' => nil }
10
10
  else
11
11
  { 'id' => nil, 'name' => nil, 'course_id' => nil }
@@ -61,7 +61,9 @@ class DefaultTest < Test::Unit::TestCase
61
61
 
62
62
  if current_adapter?(:PostgreSQLAdapter)
63
63
  def test_multiline_default_text
64
- assert_equal "--- []\n\n", Default.columns_hash['multiline_default'].default
64
+ # older postgres versions represent the default with escapes ("\\012" for a newline)
65
+ assert ( "--- []\n\n" == Default.columns_hash['multiline_default'].default ||
66
+ "--- []\\012\\012" == Default.columns_hash['multiline_default'].default)
65
67
  end
66
68
  end
67
69
  end
@@ -1,7 +1,7 @@
1
- require 'abstract_unit'
2
- require 'fixtures/entrant'
1
+ require "cases/helper"
2
+ require 'models/entrant'
3
3
 
4
- class DeprecatedFinderTest < Test::Unit::TestCase
4
+ class DeprecatedFinderTest < ActiveRecord::TestCase
5
5
  fixtures :entrants
6
6
 
7
7
  def test_deprecated_find_all_was_removed
@@ -0,0 +1,163 @@
1
+ require 'cases/helper'
2
+ require 'models/topic' # For booleans
3
+ require 'models/pirate' # For timestamps
4
+ require 'models/parrot'
5
+
6
+ class Pirate # Just reopening it, not defining it
7
+ attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
8
+ attr_accessor :changes_detected_in_after_update # Actual changes
9
+
10
+ after_update :check_changes
11
+
12
+ private
13
+ # after_save/update in sweepers, observers, and the model itself
14
+ # can end up checking dirty status and acting on the results
15
+ def check_changes
16
+ if self.changed?
17
+ self.detected_changes_in_after_update = true
18
+ self.changes_detected_in_after_update = self.changes
19
+ end
20
+ end
21
+ end
22
+
23
+ class DirtyTest < ActiveRecord::TestCase
24
+ def test_attribute_changes
25
+ # New record - no changes.
26
+ pirate = Pirate.new
27
+ assert !pirate.catchphrase_changed?
28
+ assert_nil pirate.catchphrase_change
29
+
30
+ # Change catchphrase.
31
+ pirate.catchphrase = 'arrr'
32
+ assert pirate.catchphrase_changed?
33
+ assert_nil pirate.catchphrase_was
34
+ assert_equal [nil, 'arrr'], pirate.catchphrase_change
35
+
36
+ # Saved - no changes.
37
+ pirate.save!
38
+ assert !pirate.catchphrase_changed?
39
+ assert_nil pirate.catchphrase_change
40
+
41
+ # Same value - no changes.
42
+ pirate.catchphrase = 'arrr'
43
+ assert !pirate.catchphrase_changed?
44
+ assert_nil pirate.catchphrase_change
45
+ end
46
+
47
+ def test_nullable_integer_not_marked_as_changed_if_new_value_is_blank
48
+ pirate = Pirate.new
49
+
50
+ ["", nil].each do |value|
51
+ pirate.parrot_id = value
52
+ assert !pirate.parrot_id_changed?
53
+ assert_nil pirate.parrot_id_change
54
+ end
55
+ end
56
+
57
+ def test_object_should_be_changed_if_any_attribute_is_changed
58
+ pirate = Pirate.new
59
+ assert !pirate.changed?
60
+ assert_equal [], pirate.changed
61
+ assert_equal Hash.new, pirate.changes
62
+
63
+ pirate.catchphrase = 'arrr'
64
+ assert pirate.changed?
65
+ assert_nil pirate.catchphrase_was
66
+ assert_equal %w(catchphrase), pirate.changed
67
+ assert_equal({'catchphrase' => [nil, 'arrr']}, pirate.changes)
68
+
69
+ pirate.save
70
+ assert !pirate.changed?
71
+ assert_equal [], pirate.changed
72
+ assert_equal Hash.new, pirate.changes
73
+ end
74
+
75
+ def test_attribute_will_change!
76
+ pirate = Pirate.create!(:catchphrase => 'arr')
77
+
78
+ pirate.catchphrase << ' matey'
79
+ assert !pirate.catchphrase_changed?
80
+
81
+ assert pirate.catchphrase_will_change!
82
+ assert pirate.catchphrase_changed?
83
+ assert_equal ['arr matey', 'arr matey'], pirate.catchphrase_change
84
+
85
+ pirate.catchphrase << '!'
86
+ assert pirate.catchphrase_changed?
87
+ assert_equal ['arr matey', 'arr matey!'], pirate.catchphrase_change
88
+ end
89
+
90
+ def test_association_assignment_changes_foreign_key
91
+ pirate = Pirate.create!(:catchphrase => 'jarl')
92
+ pirate.parrot = Parrot.create!
93
+ assert pirate.changed?
94
+ assert_equal %w(parrot_id), pirate.changed
95
+ end
96
+
97
+ def test_attribute_should_be_compared_with_type_cast
98
+ topic = Topic.new
99
+ assert topic.approved?
100
+ assert !topic.approved_changed?
101
+
102
+ # Coming from web form.
103
+ params = {:topic => {:approved => 1}}
104
+ # In the controller.
105
+ topic.attributes = params[:topic]
106
+ assert topic.approved?
107
+ assert !topic.approved_changed?
108
+ end
109
+
110
+ def test_partial_update
111
+ pirate = Pirate.new(:catchphrase => 'foo')
112
+ old_updated_on = 1.hour.ago.beginning_of_day
113
+
114
+ with_partial_updates Pirate, false do
115
+ assert_queries(2) { 2.times { pirate.save! } }
116
+ Pirate.update_all({ :updated_on => old_updated_on }, :id => pirate.id)
117
+ end
118
+
119
+ with_partial_updates Pirate, true do
120
+ assert_queries(0) { 2.times { pirate.save! } }
121
+ assert_equal old_updated_on, pirate.reload.updated_on
122
+
123
+ assert_queries(1) { pirate.catchphrase = 'bar'; pirate.save! }
124
+ assert_not_equal old_updated_on, pirate.reload.updated_on
125
+ end
126
+ end
127
+
128
+ def test_changed_attributes_should_be_preserved_if_save_failure
129
+ pirate = Pirate.new
130
+ pirate.parrot_id = 1
131
+ assert !pirate.save
132
+ check_pirate_after_save_failure(pirate)
133
+
134
+ pirate = Pirate.new
135
+ pirate.parrot_id = 1
136
+ assert_raises(ActiveRecord::RecordInvalid) { pirate.save! }
137
+ check_pirate_after_save_failure(pirate)
138
+ end
139
+
140
+ def test_reload_should_clear_changed_attributes
141
+ pirate = Pirate.create!(:catchphrase => "shiver me timbers")
142
+ pirate.catchphrase = "*hic*"
143
+ assert pirate.changed?
144
+ pirate.reload
145
+ assert !pirate.changed?
146
+ end
147
+
148
+ private
149
+ def with_partial_updates(klass, on = true)
150
+ old = klass.partial_updates?
151
+ klass.partial_updates = on
152
+ yield
153
+ ensure
154
+ klass.partial_updates = old
155
+ end
156
+
157
+ def check_pirate_after_save_failure(pirate)
158
+ assert pirate.changed?
159
+ assert pirate.parrot_id_changed?
160
+ assert_equal %w(parrot_id), pirate.changed
161
+ assert_nil pirate.parrot_id_was
162
+ end
163
+ end
@@ -0,0 +1,76 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+
4
+ class FinderRespondToTest < ActiveRecord::TestCase
5
+
6
+ fixtures :topics
7
+
8
+ def test_should_preserve_normal_respond_to_behaviour_and_respond_to_newly_added_method
9
+ class << Topic; self; end.send(:define_method, :method_added_for_finder_respond_to_test) { }
10
+ assert Topic.respond_to?(:method_added_for_finder_respond_to_test)
11
+ ensure
12
+ class << Topic; self; end.send(:remove_method, :method_added_for_finder_respond_to_test)
13
+ end
14
+
15
+ def test_should_preserve_normal_respond_to_behaviour_and_respond_to_standard_object_method
16
+ assert Topic.respond_to?(:to_s)
17
+ end
18
+
19
+ def test_should_respond_to_find_by_one_attribute_before_caching
20
+ ensure_topic_method_is_not_cached(:find_by_title)
21
+ assert Topic.respond_to?(:find_by_title)
22
+ end
23
+
24
+ def test_should_respond_to_find_all_by_one_attribute
25
+ ensure_topic_method_is_not_cached(:find_all_by_title)
26
+ assert Topic.respond_to?(:find_all_by_title)
27
+ end
28
+
29
+ def test_should_respond_to_find_all_by_two_attributes
30
+ ensure_topic_method_is_not_cached(:find_all_by_title_and_author_name)
31
+ assert Topic.respond_to?(:find_all_by_title_and_author_name)
32
+ end
33
+
34
+ def test_should_respond_to_find_by_two_attributes
35
+ ensure_topic_method_is_not_cached(:find_by_title_and_author_name)
36
+ assert Topic.respond_to?(:find_by_title_and_author_name)
37
+ end
38
+
39
+ def test_should_respond_to_find_or_initialize_from_one_attribute
40
+ ensure_topic_method_is_not_cached(:find_or_initialize_by_title)
41
+ assert Topic.respond_to?(:find_or_initialize_by_title)
42
+ end
43
+
44
+ def test_should_respond_to_find_or_initialize_from_two_attributes
45
+ ensure_topic_method_is_not_cached(:find_or_initialize_by_title_and_author_name)
46
+ assert Topic.respond_to?(:find_or_initialize_by_title_and_author_name)
47
+ end
48
+
49
+ def test_should_respond_to_find_or_create_from_one_attribute
50
+ ensure_topic_method_is_not_cached(:find_or_create_by_title)
51
+ assert Topic.respond_to?(:find_or_create_by_title)
52
+ end
53
+
54
+ def test_should_respond_to_find_or_create_from_two_attributes
55
+ ensure_topic_method_is_not_cached(:find_or_create_by_title_and_author_name)
56
+ assert Topic.respond_to?(:find_or_create_by_title_and_author_name)
57
+ end
58
+
59
+ def test_should_not_respond_to_find_by_one_missing_attribute
60
+ assert !Topic.respond_to?(:find_by_undertitle)
61
+ end
62
+
63
+ def test_should_not_respond_to_find_by_invalid_method_syntax
64
+ assert !Topic.respond_to?(:fail_to_find_by_title)
65
+ assert !Topic.respond_to?(:find_by_title?)
66
+ assert !Topic.respond_to?(:fail_to_find_or_create_by_title)
67
+ assert !Topic.respond_to?(:find_or_create_by_title?)
68
+ end
69
+
70
+ private
71
+
72
+ def ensure_topic_method_is_not_cached(method_id)
73
+ class << Topic; self; end.send(:remove_method, method_id) if Topic.public_methods.any? { |m| m.to_s == method_id.to_s }
74
+ end
75
+
76
+ end
@@ -1,15 +1,18 @@
1
- require 'abstract_unit'
2
- require 'fixtures/author'
3
- require 'fixtures/comment'
4
- require 'fixtures/company'
5
- require 'fixtures/topic'
6
- require 'fixtures/reply'
7
- require 'fixtures/entrant'
8
- require 'fixtures/developer'
9
- require 'fixtures/post'
10
-
11
- class FinderTest < Test::Unit::TestCase
12
- fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors
1
+ require "cases/helper"
2
+ require 'models/author'
3
+ require 'models/comment'
4
+ require 'models/company'
5
+ require 'models/topic'
6
+ require 'models/reply'
7
+ require 'models/entrant'
8
+ require 'models/developer'
9
+ require 'models/post'
10
+ require 'models/customer'
11
+ require 'models/job'
12
+ require 'models/categorization'
13
+
14
+ class FinderTest < ActiveRecord::TestCase
15
+ fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers
13
16
 
14
17
  def test_find
15
18
  assert_equal(topics(:first).title, Topic.find(1).title)
@@ -40,6 +43,21 @@ class FinderTest < Test::Unit::TestCase
40
43
  assert_raise(NoMethodError) { Topic.exists?([1,2]) }
41
44
  end
42
45
 
46
+ def test_exists_with_aggregate_having_three_mappings
47
+ existing_address = customers(:david).address
48
+ assert Customer.exists?(:address => existing_address)
49
+ end
50
+
51
+ def test_exists_with_aggregate_having_three_mappings_with_one_difference
52
+ existing_address = customers(:david).address
53
+ assert !Customer.exists?(:address =>
54
+ Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
55
+ assert !Customer.exists?(:address =>
56
+ Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
57
+ assert !Customer.exists?(:address =>
58
+ Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
59
+ end
60
+
43
61
  def test_find_by_array_of_one_id
44
62
  assert_kind_of(Array, Topic.find([ 1 ]))
45
63
  assert_equal(1, Topic.find([ 1 ]).length)
@@ -127,6 +145,14 @@ class FinderTest < Test::Unit::TestCase
127
145
  first = Topic.find(:first, :conditions => "title = 'The First Topic!'")
128
146
  assert_nil(first)
129
147
  end
148
+
149
+ def test_first
150
+ assert_equal topics(:second).title, Topic.first(:conditions => "title = 'The Second Topic of the day'").title
151
+ end
152
+
153
+ def test_first_failing
154
+ assert_nil Topic.first(:conditions => "title = 'The Second Topic of the day!'")
155
+ end
130
156
 
131
157
  def test_unexisting_record_exception_handling
132
158
  assert_raises(ActiveRecord::RecordNotFound) {
@@ -145,13 +171,13 @@ class FinderTest < Test::Unit::TestCase
145
171
  assert topic.attribute_present?("author_name")
146
172
  assert topic.respond_to?("author_name")
147
173
  end
148
-
174
+
149
175
  def test_find_on_blank_conditions
150
176
  [nil, " ", [], {}].each do |blank|
151
177
  assert_nothing_raised { Topic.find(:first, :conditions => blank) }
152
178
  end
153
179
  end
154
-
180
+
155
181
  def test_find_on_blank_bind_conditions
156
182
  [ [""], ["",{}] ].each do |blank|
157
183
  assert_nothing_raised { Topic.find(:first, :conditions => blank) }
@@ -173,6 +199,14 @@ class FinderTest < Test::Unit::TestCase
173
199
  assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) }
174
200
  end
175
201
 
202
+ def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
203
+ david = customers(:david)
204
+ assert Customer.find(david.id, :conditions => { 'customers.name' => david.name, :address => david.address })
205
+ assert_raises(ActiveRecord::RecordNotFound) {
206
+ Customer.find(david.id, :conditions => { 'customers.name' => david.name + "1", :address => david.address })
207
+ }
208
+ end
209
+
176
210
  def test_find_on_association_proxy_conditions
177
211
  assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10], Comment.find_all_by_post_id(authors(:david).posts).map(&:id).sort
178
212
  end
@@ -238,6 +272,48 @@ class FinderTest < Test::Unit::TestCase
238
272
  assert_nil topic.last_read
239
273
  end
240
274
 
275
+ def test_hash_condition_find_with_aggregate_having_one_mapping
276
+ balance = customers(:david).balance
277
+ assert_kind_of Money, balance
278
+ found_customer = Customer.find(:first, :conditions => {:balance => balance})
279
+ assert_equal customers(:david), found_customer
280
+ end
281
+
282
+ def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
283
+ gps_location = customers(:david).gps_location
284
+ assert_kind_of GpsLocation, gps_location
285
+ found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location})
286
+ assert_equal customers(:david), found_customer
287
+ end
288
+
289
+ def test_hash_condition_find_with_aggregate_having_one_mapping_and_key_value_being_attribute_value
290
+ balance = customers(:david).balance
291
+ assert_kind_of Money, balance
292
+ found_customer = Customer.find(:first, :conditions => {:balance => balance.amount})
293
+ assert_equal customers(:david), found_customer
294
+ end
295
+
296
+ def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_attribute_value
297
+ gps_location = customers(:david).gps_location
298
+ assert_kind_of GpsLocation, gps_location
299
+ found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location.gps_location})
300
+ assert_equal customers(:david), found_customer
301
+ end
302
+
303
+ def test_hash_condition_find_with_aggregate_having_three_mappings
304
+ address = customers(:david).address
305
+ assert_kind_of Address, address
306
+ found_customer = Customer.find(:first, :conditions => {:address => address})
307
+ assert_equal customers(:david), found_customer
308
+ end
309
+
310
+ def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
311
+ address = customers(:david).address
312
+ assert_kind_of Address, address
313
+ found_customer = Customer.find(:first, :conditions => {:address => address, :name => customers(:david).name})
314
+ assert_equal customers(:david), found_customer
315
+ end
316
+
241
317
  def test_bind_variables
242
318
  assert_kind_of Firm, Company.find(:first, :conditions => ["name = ?", "37signals"])
243
319
  assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!"])
@@ -273,6 +349,8 @@ class FinderTest < Test::Unit::TestCase
273
349
  def test_named_bind_variables
274
350
  assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
275
351
  assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
352
+
353
+ assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
276
354
 
277
355
  assert_kind_of Firm, Company.find(:first, :conditions => ["name = :name", { :name => "37signals" }])
278
356
  assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!" }])
@@ -337,18 +415,18 @@ class FinderTest < Test::Unit::TestCase
337
415
  assert_equal topics(:first), Topic.find_by_title("The First Topic")
338
416
  assert_nil Topic.find_by_title("The First Topic!")
339
417
  end
340
-
418
+
341
419
  def test_find_by_one_attribute_caches_dynamic_finder
342
420
  # ensure this test can run independently of order
343
- class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.respond_to?(:find_by_title)
344
- assert !Topic.respond_to?(:find_by_title)
421
+ class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
422
+ assert !Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
345
423
  t = Topic.find_by_title("The First Topic")
346
- assert Topic.respond_to?(:find_by_title)
424
+ assert Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
347
425
  end
348
426
 
349
427
  def test_dynamic_finder_returns_same_results_after_caching
350
428
  # ensure this test can run independently of order
351
- class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.respond_to?(:find_by_title)
429
+ class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_method_defined?(:find_by_title)
352
430
  t = Topic.find_by_title("The First Topic")
353
431
  assert_equal t, Topic.find_by_title("The First Topic") # find_by_title has been cached
354
432
  end
@@ -362,17 +440,51 @@ class FinderTest < Test::Unit::TestCase
362
440
  assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
363
441
  end
364
442
 
443
+ def test_find_by_one_attribute_that_is_an_aggregate
444
+ address = customers(:david).address
445
+ assert_kind_of Address, address
446
+ found_customer = Customer.find_by_address(address)
447
+ assert_equal customers(:david), found_customer
448
+ end
449
+
450
+ def test_find_by_one_attribute_that_is_an_aggregate_with_one_attribute_difference
451
+ address = customers(:david).address
452
+ assert_kind_of Address, address
453
+ missing_address = Address.new(address.street, address.city, address.country + "1")
454
+ assert_nil Customer.find_by_address(missing_address)
455
+ missing_address = Address.new(address.street, address.city + "1", address.country)
456
+ assert_nil Customer.find_by_address(missing_address)
457
+ missing_address = Address.new(address.street + "1", address.city, address.country)
458
+ assert_nil Customer.find_by_address(missing_address)
459
+ end
460
+
461
+ def test_find_by_two_attributes_that_are_both_aggregates
462
+ balance = customers(:david).balance
463
+ address = customers(:david).address
464
+ assert_kind_of Money, balance
465
+ assert_kind_of Address, address
466
+ found_customer = Customer.find_by_balance_and_address(balance, address)
467
+ assert_equal customers(:david), found_customer
468
+ end
469
+
470
+ def test_find_by_two_attributes_with_one_being_an_aggregate
471
+ balance = customers(:david).balance
472
+ assert_kind_of Money, balance
473
+ found_customer = Customer.find_by_balance_and_name(balance, customers(:david).name)
474
+ assert_equal customers(:david), found_customer
475
+ end
476
+
365
477
  def test_dynamic_finder_on_one_attribute_with_conditions_caches_method
366
478
  # ensure this test can run independently of order
367
- class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.respond_to?(:find_by_credit_limit)
368
- assert !Account.respond_to?(:find_by_credit_limit)
479
+ class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
480
+ assert !Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
369
481
  a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
370
- assert Account.respond_to?(:find_by_credit_limit)
482
+ assert Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
371
483
  end
372
484
 
373
485
  def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
374
486
  # ensure this test can run independently of order
375
- class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.respond_to?(:find_by_credit_limit)
487
+ class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
376
488
  a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
377
489
  assert_equal a, Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6]) # find_by_credit_limit has been cached
378
490
  end
@@ -405,6 +517,32 @@ class FinderTest < Test::Unit::TestCase
405
517
  assert_equal [], Topic.find_all_by_title("The First Topic!!")
406
518
  end
407
519
 
520
+ def test_find_all_by_one_attribute_that_is_an_aggregate
521
+ balance = customers(:david).balance
522
+ assert_kind_of Money, balance
523
+ found_customers = Customer.find_all_by_balance(balance)
524
+ assert_equal 1, found_customers.size
525
+ assert_equal customers(:david), found_customers.first
526
+ end
527
+
528
+ def test_find_all_by_two_attributes_that_are_both_aggregates
529
+ balance = customers(:david).balance
530
+ address = customers(:david).address
531
+ assert_kind_of Money, balance
532
+ assert_kind_of Address, address
533
+ found_customers = Customer.find_all_by_balance_and_address(balance, address)
534
+ assert_equal 1, found_customers.size
535
+ assert_equal customers(:david), found_customers.first
536
+ end
537
+
538
+ def test_find_all_by_two_attributes_with_one_being_an_aggregate
539
+ balance = customers(:david).balance
540
+ assert_kind_of Money, balance
541
+ found_customers = Customer.find_all_by_balance_and_name(balance, customers(:david).name)
542
+ assert_equal 1, found_customers.size
543
+ assert_equal customers(:david), found_customers.first
544
+ end
545
+
408
546
  def test_find_all_by_one_attribute_with_options
409
547
  topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC")
410
548
  assert topics(:first), topics.last
@@ -414,7 +552,7 @@ class FinderTest < Test::Unit::TestCase
414
552
  end
415
553
 
416
554
  def test_find_all_by_array_attribute
417
- assert_equal 2, Topic.find_all_by_title(["The First Topic", "The Second Topic's of the day"]).size
555
+ assert_equal 2, Topic.find_all_by_title(["The First Topic", "The Second Topic of the day"]).size
418
556
  end
419
557
 
420
558
  def test_find_all_by_boolean_attribute
@@ -423,7 +561,7 @@ class FinderTest < Test::Unit::TestCase
423
561
  assert topics.include?(topics(:first))
424
562
 
425
563
  topics = Topic.find_all_by_approved(true)
426
- assert_equal 1, topics.size
564
+ assert_equal 3, topics.size
427
565
  assert topics.include?(topics(:second))
428
566
  end
429
567
 
@@ -435,8 +573,8 @@ class FinderTest < Test::Unit::TestCase
435
573
 
436
574
  def test_find_all_by_nil_attribute
437
575
  topics = Topic.find_all_by_last_read nil
438
- assert_equal 1, topics.size
439
- assert_nil topics[0].last_read
576
+ assert_equal 3, topics.size
577
+ assert topics.collect(&:last_read).all?(&:nil?)
440
578
  end
441
579
 
442
580
  def test_find_by_nil_and_not_nil_attributes
@@ -466,6 +604,14 @@ class FinderTest < Test::Unit::TestCase
466
604
  assert !another.new_record?
467
605
  end
468
606
 
607
+ def test_find_or_create_from_two_attributes_with_one_being_an_aggregate
608
+ number_of_customers = Customer.count
609
+ created_customer = Customer.find_or_create_by_balance_and_name(Money.new(123), "Elizabeth")
610
+ assert_equal number_of_customers + 1, Customer.count
611
+ assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123), "Elizabeth")
612
+ assert !created_customer.new_record?
613
+ end
614
+
469
615
  def test_find_or_create_from_one_attribute_and_hash
470
616
  number_of_companies = Company.count
471
617
  sig38 = Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
@@ -477,18 +623,60 @@ class FinderTest < Test::Unit::TestCase
477
623
  assert_equal 23, sig38.client_of
478
624
  end
479
625
 
626
+ def test_find_or_create_from_one_aggregate_attribute
627
+ number_of_customers = Customer.count
628
+ created_customer = Customer.find_or_create_by_balance(Money.new(123))
629
+ assert_equal number_of_customers + 1, Customer.count
630
+ assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123))
631
+ assert !created_customer.new_record?
632
+ end
633
+
634
+ def test_find_or_create_from_one_aggregate_attribute_and_hash
635
+ number_of_customers = Customer.count
636
+ balance = Money.new(123)
637
+ name = "Elizabeth"
638
+ created_customer = Customer.find_or_create_by_balance({:balance => balance, :name => name})
639
+ assert_equal number_of_customers + 1, Customer.count
640
+ assert_equal created_customer, Customer.find_or_create_by_balance({:balance => balance, :name => name})
641
+ assert !created_customer.new_record?
642
+ assert_equal balance, created_customer.balance
643
+ assert_equal name, created_customer.name
644
+ end
645
+
480
646
  def test_find_or_initialize_from_one_attribute
481
647
  sig38 = Company.find_or_initialize_by_name("38signals")
482
648
  assert_equal "38signals", sig38.name
483
649
  assert sig38.new_record?
484
650
  end
485
-
651
+
652
+ def test_find_or_initialize_from_one_aggregate_attribute
653
+ new_customer = Customer.find_or_initialize_by_balance(Money.new(123))
654
+ assert_equal 123, new_customer.balance.amount
655
+ assert new_customer.new_record?
656
+ end
657
+
658
+ def test_find_or_initialize_from_one_attribute_should_not_set_attribute_even_when_protected
659
+ c = Company.find_or_initialize_by_name({:name => "Fortune 1000", :rating => 1000})
660
+ assert_equal "Fortune 1000", c.name
661
+ assert_not_equal 1000, c.rating
662
+ assert c.valid?
663
+ assert c.new_record?
664
+ end
665
+
666
+ def test_find_or_create_from_one_attribute_should_set_not_attribute_even_when_protected
667
+ c = Company.find_or_create_by_name({:name => "Fortune 1000", :rating => 1000})
668
+ assert_equal "Fortune 1000", c.name
669
+ assert_not_equal 1000, c.rating
670
+ assert c.valid?
671
+ assert !c.new_record?
672
+ end
673
+
486
674
  def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected
487
675
  c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000)
488
676
  assert_equal "Fortune 1000", c.name
489
677
  assert_equal 1000, c.rating
490
678
  assert c.valid?
491
- assert c.new_record?
679
+ assert c.new_record?
492
680
  end
493
681
 
494
682
  def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected
@@ -496,14 +684,30 @@ class FinderTest < Test::Unit::TestCase
496
684
  assert_equal "Fortune 1000", c.name
497
685
  assert_equal 1000, c.rating
498
686
  assert c.valid?
499
- assert !c.new_record?
687
+ assert !c.new_record?
688
+ end
689
+
690
+ def test_find_or_initialize_should_set_protected_attributes_if_given_as_block
691
+ c = Company.find_or_initialize_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
692
+ assert_equal "Fortune 1000", c.name
693
+ assert_equal 1000.to_f, c.rating.to_f
694
+ assert c.valid?
695
+ assert c.new_record?
500
696
  end
501
697
 
698
+ def test_find_or_create_should_set_protected_attributes_if_given_as_block
699
+ c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
700
+ assert_equal "Fortune 1000", c.name
701
+ assert_equal 1000.to_f, c.rating.to_f
702
+ assert c.valid?
703
+ assert !c.new_record?
704
+ end
705
+
502
706
  def test_dynamic_find_or_initialize_from_one_attribute_caches_method
503
- class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.respond_to?(:find_or_initialize_by_name)
504
- assert !Company.respond_to?(:find_or_initialize_by_name)
707
+ class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
708
+ assert !Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
505
709
  sig38 = Company.find_or_initialize_by_name("38signals")
506
- assert Company.respond_to?(:find_or_initialize_by_name)
710
+ assert Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
507
711
  end
508
712
 
509
713
  def test_find_or_initialize_from_two_attributes
@@ -513,6 +717,13 @@ class FinderTest < Test::Unit::TestCase
513
717
  assert another.new_record?
514
718
  end
515
719
 
720
+ def test_find_or_initialize_from_one_aggregate_attribute_and_one_not
721
+ new_customer = Customer.find_or_initialize_by_balance_and_name(Money.new(123), "Elizabeth")
722
+ assert_equal 123, new_customer.balance.amount
723
+ assert_equal "Elizabeth", new_customer.name
724
+ assert new_customer.new_record?
725
+ end
726
+
516
727
  def test_find_or_initialize_from_one_attribute_and_hash
517
728
  sig38 = Company.find_or_initialize_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
518
729
  assert_equal "38signals", sig38.name
@@ -521,6 +732,15 @@ class FinderTest < Test::Unit::TestCase
521
732
  assert sig38.new_record?
522
733
  end
523
734
 
735
+ def test_find_or_initialize_from_one_aggregate_attribute_and_hash
736
+ balance = Money.new(123)
737
+ name = "Elizabeth"
738
+ new_customer = Customer.find_or_initialize_by_balance({:balance => balance, :name => name})
739
+ assert_equal balance, new_customer.balance
740
+ assert_equal name, new_customer.name
741
+ assert new_customer.new_record?
742
+ end
743
+
524
744
  def test_find_with_bad_sql
525
745
  assert_raises(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
526
746
  end
@@ -639,6 +859,19 @@ class FinderTest < Test::Unit::TestCase
639
859
  Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}}
640
860
  end
641
861
 
862
+ def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
863
+ assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => ' author_addresses.id DESC ', :limit => 2).size
864
+
865
+ assert_equal 3, Post.find(:all, :include => { :author => :author_address, :authors => :author_address},
866
+ :order => ' author_addresses_authors.id DESC ', :limit => 3).size
867
+ end
868
+
869
+ def test_with_limiting_with_custom_select
870
+ posts = Post.find(:all, :include => :author, :select => ' posts.*, authors.id as "author_id"', :limit => 3, :order => 'posts.id')
871
+ assert_equal 3, posts.size
872
+ assert_equal [0, 1, 1], posts.map(&:author_id).sort
873
+ end
874
+
642
875
  protected
643
876
  def bind(statement, *vars)
644
877
  if vars.first.is_a?(Hash)