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
@@ -9,16 +9,16 @@ module ActiveRecord
9
9
  # Count operates using three different approaches.
10
10
  #
11
11
  # * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
12
- # * Count using column : By passing a column name to count, it will return a count of all the rows for the model with supplied column present
12
+ # * Count using column: By passing a column name to count, it will return a count of all the rows for the model with supplied column present
13
13
  # * Count using options will find the row count matched by the options used.
14
14
  #
15
15
  # The third approach, count using options, accepts an option hash as the only parameter. The options are:
16
16
  #
17
17
  # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
18
18
  # * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
19
- # or named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s).
19
+ # or named associations in the same form used for the <tt>:include</tt> option, which will perform an INNER JOIN on the associated table(s).
20
20
  # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
21
- # Pass :readonly => false to override.
21
+ # Pass <tt>:readonly => false</tt> to override.
22
22
  # * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
23
23
  # to already defined associations. When using named associations, count returns the number of DISTINCT items for the model you're counting.
24
24
  # See eager loading under Associations.
@@ -41,45 +41,45 @@ module ActiveRecord
41
41
  # Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
42
42
  # Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
43
43
  #
44
- # Note: Person.count(:all) will not work because it will use :all as the condition. Use Person.count instead.
44
+ # Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition. Use Person.count instead.
45
45
  def count(*args)
46
46
  calculate(:count, *construct_count_options_from_args(*args))
47
47
  end
48
48
 
49
- # Calculates the average value on a given column. The value is returned as a float. See #calculate for examples with options.
49
+ # Calculates the average value on a given column. The value is returned as a float. See +calculate+ for examples with options.
50
50
  #
51
51
  # Person.average('age')
52
52
  def average(column_name, options = {})
53
53
  calculate(:avg, column_name, options)
54
54
  end
55
55
 
56
- # Calculates the minimum value on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
56
+ # Calculates the minimum value on a given column. The value is returned with the same data type of the column. See +calculate+ for examples with options.
57
57
  #
58
58
  # Person.minimum('age')
59
59
  def minimum(column_name, options = {})
60
60
  calculate(:min, column_name, options)
61
61
  end
62
62
 
63
- # Calculates the maximum value on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
63
+ # Calculates the maximum value on a given column. The value is returned with the same data type of the column. See +calculate+ for examples with options.
64
64
  #
65
65
  # Person.maximum('age')
66
66
  def maximum(column_name, options = {})
67
67
  calculate(:max, column_name, options)
68
68
  end
69
69
 
70
- # Calculates the sum of values on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
70
+ # Calculates the sum of values on a given column. The value is returned with the same data type of the column. See +calculate+ for examples with options.
71
71
  #
72
72
  # Person.sum('age')
73
73
  def sum(column_name, options = {})
74
- calculate(:sum, column_name, options)
74
+ calculate(:sum, column_name, options) || 0
75
75
  end
76
76
 
77
77
  # This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
78
- # Options such as :conditions, :order, :group, :having, and :joins can be passed to customize the query.
78
+ # Options such as <tt>:conditions</tt>, <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
79
79
  #
80
80
  # There are two basic forms of output:
81
81
  # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float for AVG, and the given column's type for everything else.
82
- # * Grouped values: This returns an ordered hash of the values and groups them by the :group option. It takes either a column name, or the name
82
+ # * Grouped values: This returns an ordered hash of the values and groups them by the <tt>:group</tt> option. It takes either a column name, or the name
83
83
  # of a belongs_to association.
84
84
  #
85
85
  # values = Person.maximum(:age, :group => 'last_name')
@@ -111,6 +111,7 @@ module ActiveRecord
111
111
  # Person.average(:age) # SELECT AVG(age) FROM people...
112
112
  # Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for everyone with a last name other than 'Drake'
113
113
  # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name) # Selects the minimum age for any family without any minors
114
+ # Person.sum("2 * age")
114
115
  def calculate(operation, column_name, options = {})
115
116
  validate_calculation_options(operation, options)
