railties 3.0.20 → 3.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. data/CHANGELOG +36 -49
  2. data/README.rdoc +2 -1
  3. data/guides/assets/stylesheets/fixes.css +16 -0
  4. data/guides/rails_guides.rb +2 -2
  5. data/guides/rails_guides/generator.rb +8 -3
  6. data/guides/rails_guides/textile_extensions.rb +4 -2
  7. data/guides/source/2_2_release_notes.textile +3 -3
  8. data/guides/source/2_3_release_notes.textile +2 -2
  9. data/guides/source/3_0_release_notes.textile +14 -14
  10. data/guides/source/action_controller_overview.textile +54 -79
  11. data/guides/source/action_mailer_basics.textile +39 -9
  12. data/guides/source/action_view_overview.textile +257 -211
  13. data/guides/source/active_record_basics.textile +1 -1
  14. data/guides/source/active_record_querying.textile +217 -27
  15. data/guides/source/active_record_validations_callbacks.textile +94 -25
  16. data/guides/source/active_support_core_extensions.textile +109 -77
  17. data/guides/source/ajax_on_rails.textile +15 -150
  18. data/guides/source/api_documentation_guidelines.textile +12 -12
  19. data/guides/source/association_basics.textile +74 -60
  20. data/guides/source/caching_with_rails.textile +59 -60
  21. data/guides/source/command_line.textile +46 -47
  22. data/guides/source/configuring.textile +55 -37
  23. data/guides/source/contribute.textile +7 -7
  24. data/guides/source/contributing_to_ruby_on_rails.textile +14 -23
  25. data/guides/source/credits.html.erb +3 -3
  26. data/guides/source/debugging_rails_applications.textile +59 -46
  27. data/guides/source/form_helpers.textile +76 -31
  28. data/guides/source/generators.textile +39 -40
  29. data/guides/source/getting_started.textile +73 -94
  30. data/guides/source/i18n.textile +64 -58
  31. data/guides/source/index.html.erb +3 -3
  32. data/guides/source/initialization.textile +634 -3284
  33. data/guides/source/layout.html.erb +6 -7
  34. data/guides/source/layouts_and_rendering.textile +59 -60
  35. data/guides/source/migrations.textile +63 -59
  36. data/guides/source/nested_model_forms.textile +2 -2
  37. data/guides/source/performance_testing.textile +16 -16
  38. data/guides/source/plugins.textile +236 -1280
  39. data/guides/source/rails_application_templates.textile +37 -29
  40. data/guides/source/rails_on_rack.textile +4 -9
  41. data/guides/source/routing.textile +96 -75
  42. data/guides/source/ruby_on_rails_guides_guidelines.textile +19 -12
  43. data/guides/source/security.textile +57 -30
  44. data/guides/source/testing.textile +26 -24
  45. data/guides/w3c_validator.rb +2 -2
  46. data/lib/rails.rb +1 -7
  47. data/lib/rails/application.rb +46 -76
  48. data/lib/rails/application/bootstrap.rb +6 -11
  49. data/lib/rails/application/configuration.rb +43 -40
  50. data/lib/rails/application/finisher.rb +16 -4
  51. data/lib/rails/application/railties.rb +6 -24
  52. data/lib/rails/application/routes_reloader.rb +45 -0
  53. data/lib/rails/backtrace_cleaner.rb +1 -1
  54. data/lib/rails/cli.rb +7 -5
  55. data/lib/rails/commands.rb +27 -2
  56. data/lib/rails/commands/application.rb +14 -1
  57. data/lib/rails/commands/benchmarker.rb +3 -1
  58. data/lib/rails/commands/dbconsole.rb +2 -2
  59. data/lib/rails/commands/destroy.rb +3 -1
  60. data/lib/rails/commands/generate.rb +3 -1
  61. data/lib/rails/commands/plugin.rb +2 -7
  62. data/lib/rails/commands/plugin_new.rb +10 -0
  63. data/lib/rails/commands/profiler.rb +3 -1
  64. data/lib/rails/commands/server.rb +4 -0
  65. data/lib/rails/configuration.rb +8 -81
  66. data/lib/rails/console/app.rb +2 -2
  67. data/lib/rails/engine.rb +460 -78
  68. data/lib/rails/engine/configuration.rb +46 -49
  69. data/lib/rails/engine/railties.rb +33 -0
  70. data/lib/rails/generators.rb +11 -5
  71. data/lib/rails/generators/actions.rb +2 -27
  72. data/lib/rails/generators/app_base.rb +216 -0
  73. data/lib/rails/generators/base.rb +3 -2
  74. data/lib/rails/generators/erb/scaffold/templates/index.html.erb +1 -1
  75. data/lib/rails/generators/generated_attribute.rb +2 -1
  76. data/lib/rails/generators/migration.rb +6 -2
  77. data/lib/rails/generators/named_base.rb +79 -3
  78. data/lib/rails/generators/rails/app/app_generator.rb +44 -209
  79. data/lib/rails/generators/rails/app/templates/Gemfile +15 -31
  80. data/lib/rails/generators/rails/app/templates/README +2 -2
  81. data/lib/rails/generators/rails/app/templates/Rakefile +1 -1
  82. data/lib/rails/generators/rails/app/templates/{public → app/assets}/images/rails.png +0 -0
  83. data/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +8 -0
  84. data/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css +5 -0
  85. data/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory +0 -0
  86. data/lib/rails/generators/rails/app/templates/app/models/.empty_directory +0 -0
  87. data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +4 -4
  88. data/lib/rails/generators/rails/app/templates/config/application.rb +19 -3
  89. data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml +4 -4
  90. data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml +11 -6
  91. data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml +3 -3
  92. data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml +1 -1
  93. data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +1 -2
  94. data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +14 -11
  95. data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +5 -1
  96. data/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt +1 -1
  97. data/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt +12 -0
  98. data/lib/rails/generators/rails/app/templates/config/locales/en.yml +1 -1
  99. data/lib/rails/generators/rails/app/templates/config/routes.rb +1 -1
  100. data/lib/rails/generators/rails/app/templates/db/{seeds.rb → seeds.rb.tt} +2 -2
  101. data/lib/rails/generators/rails/app/templates/public/index.html +10 -8
  102. data/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory +0 -0
  103. data/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory +0 -0
  104. data/lib/rails/generators/rails/app/templates/test/functional/.empty_directory +0 -0
  105. data/lib/rails/generators/rails/app/templates/test/integration/.empty_directory +0 -0
  106. data/lib/rails/generators/rails/app/templates/test/{test_helper.rb.tt → test_helper.rb} +0 -0
  107. data/lib/rails/generators/rails/app/templates/test/unit/.empty_directory +0 -0
  108. data/lib/rails/generators/rails/assets/USAGE +20 -0
  109. data/lib/rails/generators/rails/assets/assets_generator.rb +39 -0
  110. data/lib/rails/generators/rails/assets/templates/javascript.js +2 -0
  111. data/lib/rails/generators/rails/assets/templates/javascript.js.coffee +3 -0
  112. data/lib/rails/generators/rails/assets/templates/stylesheet.css +4 -0
  113. data/lib/rails/generators/rails/assets/templates/stylesheet.css.scss +5 -0
  114. data/lib/rails/generators/rails/controller/controller_generator.rb +1 -1
  115. data/lib/rails/generators/rails/controller/templates/controller.rb +2 -0
  116. data/lib/rails/generators/rails/generator/generator_generator.rb +2 -2
  117. data/lib/rails/generators/rails/generator/templates/templates/.empty_directory +0 -0
  118. data/lib/rails/generators/rails/helper/templates/helper.rb +2 -0
  119. data/lib/rails/generators/rails/plugin/plugin_generator.rb +7 -0
  120. data/lib/rails/generators/rails/plugin/templates/Rakefile.tt +4 -4
  121. data/lib/rails/generators/rails/plugin_new/USAGE +10 -0
  122. data/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +303 -0
  123. data/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec +9 -0
  124. data/lib/rails/generators/rails/plugin_new/templates/Gemfile +11 -0
  125. data/lib/rails/generators/rails/plugin_new/templates/MIT-LICENSE +20 -0
  126. data/lib/rails/generators/rails/plugin_new/templates/README.rdoc +3 -0
  127. data/lib/rails/generators/rails/plugin_new/templates/Rakefile +21 -0
  128. data/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt +4 -0
  129. data/lib/rails/generators/rails/plugin_new/templates/app/helpers/%name%/application_helper.rb.tt +4 -0
  130. data/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory +0 -0
  131. data/lib/rails/generators/rails/plugin_new/templates/config/routes.rb +6 -0
  132. data/lib/rails/generators/rails/plugin_new/templates/gitignore +6 -0
  133. data/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb +6 -0
  134. data/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb +7 -0
  135. data/lib/rails/generators/rails/plugin_new/templates/lib/tasks/%name%_tasks.rake +4 -0
  136. data/lib/rails/generators/rails/plugin_new/templates/rails/application.rb +16 -0
  137. data/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb +10 -0
  138. data/lib/rails/generators/rails/plugin_new/templates/rails/routes.rb +4 -0
  139. data/lib/rails/generators/rails/plugin_new/templates/script/rails.tt +5 -0
  140. data/lib/rails/generators/rails/plugin_new/templates/test/%name%_test.rb +7 -0
  141. data/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb +12 -0
  142. data/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb +10 -0
  143. data/lib/rails/generators/rails/resource/resource_generator.rb +2 -2
  144. data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +20 -1
  145. data/lib/rails/generators/rails/{stylesheets → scaffold}/templates/scaffold.css +0 -0
  146. data/lib/rails/generators/rails/scaffold/templates/scaffold.css.scss +58 -0
  147. data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +21 -19
  148. data/lib/rails/generators/resource_helpers.rb +3 -3
  149. data/lib/rails/generators/test_case.rb +2 -20
  150. data/lib/rails/generators/test_unit/controller/templates/functional_test.rb +5 -4
  151. data/lib/rails/generators/test_unit/helper/templates/helper_test.rb +2 -0
  152. data/lib/rails/generators/test_unit/integration/templates/integration_test.rb +3 -4
  153. data/lib/rails/generators/test_unit/mailer/templates/functional_test.rb +5 -4
  154. data/lib/rails/generators/test_unit/model/templates/fixtures.yml +1 -1
  155. data/lib/rails/generators/test_unit/model/templates/unit_test.rb +5 -4
  156. data/lib/rails/generators/test_unit/observer/templates/unit_test.rb +5 -4
  157. data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +3 -4
  158. data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +7 -5
  159. data/lib/rails/info.rb +0 -1
  160. data/lib/rails/paths.rb +119 -65
  161. data/lib/rails/plugin.rb +18 -19
  162. data/lib/rails/rack/log_tailer.rb +1 -1
  163. data/lib/rails/railtie.rb +50 -47
  164. data/lib/rails/railtie/configurable.rb +20 -10
  165. data/lib/rails/railtie/configuration.rb +20 -19
  166. data/lib/rails/source_annotation_extractor.rb +5 -5
  167. data/lib/rails/tasks.rb +1 -0
  168. data/lib/rails/tasks/assets.rake +10 -0
  169. data/lib/rails/tasks/documentation.rake +2 -8
  170. data/lib/rails/tasks/engine.rake +69 -0
  171. data/lib/rails/tasks/framework.rake +4 -21
  172. data/lib/rails/tasks/misc.rake +1 -1
  173. data/lib/rails/tasks/routes.rake +2 -1
  174. data/lib/rails/test_help.rb +17 -1
  175. data/lib/rails/test_unit/railtie.rb +1 -1
  176. data/lib/rails/test_unit/testing.rake +8 -3
  177. data/lib/rails/version.rb +3 -3
  178. metadata +128 -100
  179. checksums.yaml +0 -7
  180. data/lib/rails/application/configurable.rb +0 -19
  181. data/lib/rails/console/sandbox.rb +0 -6
  182. data/lib/rails/deprecation.rb +0 -41
  183. data/lib/rails/engine/configurable.rb +0 -25
  184. data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml +0 -62
  185. data/lib/rails/generators/rails/app/templates/public/javascripts/application.js +0 -2
  186. data/lib/rails/generators/rails/app/templates/public/javascripts/controls.js +0 -965
  187. data/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js +0 -974
  188. data/lib/rails/generators/rails/app/templates/public/javascripts/effects.js +0 -1123
  189. data/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js +0 -6001
  190. data/lib/rails/generators/rails/app/templates/public/javascripts/rails.js +0 -202
  191. data/lib/rails/generators/rails/stylesheets/USAGE +0 -5
  192. data/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb +0 -9
  193. data/lib/rails/info_routes.rb +0 -3