116
117
  column_name = options[:select] if options[:select]
@@ -155,7 +156,7 @@ module ActiveRecord
155
156
  scope = scope(:find)
156
157
  merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
157
158
  aggregate_alias = column_alias_for(operation, column_name)
158
- column_name = "#{connection.quote_table_name(table_name)}.#{column_name}" unless column_name == "*" || column_name.to_s.include?('.')
159
+ column_name = "#{connection.quote_table_name(table_name)}.#{column_name}" if column_names.include?(column_name.to_s)
159
160
 
160
161
  if operation == 'count'
161
162
  if merged_includes.any?
@@ -168,13 +169,16 @@ module ActiveRecord
168
169
  end
169
170
  end
170
171
 
171
- sql = "SELECT #{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name}) AS #{aggregate_alias}"
172
+ if options[:distinct] && column_name.to_s !~ /\s*DISTINCT\s+/i
173
+ distinct = 'DISTINCT '
174
+ end
175
+ sql = "SELECT #{operation}(#{distinct}#{column_name}) AS #{aggregate_alias}"
172
176
 
173
177
  # A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
174
178
  sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
175
179
 
176
180
  sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
177
- sql << " FROM (SELECT DISTINCT #{column_name}" if use_workaround
181
+ sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
178
182
  sql << " FROM #{connection.quote_table_name(table_name)} "
179
183
  if merged_includes.any?
180
184
  join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, options[:joins])
@@ -231,7 +235,8 @@ module ActiveRecord
231
235
  key = type_cast_calculated_value(row[group_alias], group_column)
232
236
  key = key_records[key] if associated
233
237
  value = row[aggregate_alias]
234
- all << [key, type_cast_calculated_value(value, column, operation)]
238
+ all[key] = type_cast_calculated_value(value, column, operation)
239
+ all
235
240
  end
236
241
  end
237
242
 
@@ -240,12 +245,14 @@ module ActiveRecord
240
245
  options.assert_valid_keys(CALCULATIONS_OPTIONS)
241
246
  end
242
247
 
243
- # Converts a given key to the value that the database adapter returns as
244
- # a usable column name.
245
- # users.id #=> users_id
246
- # sum(id) #=> sum_id
247
- # count(distinct users.id) #=> count_distinct_users_id
248
- # count(*) #=> count_all
248
+ # Converts the given keys to the value that the database adapter returns as
249
+ # a usable column name:
250
+ #
251
+ # column_alias_for("users.id") # => "users_id"
252
+ # column_alias_for("sum(id)") # => "sum_id"
253
+ # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
254
+ # column_alias_for("count(*)") # => "count_all"
255
+ # column_alias_for("count", "id") # => "count_id"
249
256
  def column_alias_for(*keys)
250
257
  connection.table_alias_for(keys.join(' ').downcase.gsub(/\*/, 'all').gsub(/\W+/, ' ').strip.gsub(/ +/, '_'))
251
258
  end
@@ -161,7 +161,7 @@ module ActiveRecord
161
161
  # == <tt>before_validation*</tt> returning statements
162
162
  #
163
163
  # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be aborted and <tt>Base#save</tt> will return +false+.
164
- # If <tt>Base#save!</tt> is called it will raise a +RecordNotSaved+ exception.
164
+ # If Base#save! is called it will raise a RecordNotSaved exception.
165
165
  # Nothing will be appended to the errors object.
166
166
  #
167
167
  # == Canceling callbacks
@@ -183,14 +183,8 @@ module ActiveRecord
183
183
  base.send :alias_method_chain, method, :callbacks
184
184
  end
185
185
 