@@ -180,7 +180,7 @@ Active Record provides a rich API for accessing data within a database. Below ar
180
180
 
181
181
  <ruby>
182
182
  # find all users named David who are Code Artists and sort by created_at in reverse chronological order
183
- users = User.all(:conditions => { :name => 'David', :occupation => 'Code Artist'}, :order => 'created_at DESC')
183
+ users = User.where(:name => 'David', :occupation => 'Code Artist').order('created_at DESC')
184
184
  </ruby>
185
185
 
186
186
  You can learn more about querying an Active Record model in the "Active Record Query Interface":"active_record_querying.html" guide.
@@ -19,8 +19,6 @@ Code examples throughout this guide will refer to one or more of the following m
19
19
 
20
20
  TIP: All of the following models use +id+ as the primary key, unless specified otherwise.
21
21
 
22
- <br />
23
-
24
22
  <ruby>
25
23
  class Client < ActiveRecord::Base
26
24
  has_one :address
@@ -58,6 +56,7 @@ The methods are:
58
56
  * +select+
59
57
  * +group+
60
58
  * +order+
59
+ * +reorder+
61
60
  * +limit+
62
61
  * +offset+
63
62
  * +joins+
@@ -78,7 +77,7 @@ Primary operation of <tt>Model.find(options)</tt> can be summarized as:
78
77
 
79
78
  h4. Retrieving a Single Object
80
79
 
81
- Active Record lets you retrieve a single object using three different ways.
80
+ Active Record lets you retrieve a single object using five different ways.
82
81
 
83
82
  h5. Using a Primary Key
84
83
 
@@ -132,6 +131,40 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
132
131
 
133
132
  <tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised.
134
133
 
134
+ h5. +first!+
135
+
136
+ <tt>Model.first!</tt> finds the first record. For example:
137
+
138
+ <ruby>
139
+ client = Client.first!
140
+ => #<Client id: 1, first_name: "Lifo">
141
+ </ruby>
142
+
143
+ SQL equivalent of the above is:
144
+
145
+ <sql>
146
+ SELECT * FROM clients LIMIT 1
147
+ </sql>
148
+
149
+ <tt>Model.first!</tt> raises +RecordNotFound+ if no matching record is found.
150
+
151
+ h5. +last!+
152
+
153
+ <tt>Model.last!</tt> finds the last record. For example:
154
+
155
+ <ruby>
156
+ client = Client.last!
157
+ => #<Client id: 221, first_name: "Russel">
158
+ </ruby>
159
+
160
+ SQL equivalent of the above is:
161
+
162
+ <sql>
163
+ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
164
+ </sql>
165
+
166
+ <tt>Model.last!</tt> raises +RecordNotFound+ if no matching record is found.
167
+
135
168
  h4. Retrieving Multiple Objects
136
169
 
137
170
  h5. Using Multiple Primary Keys
@@ -150,7 +183,7 @@ SQL equivalent of the above is:
150
183
  SELECT * FROM clients WHERE (clients.id IN (1,10))
151
184
  </sql>
152
185
 
153
- <tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception unless a matching record is found for <strong>all</strong> of the supplied primary keys.
186
+ WARNING: <tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception unless a matching record is found for <strong>all</strong> of the supplied primary keys.
154
187
 
155
188
  h4. Retrieving Multiple Objects in Batches
156
189
 
@@ -337,7 +370,7 @@ This code will generate SQL like this:
337
370
  SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
338
371
  </sql>
339
372
 
340
- h4. Ordering
373
+ h3. Ordering
341
374
 
342
375
  To retrieve records from the database in a specific order, you can use the +order+ method.
343
376
 
@@ -361,7 +394,7 @@ Or ordering by multiple fields:
361
394
  Client.order("orders_count ASC, created_at DESC")
362
395
  </ruby>
363
396
 
364
- h4. Selecting Specific Fields
397
+ h3. Selecting Specific Fields
365
398
 