186
- CALLBACKS.each do |method|
187
- base.class_eval <<-"end_eval"
188
- def self.#{method}(*callbacks, &block)
189
- callbacks << block if block_given?
190
- write_inheritable_array(#{method.to_sym.inspect}, callbacks)
191
- end
192
- end_eval
193
- end
186
+ base.send :include, ActiveSupport::Callbacks
187
+ base.define_callbacks *CALLBACKS
194
188
  end
195
189
 
196
190
  # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
@@ -235,9 +229,9 @@ module ActiveRecord
235
229
  # Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
236
230
  def after_update() end
237
231
 
238
- def update_with_callbacks #:nodoc:
232
+ def update_with_callbacks(*args) #:nodoc:
239
233
  return false if callback(:before_update) == false
240
- result = update_without_callbacks
234
+ result = update_without_callbacks(*args)
241
235
  callback(:after_update)
242
236
  result
243
237
  end
@@ -301,38 +295,15 @@ module ActiveRecord
301
295
  def callback(method)
302
296
  notify(method)
303
297
 
304
- callbacks_for(method).each do |callback|
305
- result = case callback
306
- when Symbol
307
- self.send(callback)
308
- when String
309
- eval(callback, binding)
310
- when Proc, Method
311
- callback.call(self)
312
- else
313
- if callback.respond_to?(method)
314
- callback.send(method, self)
315
- else
316
- raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
317
- end
318
- end
319
- return false if result == false
320
- end
298
+ result = run_callbacks(method) { |result, object| result == false }
321
299
 
322
- result = send(method) if respond_to_without_attributes?(method)
300
+ if result != false && respond_to_without_attributes?(method)
301
+ result = send(method)
302
+ end
323
303
 
324
304
  return result
325
305
  end
326
306
 
327
- def callbacks_for(method)
328
- self.class.read_inheritable_attribute(method.to_sym) or []
329
- end
330
-
331
- def invoke_and_notify(method)
332
- notify(method)
333
- send(method) if respond_to_without_attributes?(method)
334
- end
335
-
336
307
  def notify(method) #:nodoc:
337
308
  self.class.changed
338
309
  self.class.notify_observers(method, self)
@@ -175,7 +175,7 @@ module ActiveRecord
175
175
  end
176
176
 
177
177
  # Establishes the connection to the database. Accepts a hash as input where
178
- # the :adapter key must be specified with the name of a database adapter (in lower-case)
178
+ # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
179
179
  # example for regular databases (MySQL, Postgresql, etc):
180
180
  #
181
181
  # ActiveRecord::Base.establish_connection(
@@ -193,7 +193,8 @@ module ActiveRecord
193
193
  # :database => "path/to/dbfile"
194
194
  # )
195
195
  #
196
- # Also accepts keys as strings (for parsing from yaml for example):
196
+ # Also accepts keys as strings (for parsing from YAML for example):
197
+ #
197
198
  # ActiveRecord::Base.establish_connection(
198
199
  # "adapter" => "sqlite",
199
200
  # "database" => "path/to/dbfile"
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  end
30
30
 
31
31
  # Returns an array of arrays containing the field values.
32
- # Order is the same as that returned by #columns.
32
+ # Order is the same as that returned by +columns+.
33
33
  def select_rows(sql, name = nil)
34
34
  raise NotImplementedError, "select_rows is an abstract method"
35
35
  end
@@ -93,7 +93,7 @@ module ActiveRecord
93
93
  # done if the transaction block raises an exception or returns false.
94
94
  def rollback_db_transaction() end
95
95
 
96
- # Alias for #add_limit_offset!.
96
+ # Alias for <tt>add_limit_offset!</tt>.
97
97
  def add_limit!(sql, options)
98
98
  add_limit_offset!(sql, options) if options
99
99
  end
@@ -44,6 +44,12 @@ module ActiveRecord
44
44
  @query_cache_enabled = old
45
45
  end
46
46
 
47
+ # Clears the query cache.
48
+ #
49
+ # One reason you may wish to call this method explicitly is between queries
50
+ # that ask the database to randomize results. Otherwise the cache would see
51
+ # the same SQL query and repeatedly return the same result each time, silently
52
+ # undermining the randomness you were expecting.
47
53
  def clear_query_cache
48
54
  @query_cache.clear if @query_cache
49
55
  end
@@ -16,13 +16,13 @@ module ActiveRecord
16
16
 
17
17
  # Instantiates a new column in the table.
18
18
  #
19
- # +name+ is the column's name, as in <tt><b>supplier_id</b> int(11)</tt>.
20
- # +default+ is the type-casted default value, such as <tt>sales_stage varchar(20) default <b>'new'</b></tt>.
21
- # +sql_type+ is only used to extract the column's length, if necessary. For example, <tt>company_name varchar(<b>60</b>)</tt>.
19
+ # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
20
+ # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
21
+ # +sql_type+ is only used to extract the column's length, if necessary. For example +60+ in <tt>company_name varchar(60)</tt>.
22
22
  # +null+ determines if this column allows +NULL+ values.
23
23
  def initialize(name, default, sql_type = nil, null = true)
24
24
  @name, @sql_type, @null = name, sql_type, null
25
- @limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
25
+ @limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
26
26
  @type = simplified_type(sql_type)
27
27
  @default = extract_default(default)
28
28
 
@@ -92,7 +92,7 @@ module ActiveRecord
92
92
  # Returns the human name of the column name.
93
93
  #
94
94
  # ===== Examples
95
- # Column.new('sales_stage', ...).human_name #=> 'Sales stage'
95
+ # Column.new('sales_stage', ...).human_name # => 'Sales stage'
96
96
  def human_name
97
97
  Base.human_attribute_name(@name)
98
98
  end
@@ -144,7 +144,10 @@ module ActiveRecord
144
144
 
145
145
  # convert something to a BigDecimal
146
146
  def value_to_decimal(value)
147
- if value.is_a?(BigDecimal)
147
+ # Using .class is faster than .is_a? and
148
+ # subclasses of BigDecimal will be handled
149
+ # in the else clause
150
+ if value.class == BigDecimal
148
151
  value
149
152
  elsif value.respond_to?(:to_d)
150
153
  value.to_d
@@ -170,11 +173,7 @@ module ActiveRecord
170
173
  # Treat 0000-00-00 00:00:00 as nil.
171
174
  return nil if year.nil? || year == 0
172
175
 
173
- Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec)
174
- # Over/underflow to DateTime
175
- rescue ArgumentError, TypeError
176
- zone_offset = Base.default_timezone == :local ? DateTime.local_offset : 0
177
- DateTime.civil(year, mon, mday, hour, min, sec, zone_offset) rescue nil
176
+ Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
178
177
  end
179
178
 
180
179
  def fast_string_to_date(string)
@@ -192,7 +191,7 @@ module ActiveRecord
192
191
  end
193
192
 
194
193
  def fallback_string_to_date(string)
195
- new_date(*ParseDate.parsedate(string)[0..2])
194
+ new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
196
195
  end
197
196
 
198
197
  def fallback_string_to_time(string)
@@ -251,11 +250,11 @@ module ActiveRecord
251
250
  end
252
251
 
253
252
  class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
254
-
253
+
255
254
  def sql_type
256
255
  base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
257
256
  end
258
-
257
+
259
258
  def to_sql
260
259
  column_sql = "#{base.quote_column_name(name)} #{sql_type}"
261
260
  add_column_options!(column_sql, :null => null, :default => default) unless type.to_sym == :primary_key
@@ -271,7 +270,7 @@ module ActiveRecord
271
270
  end
272
271
 
273
272
  # Represents a SQL table in an abstract way.
274
- # Columns are stored as a ColumnDefinition in the #columns attribute.
273
+ # Columns are stored as a ColumnDefinition in the +columns+ attribute.
275
274
  class TableDefinition
276
275
  attr_accessor :columns
277
276
 
@@ -310,39 +309,39 @@ module ActiveRecord
310
309
  # * <tt>:default</tt> -
311
310
  # The column's default value. Use nil for NULL.
312
311
  # * <tt>:null</tt> -