366
399
  By default, <tt>Model.find</tt> selects all the fields from the result set using +select *+.
367
400
 
@@ -386,7 +419,7 @@ SELECT viewable_by, locked FROM clients
386
419
  Be careful because this also means you're initializing a model object with only the fields that you've selected. If you attempt to access a field that is not in the initialized record you'll receive:
387
420
 
388
421
  <shell>
389
- ActiveRecord::MissingAttributeError: missing attribute: <attribute>
422
+ ActiveModel::MissingAttributeError: missing attribute: <attribute>
390
423
  </shell>
391
424
 
392
425
  Where +&lt;attribute&gt;+ is the attribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
@@ -397,7 +430,7 @@ You can also call SQL functions within the select option. For example, if you wo
397
430
  Client.select("DISTINCT(name)")
398
431
  </ruby>
399
432
 
400
- h4. Limit and Offset
433
+ h3. Limit and Offset
401
434
 
402
435
  To apply +LIMIT+ to the SQL fired by the +Model.find+, you can specify the +LIMIT+ using +limit+ and +offset+ methods on the relation.
403
436
 
@@ -422,10 +455,10 @@ Client.limit(5).offset(30)
422
455
  will return instead a maximum of 5 clients beginning with the 31st. The SQL looks like:
423
456
 
424
457
  <sql>
425
- SELECT * FROM clients LIMIT 5, 30
458
+ SELECT * FROM clients LIMIT 5 OFFSET 30
426
459
  </sql>
427
460
 
428
- h4. Group
461
+ h3. Group
429
462
 
430
463
  To apply a +GROUP BY+ clause to the SQL fired by the finder, you can specify the +group+ method on the find.
431
464
 
@@ -440,10 +473,10 @@ And this will give you a single +Order+ object for each date where there are ord
440
473
  The SQL that would be executed would be something like this:
441
474
 
442
475
  <sql>
443
- SELECT * FROM orders GROUP BY date(created_at)
476
+ SELECT * FROM orders GROUP BY date(created_at) ORDER BY created_at
444
477
  </sql>
445
478
 
446
- h4. Having
479
+ h3. Having
447
480
 
448
481
  SQL uses the +HAVING+ clause to specify conditions on the +GROUP BY+ fields. You can add the +HAVING+ clause to the SQL fired by the +Model.find+ by adding the +:having+ option to the find.
449
482
 
@@ -461,7 +494,63 @@ SELECT * FROM orders GROUP BY date(created_at) HAVING created_at > '2009-01-15'
461
494
 
462
495
  This will return single order objects for each day, but only for the last month.
463
496
 
464
- h4. Readonly Objects
497
+ h3. Overriding Conditions
498
+
499
+ h4. +except+
500
+
501
+ You can specify certain conditions to be excepted by using the +except+ method. For example:
502
+
503
+ <ruby>
504
+ Post.where('id > 10').limit(20).order('id asc').except(:order)
505
+ </ruby>
506
+
507
+ The SQL that would be executed:
508
+
509
+ <sql>
510
+ SELECT * FROM posts WHERE id > 10 LIMIT 20
511
+ </sql>
512
+
513
+ h4. +only+
514
+
515
+ You can also override conditions using the +only+ method. For example:
516
+
517
+ <ruby>
518
+ Post.where('id > 10').limit(20).order('id desc').only(:order, :where)
519
+ </ruby>
520
+
521
+ The SQL that would be executed:
522
+
523
+ <sql>
524
+ SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
525
+ </sql>
526
+
527
+ h4. +reorder+
528
+
529
+ The +reorder+ method overrides the default scope order. For example:
530
+
531
+ <ruby>
532
+ class Post < ActiveRecord::Base
533
+ ..
534
+ ..
535
+ has_many :comments, :order => 'posted_at DESC'
536
+ end
537
+
538
+ Post.find(10).comments.reorder('name')
539
+ </ruby>
540
+
541
+ The SQL that would be executed:
542
+
543
+ <sql>
544
+ SELECT * FROM posts WHERE id = 10 ORDER BY name
545
+ </sql>
546
+
547
+ In case the +reorder+ clause is not used, the SQL executed would be:
548
+
549
+ <sql>
550
+ SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC
551
+ </sql>
552
+
553
+ h3. Readonly Objects
465
554
 
466
555
  Active Record provides +readonly+ method on a relation to explicitly disallow modification or deletion of any of the returned object. Any attempt to alter or destroy a readonly record will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception.
467
556
 
@@ -471,9 +560,9 @@ client.visits += 1
471
560
  client.save
472
561
  </ruby>
473
562
 
474
- As +client+ is explicitly set to be a readonly object, the above code will raise an +ActiveRecord::ReadOnlyRecord+ exception when calling +client.save+ with an updated value of _visists_.
563
+ As +client+ is explicitly set to be a readonly object, the above code will raise an +ActiveRecord::ReadOnlyRecord+ exception when calling +client.save+ with an updated value of _visits_.
475
564
 
476
- h4. Locking Records for Update
565
+ h3. Locking Records for Update
477
566
 
478
567
  Locking is helpful for preventing race conditions when updating records in the database and ensuring atomic updates.
479
568
 
@@ -482,9 +571,9 @@ Active Record provides two locking mechanisms:
482
571
  * Optimistic Locking
483
572
  * Pessimistic Locking
484
573
 
485
- h5. Optimistic Locking
574
+ h4. Optimistic Locking
486
575
 