313
- # Allows or disallows +NULL+ values in the column. This option could
312
+ # Allows or disallows +NULL+ values in the column. This option could
314
313
  # have been named <tt>:null_allowed</tt>.
315
314
  # * <tt>:precision</tt> -
316
- # Specifies the precision for a <tt>:decimal</tt> column.
315
+ # Specifies the precision for a <tt>:decimal</tt> column.
317
316
  # * <tt>:scale</tt> -
318
- # Specifies the scale for a <tt>:decimal</tt> column.
317
+ # Specifies the scale for a <tt>:decimal</tt> column.
319
318
  #
320
319
  # Please be aware of different RDBMS implementations behavior with
321
320
  # <tt>:decimal</tt> columns:
322
321
  # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
323
322
  # <tt>:precision</tt>, and makes no comments about the requirements of
324
323
  # <tt>:precision</tt>.
325
- # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
324
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
326
325
  # Default is (10,0).
327
- # * PostgreSQL: <tt>:precision</tt> [1..infinity],
326
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
328
327
  # <tt>:scale</tt> [0..infinity]. No default.
329
- # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
328
+ # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
330
329
  # Internal storage as strings. No default.
331
330
  # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
332
331
  # but the maximum supported <tt>:precision</tt> is 16. No default.
333
- # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
332
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
334
333
  # Default is (38,0).
335
- # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
334
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
336
335
  # Default unknown.
337
- # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
336
+ # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
338
337
  # Default (9,0). Internal types NUMERIC and DECIMAL have different
339
338
  # storage rules, decimal being better.
340
- # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
339
+ # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
341
340
  # Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
342
341
  # NUMERIC is 19, and DECIMAL is 38.
343
- # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
342
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
344
343
  # Default (38,0).
345
- # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
344
+ # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
346
345
  # Default (38,0).
347
346
  # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
348
347
  #
@@ -351,28 +350,28 @@ module ActiveRecord
351
350
  # == Examples
352
351
  # # Assuming td is an instance of TableDefinition
353
352
  # td.column(:granted, :boolean)
354
- # #=> granted BOOLEAN
353
+ # # granted BOOLEAN
355
354
  #
356
355
  # td.column(:picture, :binary, :limit => 2.megabytes)
357
- # #=> picture BLOB(2097152)
356
+ # # => picture BLOB(2097152)
358
357
  #
359
358
  # td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
360
- # #=> sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
359
+ # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
361
360
  #
362
- # def.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
363
- # #=> bill_gates_money DECIMAL(15,2)
361
+ # td.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
362
+ # # => bill_gates_money DECIMAL(15,2)
364
363
  #
365
- # def.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
366
- # #=> sensor_reading DECIMAL(30,20)
364
+ # td.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
365
+ # # => sensor_reading DECIMAL(30,20)
367
366
  #
368
367
  # # While <tt>:scale</tt> defaults to zero on most databases, it
369
368
  # # probably wouldn't hurt to include it.
370
- # def.column(:huge_integer, :decimal, :precision => 30)
371
- # #=> huge_integer DECIMAL(30)
369
+ # td.column(:huge_integer, :decimal, :precision => 30)
370
+ # # => huge_integer DECIMAL(30)
372
371
  #
373
372
  # == Short-hand examples
374
373
  #
375
- # Instead of calling column directly, you can also work with the short-hand definitions for the default types.
374
+ # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
376
375
  # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
377
376
  # in a single statement.
378
377
  #
@@ -395,12 +394,12 @@ module ActiveRecord
395
394
  # t.timestamps
396
395
  # end
397
396
  #
398
- # There's a short-hand method for each of the type values declared at the top. And then there's
399
- # TableDefinition#timestamps that'll add created_at and updated_at as datetimes.
397
+ # There's a short-hand method for each of the type values declared at the top. And then there's
398
+ # TableDefinition#timestamps that'll add created_at and +updated_at+ as datetimes.
400
399
  #
401
400
  # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
402
- # column if the :polymorphic option is supplied. If :polymorphic is a hash of options, these will be
403
- # used when creating the _type column. So what can be written like this:
401
+ # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of options, these will be
402
+ # used when creating the <tt>_type</tt> column. So what can be written like this:
404
403
  #
405
404
  # create_table :taggings do |t|
406
405
  # t.integer :tag_id, :tagger_id, :taggable_id
@@ -435,13 +434,13 @@ module ActiveRecord
435
434
  def #{column_type}(*args)
436
435
  options = args.extract_options!
437
436
  column_names = args
438
-
437
+
439
438
  column_names.each { |name| column(name, '#{column_type}', options) }
440
439
  end
441
440
  EOV
442
441
  end
443
-
444
- # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
442
+
443
+ # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
445
444
  # <tt>:updated_at</tt> to the table.
446
445
  def timestamps
447
446
  column(:created_at, :datetime)
@@ -459,7 +458,7 @@ module ActiveRecord
459
458
  alias :belongs_to :references
460
459
 
461
460
  # Returns a String whose contents are the column definitions
462
- # concatenated together. This string can then be prepended and appended to
461
+ # concatenated together. This string can then be prepended and appended to
463
462
  # to generate the final SQL to create the table.
464
463
  def to_sql
465
464
  @columns * ', '
@@ -470,5 +469,193 @@ module ActiveRecord
470
469
  @base.native_database_types
471
470
  end
472
471
  end