487
- Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of conflicts with the data. It does this by checking whether another process has made changes to a record since it was opened. An +ActiveRecord::StaleObjectError+ exception is thrown if that has occurred and the update is ignored.
576
+ Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of conflicts with the data. It does this by checking whether another process has made changes to a record since it was opened. An +ActiveRecord::StaleObjectError+ exception is thrown if that has occurred and the update is ignored.
488
577
 
489
578
  <strong>Optimistic locking column</strong>
490
579
 
@@ -517,7 +606,7 @@ class Client < ActiveRecord::Base
517
606
  end
518
607
  </ruby>
519
608
 
520
- h5. Pessimistic Locking
609
+ h4. Pessimistic Locking
521
610
 
522
611
  Pessimistic locking uses a locking mechanism provided by the underlying database. Using +lock+ when building a relation obtains an exclusive lock on the selected rows. Relations using +lock+ are usually wrapped inside a transaction for preventing deadlock conditions.
523
612
 
@@ -569,9 +658,7 @@ SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id =
569
658
 
570
659
  h4. Using Array/Hash of Named Associations
571
660
 
572
- WARNING: This method only works with +INNER JOIN+,
573
-
574
- <br />
661
+ WARNING: This method only works with +INNER JOIN+.
575
662
 
576
663
  Active Record lets you use the names of the "associations":association_basics.html defined on the model as a shortcut for specifying +JOIN+ clause for those associations when using the +joins+ method.
577
664
 
@@ -666,7 +753,7 @@ Eager loading is the mechanism for loading the associated records of the objects
666
753
  Consider the following code, which finds 10 clients and prints their postcodes:
667
754
 
668
755
  <ruby>
669
- clients = Client.all(:limit => 10)
756
+ clients = Client.limit(10)
670
757
 
671
758
  clients.each do |client|
672
759
  puts client.address.postcode
@@ -721,15 +808,117 @@ h4. Specifying Conditions on Eager Loaded Associations
721
808
 
722
809
  Even though Active Record lets you specify conditions on the eager loaded associations just like +joins+, the recommended way is to use "joins":#joining-tables instead.
723
810
 