472
+
473
+ # Represents a SQL table in an abstract way for updating a table.
474
+ # Also see TableDefinition and SchemaStatements#create_table
475
+ #
476
+ # Available transformations are:
477
+ #
478
+ # change_table :table do |t|
479
+ # t.column
480
+ # t.index
481
+ # t.timestamps
482
+ # t.change
483
+ # t.change_default
484
+ # t.rename
485
+ # t.references
486
+ # t.belongs_to
487
+ # t.string
488
+ # t.text
489
+ # t.integer
490
+ # t.float
491
+ # t.decimal
492
+ # t.datetime
493
+ # t.timestamp
494
+ # t.time
495
+ # t.date
496
+ # t.binary
497
+ # t.boolean
498
+ # t.remove
499
+ # t.remove_references
500
+ # t.remove_belongs_to
501
+ # t.remove_index
502
+ # t.remove_timestamps
503
+ # end
504
+ #
505
+ class Table
506
+ def initialize(table_name, base)
507
+ @table_name = table_name
508
+ @base = base
509
+ end
510
+
511
+ # Adds a new column to the named table.
512
+ # See TableDefinition#column for details of the options you can use.
513
+ # ===== Example
514
+ # ====== Creating a simple column
515
+ # t.column(:name, :string)
516
+ def column(column_name, type, options = {})
517
+ @base.add_column(@table_name, column_name, type, options)
518
+ end
519
+
520
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
521
+ # an Array of Symbols. See SchemaStatements#add_index
522
+ #
523
+ # ===== Examples
524
+ # ====== Creating a simple index
525
+ # t.index(:name)
526
+ # ====== Creating a unique index
527
+ # t.index([:branch_id, :party_id], :unique => true)
528
+ # ====== Creating a named index
529
+ # t.index([:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
530
+ def index(column_name, options = {})
531
+ @base.add_index(@table_name, column_name, options)
532
+ end
533
+
534
+ # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps
535
+ # ===== Example
536
+ # t.timestamps
537
+ def timestamps
538
+ @base.add_timestamps(@table_name)
539
+ end
540
+
541
+ # Changes the column's definition according to the new options.
542
+ # See TableDefinition#column for details of the options you can use.
543
+ # ===== Examples
544
+ # t.change(:name, :string, :limit => 80)
545
+ # t.change(:description, :text)
546
+ def change(column_name, type, options = {})
547
+ @base.change_column(@table_name, column_name, type, options)
548
+ end
549
+
550
+ # Sets a new default value for a column. See SchemaStatements#change_column_default
551
+ # ===== Examples
552
+ # t.change_default(:qualification, 'new')
553
+ # t.change_default(:authorized, 1)
554
+ def change_default(column_name, default)
555
+ @base.change_column_default(@table_name, column_name, default)
556
+ end
557
+
558
+ # Removes the column(s) from the table definition.
559
+ # ===== Examples
560
+ # t.remove(:qualification)
561
+ # t.remove(:qualification, :experience)
562
+ def remove(*column_names)
563
+ @base.remove_column(@table_name, column_names)
564
+ end
565
+
566
+ # Removes the given index from the table.
567
+ #
568
+ # ===== Examples
569
+ # ====== Remove the suppliers_name_index in the suppliers table
570
+ # t.remove_index :name
571
+ # ====== Remove the index named accounts_branch_id_index in the accounts table
572
+ # t.remove_index :column => :branch_id
573
+ # ====== Remove the index named accounts_branch_id_party_id_index in the accounts table
574
+ # t.remove_index :column => [:branch_id, :party_id]
575
+ # ====== Remove the index named by_branch_party in the accounts table
576
+ # t.remove_index :name => :by_branch_party
577
+ def remove_index(options = {})
578
+ @base.remove_index(@table_name, options)
579
+ end
580
+
581
+ # Removes the timestamp columns (created_at and updated_at) from the table.
582
+ # ===== Example
583
+ # t.remove_timestamps
584
+ def remove_timestamps
585
+ @base.remove_timestamps(@table_name)
586
+ end
587
+
588
+ # Renames a column.
589
+ # ===== Example
590
+ # t.rename(:description, :name)
591
+ def rename(column_name, new_column_name)
592
+ @base.rename_column(@table_name, column_name, new_column_name)
593
+ end
594
+
595
+ # Adds a reference. Optionally adds a +type+ column.
596
+ # <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
597
+ # ===== Examples
598
+ # t.references(:goat)
599
+ # t.references(:goat, :polymorphic => true)
600
+ # t.belongs_to(:goat)
601
+ def references(*args)
602
+ options = args.extract_options!
603
+ polymorphic = options.delete(:polymorphic)
604
+ args.each do |col|
605
+ @base.add_column(@table_name, "#{col}_id", :integer, options)
606
+ @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
607
+ end
608
+ end
609
+ alias :belongs_to :references
610
+
611
+ # Removes a reference. Optionally removes a +type+ column.
612
+ # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
613
+ # ===== Examples
614
+ # t.remove_references(:goat)
615
+ # t.remove_references(:goat, :polymorphic => true)
616
+ # t.remove_belongs_to(:goat)
617
+ def remove_references(*args)
618
+ options = args.extract_options!
619
+ polymorphic = options.delete(:polymorphic)
620
+ args.each do |col|
621
+ @base.remove_column(@table_name, "#{col}_id")
622
+ @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
623
+ end
624
+ end
625
+ alias :remove_belongs_to :remove_references
626
+
627
+ # Adds a column or columns of a specified type
628
+ # ===== Examples
629
+ # t.string(:goat)
630
+ # t.string(:goat, :sheep)
631
+ %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
632
+ class_eval <<-EOV
633
+ def #{column_type}(*args)
634
+ options = args.extract_options!
635
+ column_names = args
636
+
637
+ column_names.each do |name|
638
+ column = ColumnDefinition.new(@base, name, '#{column_type}')
639
+ if options[:limit]
640
+ column.limit = options[:limit]
641
+ elsif native['#{column_type}'.to_sym].is_a?(Hash)
642
+ column.limit = native['#{column_type}'.to_sym][:limit]
643
+ end
644
+ column.precision = options[:precision]
645
+ column.scale = options[:scale]
646
+ column.default = options[:default]
647
+ column.null = options[:null]
648
+ @base.add_column(@table_name, name, column.sql_type, options)
649
+ end
650
+ end
651
+ EOV
652
+ end
653
+
654
+ private
655
+ def native
656
+ @base.native_database_types
657
+ end
658
+ end
659
+
473
660
  end
474
661
  end