811
+ However if you must do this, you may use +where+ as you would normally.
812
+
813
+ <ruby>
814
+ Post.includes(:comments).where("comments.visible", true)
815
+ </ruby>
816
+
817
+ This would generate a query which contains a +LEFT OUTER JOIN+ whereas the +joins+ method would generate one using the +INNER JOIN+ function instead.
818
+
819
+ <ruby>
820
+ SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible = 1)
821
+ </ruby>
822
+
823
+ If there was no +where+ condition, this would generate the normal set of two queries.
824
+
825
+ If, in the case of this +includes+ query, there were no comments for any posts, all the posts would still be loaded. By using +joins+ (an INNER JOIN), the join conditions *must* match, otherwise no records will be returned.
826
+
827
+ h3. Scopes
828
+
829
+ Scoping allows you to specify commonly-used ARel queries which can be referenced as method calls on the association objects or models. With these scopes, you can use every method previously covered such as +where+, +joins+ and +includes+. All scope methods will return an +ActiveRecord::Relation+ object which will allow for further methods (such as other scopes) to be called on it.
830
+
831
+ To define a simple scope, we use the +scope+ method inside the class, passing the ARel query that we'd like run when this scope is called:
832
+
833
+ <ruby>
834
+ class Post < ActiveRecord::Base
835
+ scope :published, where(:published => true)
836
+ end
837
+ </ruby>
838
+
839
+ Just like before, these methods are also chainable:
840
+
841
+ <ruby>
842
+ class Post < ActiveRecord::Base
843
+ scope :published, where(:published => true).joins(:category)
844
+ end
845
+ </ruby>
846
+
847
+ Scopes are also chainable within scopes:
848
+
849
+ <ruby>
850
+ class Post < ActiveRecord::Base
851
+ scope :published, where(:published => true)
852
+ scope :published_and_commented, published.and(self.arel_table[:comments_count].gt(0))
853
+ end
854
+ </ruby>
855
+
856
+ To call this +published+ scope we can call it on either the class:
857
+
858
+ <ruby>
859
+ Post.published => [published posts]
860
+ </ruby>
861
+
862
+ Or on an association consisting of +Post+ objects:
863
+
864
+ <ruby>
865
+ category = Category.first
866
+ category.posts.published => [published posts belonging to this category]
867
+ </ruby>
868
+
869
+ h4. Working with times
870
+
871
+ If you're working with dates or times within scopes, due to how they are evaluated, you will need to use a lambda so that the scope is evaluated every time.
872
+
873
+ <ruby>
874
+ class Post < ActiveRecord::Base
875
+ scope :last_week, lambda { where("created_at < ?", Time.zone.now ) }
876
+ end
877
+ </ruby>
878
+
879
+ Without the +lambda+, this +Time.zone.now+ will only be called once.
880
+
881
+ h4. Passing in arguments
882
+
883
+ When a +lambda+ is used for a +scope+, it can take arguments:
884
+
885
+ <ruby>
886
+ class Post < ActiveRecord::Base
887
+ scope :1_week_before, lambda { |time| where("created_at < ?", time)
888
+ end
889
+ </ruby>
890
+
891
+ This may then be called using this:
892
+
893
+ <ruby>
894
+ Post.1_week_before(Time.zone.now)
895
+ </ruby>
896
+
897
+ However, this is just duplicating the functionality that would be provided to you by a class method.
898
+
899
+ <ruby>
900
+ class Post < ActiveRecord::Base
901
+ def self.1_week_before(time)
902
+ where("created_at < ?", time)
903
+ end
904
+ end
905
+ </ruby>
906
+
907
+ Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible on the association objects:
908
+
909
+ <ruby>
910
+ category.posts.1_week_before(time)
911
+ </ruby>
912
+
724
913
  h3. Dynamic Finders
725
914
 
726
- For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +first_name+ on your +Client+ model for example, you get +find_by_first_name+ and +find_all_by_first_name+ for free from Active Record. If you have also have a +locked+ field on the +Client+ model, you also get +find_by_locked+ and +find_all_by_locked+.
915
+ For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +first_name+ on your +Client+ model for example, you get +find_by_first_name+ and +find_all_by_first_name+ for free from Active Record. If you have a +locked+ field on the +Client+ model, you also get +find_by_locked+ and +find_all_by_locked+ methods.
727
916
 
728
- You can do +find_last_by_*+ methods too which will find the last record matching your argument.
917
+ You can also use +find_last_by_*+ methods which will find the last record matching your argument.
729
918
 
730
919
  You can specify an exclamation point (<tt>!</tt>) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+
731
920
 
732
- If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_first_name_and_locked("Ryan", true)+.
921
+ If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields. For example, +Client.find_by_first_name_and_locked("Ryan", true)+.
733
922
 
734
923
 
735
924
  There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_first_name(params[:first_name])+. Using this will first perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_first_name("Ryan")+:
@@ -831,7 +1020,7 @@ Client.count
831
1020
  # SELECT count(*) AS count_all FROM clients
832
1021
  </ruby>
833
1022
 
834
- Or on a relation :
1023
+ Or on a relation:
835
1024
 
836
1025
  <ruby>
837
1026
  Client.where(:first_name => 'Ryan').count
@@ -898,10 +1087,11 @@ If you want to find the sum of a field for all records in your table you can cal
898
1087
  Client.sum("orders_count")
899
1088
  </ruby>
900
1089
 
901
- For options, please see the parent section, "Calculations":#calculations.
1090
+ For options, please see the parent section, "Calculations":#calculations.
902
1091
 
903
1092
  h3. Changelog
904
1093
 
1094
+ * December 23 2010: Add documentation for the +scope+ method. "Ryan Bigg":http://ryanbigg.com
905
1095
  * April 7, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
906
1096
  * February 3, 2010: Update to Rails 3 by "James Miller":credits.html#bensie
907
1097
  * February 7, 2009: Second version by "Pratik":credits.html#lifo
@@ -84,6 +84,7 @@ The following methods skip validations, and will save the object to the database
84
84
  * +toggle!+
85
85
  * +update_all+
86
86
  * +update_attribute+
87
+ * +update_column+
87
88
  * +update_counters+
88
89
 
89
90
  Note that +save+ also has the ability to skip validations if passed +:validate => false+ as argument. This technique should be used with caution.
@@ -96,7 +97,7 @@ To verify whether or not an object is valid, Rails uses the +valid?+ method. You
96
97
 
97
98
  <ruby>
98
99
  class Person < ActiveRecord::Base
99
- validates_presence_of :name
100
+ validates :name, :presence => true
100
101
  end
101
102
 
102
103
  Person.create(:name => "John Doe").valid? # => true
@@ -109,7 +110,7 @@ Note that an object instantiated with +new+ will not report errors even if it's
109
110
 
110
111
  <ruby>
111
112
  class Person < ActiveRecord::Base
112
- validates_presence_of :name
113
+ validates :name, :presence => true
113
114
  end
114
115
 
115
116
  >> p = Person.new
@@ -147,7 +148,7 @@ This method is only useful _after_ validations have been run, because it only in
147
148
 
148
149
  <ruby>
149
150
  class Person < ActiveRecord::Base
150
- validates_presence_of :name
151
+ validates :name, :presence => true
151
152
  end
152
153
 
153
154
  >> Person.new.errors[:name].any? # => false
@@ -197,7 +198,7 @@ end
197
198
 
198
199
  This validation will work with all of the association types.
199
200
 
200
- CAUTION: Don't use +validates_associated+ on both ends of your associations, they would call each other in an infinite loop.
201
+ CAUTION: Don't use +validates_associated+ on both ends of your associations. They would call each other in an infinite loop.
201
202
 
202
203
  The default error message for +validates_associated+ is "_is invalid_". Note that each associated object will contain its own +errors+ collection; errors do not bubble up to the calling model.
203
204
 
@@ -235,7 +236,7 @@ This helper validates that the attributes' values are not included in a given se
235
236
 
236
237
  <ruby>
237
238
  class Account < ActiveRecord::Base
238
- validates_exclusion_of :subdomain, :in => %w(www),
239
+ validates_exclusion_of :subdomain, :in => %w(www us ca jp),
239
240
  :message => "Subdomain %{value} is reserved."
240
241
  end
241
242
  </ruby>
@@ -314,6 +315,8 @@ class Essay < ActiveRecord::Base
314
315
  end
315
316
  </ruby>
316
317
 
318
+ Note that the default error messages are plural (e.g., "is too short (minimum is %{count} characters)"). For this reason, when +:minimum+ is 1 you should provide a personalized message or use +validates_presence_of+ instead. When +:in+ or +:within+ have a lower limit of 1, you should either provide a personalized message or call +validates_presence_of+ prior to +validates_length_of+.
319
+
317
320
  The +validates_size_of+ helper is an alias for +validates_length_of+.
318
321
 
319
322
  h4. +validates_numericality_of+
@@ -355,7 +358,7 @@ This helper validates that the specified attributes are not empty. It uses the +
355
358
 
356
359
  <ruby>
357
360
  class Person < ActiveRecord::Base
358
- validates_presence_of :name, :login, :email
361
+ validates :name, :login, :email, :presence => true
359
362
  end
360
363
  </ruby>
361
364
 
@@ -415,7 +418,7 @@ class Person < ActiveRecord::Base
415
418
  end
416
419
 
417
420
  class GoodnessValidator < ActiveModel::Validator
418
- def validate
421
+ def validate(record)
419
422
  if record.first_name == "Evil"
420
423
  record.errors[:base] << "This person is evil"
421
424
  end
@@ -425,10 +428,7 @@ end
425
428
 
426
429
  The +validates_with+ helper takes a class, or a list of classes to use for validation. There is no default error message for +validates_with+. You must manually add errors to the record's errors collection in the validator class.
427
430
 
428
- The validator class has two attributes by default:
429
-
430
- * +record+ - the record to be validated
431
- * +options+ - the extra options that were passed to +validates_with+
431
+ To implement the validate method, you must have an +record+ parameter defined, which is the record to be validated.
432
432
 
433
433
  Like all other validations, +validates_with+ takes the +:if+, +:unless+ and +:on+ options. If you pass any other options, it will send those options to the validator class as +options+:
434
434
 
@@ -437,8 +437,8 @@ class Person < ActiveRecord::Base
437
437
  validates_with GoodnessValidator, :fields => [:first_name, :last_name]
438
438
  end
439
439
 
440
- class GoodnessValidator < ActiveRecord::Validator
441
- def validate
440
+ class GoodnessValidator < ActiveModel::Validator
441
+ def validate(record)
442
442
  if options[:fields].any?{|field| record.send(field) == "Evil" }
443
443
  record.errors[:base] << "This person is evil"
444
444
  end
@@ -462,11 +462,11 @@ The block receives the model, the attribute's name and the attribute's value. Yo
462
462
 
463
463
  h3. Common Validation Options
464
464
 
465
- There are some common options that all the validation helpers can use. Here they are, except for the +:if+ and +:unless+ options, which are discussed later in "Conditional Validation":#conditional-validation.
465
+ These are common validation options:
466
466
 
467
467
  h4. +:allow_nil+
468
468
 
469
- The +:allow_nil+ option skips the validation when the value being validated is +nil+. Using +:allow_nil+ with +validates_presence_of+ allows for +nil+, but any other +blank?+ value will still be rejected.
469
+ The +:allow_nil+ option skips the validation when the value being validated is +nil+.
470
470
 
471
471
  <ruby>
472
472
  class Coffee < ActiveRecord::Base
@@ -475,6 +475,8 @@ class Coffee < ActiveRecord::Base
475
475
  end
476
476
  </ruby>
477
477
 
478
+ TIP: +:allow_nil+ is ignored by the presence validator.
479
+
478
480
  h4. +:allow_blank+
479
481
 
480
482
  The +:allow_blank+ option is similar to the +:allow_nil+ option. This option will let validation pass if the attribute's value is +blank?+, like +nil+ or an empty string for example.
@@ -488,6 +490,8 @@ Topic.create("title" => "").valid? # => true
488
490
  Topic.create("title" => nil).valid? # => true
489
491
  </ruby>
490
492
 
493
+ TIP: +:allow_blank+ is ignored by the presence validator.
494
+
491
495
  h4. +:message+
492
496
 
493
497
  As you've already seen, the +:message+ option lets you specify the message that will be added to the +errors+ collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper.
@@ -505,7 +509,7 @@ class Person < ActiveRecord::Base
505
509
  validates_numericality_of :age, :on => :update
506
510
 
507
511
  # the default (validates on both create and update)
508
- validates_presence_of :name, :on => :save
512
+ validates :name, :presence => true, :on => :save
509
513
  end
510
514
  </ruby>
511
515
 
@@ -548,6 +552,21 @@ class Account < ActiveRecord::Base
548
552
  end
549
553
  </ruby>
550
554
 
555
+ h4. Grouping conditional validations
556
+
557
+ Sometimes it is useful to have multiple validations use one condition, it can be easily achieved using +with_options+.
558
+
559
+ <ruby>
560
+ class User < ActiveRecord::Base
561
+ with_options :if => :is_admin? do |admin|
562
+ admin.validates_length_of :password, :minimum => 10
563
+ admin.validates_presence_of :email
564
+ end
565
+ end
566
+ </ruby>
567
+
568
+ All validations inside of +with_options+ block will have automatically passed the condition +:if => :is_admin?+
569
+
551
570
  h3. Creating Custom Validation Methods
552
571
 
553
572
  When the built-in validation helpers are not enough for your needs, you can write your own validation methods.
@@ -603,7 +622,7 @@ Returns an OrderedHash with all errors. Each key is the attribute name and the v
603
622
 
604
623
  <ruby>
605
624
  class Person < ActiveRecord::Base
606
- validates_presence_of :name
625
+ validates :name, :presence => true
607
626
  validates_length_of :name, :minimum => 3
608
627
  end
609
628
 
@@ -623,7 +642,7 @@ h4(#working_with_validation_errors-errors-2). +errors[]+
623
642
 
624
643
  <ruby>
625
644
  class Person < ActiveRecord::Base
626
- validates_presence_of :name
645
+ validates :name, :presence => true
627
646
  validates_length_of :name, :minimum => 3
628
647
  end
629
648
 
@@ -699,7 +718,7 @@ The +clear+ method is used when you intentionally want to clear all the messages
699
718
 
700
719
  <ruby>
701
720
  class Person < ActiveRecord::Base
702
- validates_presence_of :name
721
+ validates :name, :presence => true
703
722
  validates_length_of :name, :minimum => 3
704
723
  end
705
724
 
@@ -723,7 +742,7 @@ The +size+ method returns the total number of error messages for the object.
723
742
 
724
743
  <ruby>
725
744
  class Person < ActiveRecord::Base
726
- validates_presence_of :name
745
+ validates :name, :presence => true
727
746
  validates_length_of :name, :minimum => 3
728
747
  validates_presence_of :email
729
748
  end
@@ -739,7 +758,20 @@ person.errors.size # => 0
739
758
 
740
759
  h3. Displaying Validation Errors in the View
741
760
 
742
- Rails provides built-in helpers to display the error messages of your models in your view templates.
761
+ Rails maintains an official plugin that provides helpers to display the error messages of your models in your view templates. You can install it as a plugin or as a Gem.
762
+
763
+ h4. Installing as a plugin
764
+ <shell>
765
+ $ rails plugin install git://github.com/joelmoss/dynamic_form.git
766
+ </shell>
767
+
768
+ h4 Installing as a Gem
769
+ Add this line on your Gemfile:
770
+ <ruby>
771
+ gem "dynamic_form"
772
+ </ruby>
773
+
774
+ Now you will have access to these two methods in your view templates:
743
775
 
744
776
  h4. +error_messages+ and +error_messages_for+
745
777
 
@@ -805,7 +837,7 @@ The selectors to customize the style of error messages are:
805
837
  * +#errorExplanation p+ - Style for the paragraph that holds the message that appears right below the header of the +div+ element.
806
838
  * +#errorExplanation ul li+ - Style for the list items with individual error messages.
807
839
 
808
- Scaffolding for example generates +public/stylesheets/scaffold.css+, which defines the red-based style you saw above.
840
+ Scaffolding for example generates +app/assets/stylesheets/scaffold.css.scss+, which later compiles to +app/assets/stylesheets/scaffold.css+ and defines the red-based style you saw above.
809
841
 
810
842
  The name of the class and the id can be changed with the +:class+ and +:id+ options, accepted by both helpers.
811
843
 
@@ -865,8 +897,9 @@ The macro-style class methods can also receive a block. Consider using this styl
865
897
  class User < ActiveRecord::Base
866
898
  validates_presence_of :login, :email
867
899
 
868
- before_create {|user| user.name = user.login.capitalize
869
- if user.name.blank?}
900
+ before_create do |user|
901
+ user.name = user.login.capitalize if user.name.blank?
902
+ end
870
903
  end
871
904
  </ruby>
872
905
 
@@ -977,6 +1010,7 @@ Just as with validations, it's also possible to skip callbacks. These methods sh
977
1010
  * +increment+
978
1011
  * +increment_counter+
979
1012
  * +toggle+
1013
+ * +update_column+
980
1014
  * +update_all+
981
1015
  * +update_counters+
982
1016
 
@@ -1113,7 +1147,7 @@ h4. Creating Observers
1113
1147
  For example, imagine a +User+ model where we want to send an email every time a new user is created. Because sending emails is not directly related to our model's purpose, we could create an observer to contain this functionality.
1114
1148
 
1115
1149
  <shell>
1116
- rails generate observer User
1150
+ $ rails generate observer User
1117
1151
  </shell>
1118
1152
 
1119
1153
  <ruby>
@@ -1158,8 +1192,43 @@ In this example, the +after_create+ method would be called whenever a +Registrat
1158
1192
  config.active_record.observers = :mailer_observer
1159
1193
  </ruby>
1160
1194
 
1195
+ h3. Transaction Callbacks
1196
+
1197
+ There are two additional callbacks that are triggered by the completion of a database transaction: +after_commit+ and +after_rollback+. These callbacks are very similar to the +after_save+ callback except that they don't execute until after database changes have either been committed or rolled back. They are most useful when your active record models need to interact with external systems which are not part of the database transaction.
1198
+
1199
+ Consider, for example, the previous example where the +PictureFile+ model needs to delete a file after a record is destroyed. If anything raises an exception after the +after_destroy+ callback is called and the transaction rolls back, the file will have been deleted and the model will be left in an inconsistent state. For example, suppose that +picture_file_2+ in the code below is not valid and the +save!+ method raises an error.
1200
+
1201
+ <ruby>
1202
+ PictureFile.transaction do
1203
+ picture_file_1.destroy
1204
+ picture_file_2.save!
1205
+ end
1206
+ </ruby>
1207
+
1208
+ By using the +after_commit+ callback we can account for this case.
1209
+
1210
+ <ruby>
1211
+ class PictureFile < ActiveRecord::Base
1212
+ attr_accessor :delete_file
1213
+
1214
+ after_destroy do |picture_file|
1215
+ picture_file.delete_file = picture_file.filepath
1216
+ end
1217
+
1218
+ after_commit do |picture_file|
1219
+ if picture_file.delete_file && File.exist?(picture_file.delete_file)
1220
+ File.delete(picture_file.delete_file)
1221
+ picture_file.delete_file = nil
1222
+ end
1223
+ end
1224
+ end
1225
+ </ruby>
1226
+
1227
+ The +after_commit+ and +after_rollback+ callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don't interfere with the other callbacks. As such, if your callback code could raise an exception, you'll need to rescue it and handle it appropriately within the callback.
1228
+
1161
1229
  h3. Changelog
1162
1230
 
1231
+ * February 17, 2011: Add description of transaction callbacks.
1163
1232
  * July 20, 2010: Fixed typos and rephrased some paragraphs for clarity. "Jaime Iniesta":http://jaimeiniesta.com
1164
1233
  * May 24, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
1165
1234
  * May 15, 2010: Validation Errors section updated by "Emili Parreño":http://www.eparreno.com