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.
- data/CHANGELOG +36 -49
- data/README.rdoc +2 -1
- data/guides/assets/stylesheets/fixes.css +16 -0
- data/guides/rails_guides.rb +2 -2
- data/guides/rails_guides/generator.rb +8 -3
- data/guides/rails_guides/textile_extensions.rb +4 -2
- data/guides/source/2_2_release_notes.textile +3 -3
- data/guides/source/2_3_release_notes.textile +2 -2
- data/guides/source/3_0_release_notes.textile +14 -14
- data/guides/source/action_controller_overview.textile +54 -79
- data/guides/source/action_mailer_basics.textile +39 -9
- data/guides/source/action_view_overview.textile +257 -211
- data/guides/source/active_record_basics.textile +1 -1
- data/guides/source/active_record_querying.textile +217 -27
- data/guides/source/active_record_validations_callbacks.textile +94 -25
- data/guides/source/active_support_core_extensions.textile +109 -77
- data/guides/source/ajax_on_rails.textile +15 -150
- data/guides/source/api_documentation_guidelines.textile +12 -12
- data/guides/source/association_basics.textile +74 -60
- data/guides/source/caching_with_rails.textile +59 -60
- data/guides/source/command_line.textile +46 -47
- data/guides/source/configuring.textile +55 -37
- data/guides/source/contribute.textile +7 -7
- data/guides/source/contributing_to_ruby_on_rails.textile +14 -23
- data/guides/source/credits.html.erb +3 -3
- data/guides/source/debugging_rails_applications.textile +59 -46
- data/guides/source/form_helpers.textile +76 -31
- data/guides/source/generators.textile +39 -40
- data/guides/source/getting_started.textile +73 -94
- data/guides/source/i18n.textile +64 -58
- data/guides/source/index.html.erb +3 -3
- data/guides/source/initialization.textile +634 -3284
- data/guides/source/layout.html.erb +6 -7
- data/guides/source/layouts_and_rendering.textile +59 -60
- data/guides/source/migrations.textile +63 -59
- data/guides/source/nested_model_forms.textile +2 -2
- data/guides/source/performance_testing.textile +16 -16
- data/guides/source/plugins.textile +236 -1280
- data/guides/source/rails_application_templates.textile +37 -29
- data/guides/source/rails_on_rack.textile +4 -9
- data/guides/source/routing.textile +96 -75
- data/guides/source/ruby_on_rails_guides_guidelines.textile +19 -12
- data/guides/source/security.textile +57 -30
- data/guides/source/testing.textile +26 -24
- data/guides/w3c_validator.rb +2 -2
- data/lib/rails.rb +1 -7
- data/lib/rails/application.rb +46 -76
- data/lib/rails/application/bootstrap.rb +6 -11
- data/lib/rails/application/configuration.rb +43 -40
- data/lib/rails/application/finisher.rb +16 -4
- data/lib/rails/application/railties.rb +6 -24
- data/lib/rails/application/routes_reloader.rb +45 -0
- data/lib/rails/backtrace_cleaner.rb +1 -1
- data/lib/rails/cli.rb +7 -5
- data/lib/rails/commands.rb +27 -2
- data/lib/rails/commands/application.rb +14 -1
- data/lib/rails/commands/benchmarker.rb +3 -1
- data/lib/rails/commands/dbconsole.rb +2 -2
- data/lib/rails/commands/destroy.rb +3 -1
- data/lib/rails/commands/generate.rb +3 -1
- data/lib/rails/commands/plugin.rb +2 -7
- data/lib/rails/commands/plugin_new.rb +10 -0
- data/lib/rails/commands/profiler.rb +3 -1
- data/lib/rails/commands/server.rb +4 -0
- data/lib/rails/configuration.rb +8 -81
- data/lib/rails/console/app.rb +2 -2
- data/lib/rails/engine.rb +460 -78
- data/lib/rails/engine/configuration.rb +46 -49
- data/lib/rails/engine/railties.rb +33 -0
- data/lib/rails/generators.rb +11 -5
- data/lib/rails/generators/actions.rb +2 -27
- data/lib/rails/generators/app_base.rb +216 -0
- data/lib/rails/generators/base.rb +3 -2
- data/lib/rails/generators/erb/scaffold/templates/index.html.erb +1 -1
- data/lib/rails/generators/generated_attribute.rb +2 -1
- data/lib/rails/generators/migration.rb +6 -2
- data/lib/rails/generators/named_base.rb +79 -3
- data/lib/rails/generators/rails/app/app_generator.rb +44 -209
- data/lib/rails/generators/rails/app/templates/Gemfile +15 -31
- data/lib/rails/generators/rails/app/templates/README +2 -2
- data/lib/rails/generators/rails/app/templates/Rakefile +1 -1
- data/lib/rails/generators/rails/app/templates/{public → app/assets}/images/rails.png +0 -0
- data/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +8 -0
- data/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css +5 -0
- data/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory +0 -0
- data/lib/rails/generators/rails/app/templates/app/models/.empty_directory +0 -0
- data/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +4 -4
- data/lib/rails/generators/rails/app/templates/config/application.rb +19 -3
- data/lib/rails/generators/rails/app/templates/config/databases/jdbcmysql.yml +4 -4
- data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml +11 -6
- data/lib/rails/generators/rails/app/templates/config/databases/jdbcsqlite3.yml +3 -3
- data/lib/rails/generators/rails/app/templates/config/databases/oracle.yml +1 -1
- data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +1 -2
- data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +14 -11
- data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +5 -1
- data/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt +1 -1
- data/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt +12 -0
- data/lib/rails/generators/rails/app/templates/config/locales/en.yml +1 -1
- data/lib/rails/generators/rails/app/templates/config/routes.rb +1 -1
- data/lib/rails/generators/rails/app/templates/db/{seeds.rb → seeds.rb.tt} +2 -2
- data/lib/rails/generators/rails/app/templates/public/index.html +10 -8
- data/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory +0 -0
- data/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory +0 -0
- data/lib/rails/generators/rails/app/templates/test/functional/.empty_directory +0 -0
- data/lib/rails/generators/rails/app/templates/test/integration/.empty_directory +0 -0
- data/lib/rails/generators/rails/app/templates/test/{test_helper.rb.tt → test_helper.rb} +0 -0
- data/lib/rails/generators/rails/app/templates/test/unit/.empty_directory +0 -0
- data/lib/rails/generators/rails/assets/USAGE +20 -0
- data/lib/rails/generators/rails/assets/assets_generator.rb +39 -0
- data/lib/rails/generators/rails/assets/templates/javascript.js +2 -0
- data/lib/rails/generators/rails/assets/templates/javascript.js.coffee +3 -0
- data/lib/rails/generators/rails/assets/templates/stylesheet.css +4 -0
- data/lib/rails/generators/rails/assets/templates/stylesheet.css.scss +5 -0
- data/lib/rails/generators/rails/controller/controller_generator.rb +1 -1
- data/lib/rails/generators/rails/controller/templates/controller.rb +2 -0
- data/lib/rails/generators/rails/generator/generator_generator.rb +2 -2
- data/lib/rails/generators/rails/generator/templates/templates/.empty_directory +0 -0
- data/lib/rails/generators/rails/helper/templates/helper.rb +2 -0
- data/lib/rails/generators/rails/plugin/plugin_generator.rb +7 -0
- data/lib/rails/generators/rails/plugin/templates/Rakefile.tt +4 -4
- data/lib/rails/generators/rails/plugin_new/USAGE +10 -0
- data/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +303 -0
- data/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec +9 -0
- data/lib/rails/generators/rails/plugin_new/templates/Gemfile +11 -0
- data/lib/rails/generators/rails/plugin_new/templates/MIT-LICENSE +20 -0
- data/lib/rails/generators/rails/plugin_new/templates/README.rdoc +3 -0
- data/lib/rails/generators/rails/plugin_new/templates/Rakefile +21 -0
- data/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt +4 -0
- data/lib/rails/generators/rails/plugin_new/templates/app/helpers/%name%/application_helper.rb.tt +4 -0
- data/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory +0 -0
- data/lib/rails/generators/rails/plugin_new/templates/config/routes.rb +6 -0
- data/lib/rails/generators/rails/plugin_new/templates/gitignore +6 -0
- data/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb +6 -0
- data/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb +7 -0
- data/lib/rails/generators/rails/plugin_new/templates/lib/tasks/%name%_tasks.rake +4 -0
- data/lib/rails/generators/rails/plugin_new/templates/rails/application.rb +16 -0
- data/lib/rails/generators/rails/plugin_new/templates/rails/boot.rb +10 -0
- data/lib/rails/generators/rails/plugin_new/templates/rails/routes.rb +4 -0
- data/lib/rails/generators/rails/plugin_new/templates/script/rails.tt +5 -0
- data/lib/rails/generators/rails/plugin_new/templates/test/%name%_test.rb +7 -0
- data/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb +12 -0
- data/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb +10 -0
- data/lib/rails/generators/rails/resource/resource_generator.rb +2 -2
- data/lib/rails/generators/rails/scaffold/scaffold_generator.rb +20 -1
- data/lib/rails/generators/rails/{stylesheets → scaffold}/templates/scaffold.css +0 -0
- data/lib/rails/generators/rails/scaffold/templates/scaffold.css.scss +58 -0
- data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +21 -19
- data/lib/rails/generators/resource_helpers.rb +3 -3
- data/lib/rails/generators/test_case.rb +2 -20
- data/lib/rails/generators/test_unit/controller/templates/functional_test.rb +5 -4
- data/lib/rails/generators/test_unit/helper/templates/helper_test.rb +2 -0
- data/lib/rails/generators/test_unit/integration/templates/integration_test.rb +3 -4
- data/lib/rails/generators/test_unit/mailer/templates/functional_test.rb +5 -4
- data/lib/rails/generators/test_unit/model/templates/fixtures.yml +1 -1
- data/lib/rails/generators/test_unit/model/templates/unit_test.rb +5 -4
- data/lib/rails/generators/test_unit/observer/templates/unit_test.rb +5 -4
- data/lib/rails/generators/test_unit/plugin/templates/%file_name%_test.rb.tt +3 -4
- data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +7 -5
- data/lib/rails/info.rb +0 -1
- data/lib/rails/paths.rb +119 -65
- data/lib/rails/plugin.rb +18 -19
- data/lib/rails/rack/log_tailer.rb +1 -1
- data/lib/rails/railtie.rb +50 -47
- data/lib/rails/railtie/configurable.rb +20 -10
- data/lib/rails/railtie/configuration.rb +20 -19
- data/lib/rails/source_annotation_extractor.rb +5 -5
- data/lib/rails/tasks.rb +1 -0
- data/lib/rails/tasks/assets.rake +10 -0
- data/lib/rails/tasks/documentation.rake +2 -8
- data/lib/rails/tasks/engine.rake +69 -0
- data/lib/rails/tasks/framework.rake +4 -21
- data/lib/rails/tasks/misc.rake +1 -1
- data/lib/rails/tasks/routes.rake +2 -1
- data/lib/rails/test_help.rb +17 -1
- data/lib/rails/test_unit/railtie.rb +1 -1
- data/lib/rails/test_unit/testing.rake +8 -3
- data/lib/rails/version.rb +3 -3
- metadata +128 -100
- checksums.yaml +0 -7
- data/lib/rails/application/configurable.rb +0 -19
- data/lib/rails/console/sandbox.rb +0 -6
- data/lib/rails/deprecation.rb +0 -41
- data/lib/rails/engine/configurable.rb +0 -25
- data/lib/rails/generators/rails/app/templates/config/databases/jdbc.yml +0 -62
- data/lib/rails/generators/rails/app/templates/public/javascripts/application.js +0 -2
- data/lib/rails/generators/rails/app/templates/public/javascripts/controls.js +0 -965
- data/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js +0 -974
- data/lib/rails/generators/rails/app/templates/public/javascripts/effects.js +0 -1123
- data/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js +0 -6001
- data/lib/rails/generators/rails/app/templates/public/javascripts/rails.js +0 -202
- data/lib/rails/generators/rails/stylesheets/USAGE +0 -5
- data/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb +0 -9
- 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.
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
422
|
+
ActiveModel::MissingAttributeError: missing attribute: <attribute>
|
390
423
|
</shell>
|
391
424
|
|
392
425
|
Where +<attribute>+ 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
|
-
|
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
|
458
|
+
SELECT * FROM clients LIMIT 5 OFFSET 30
|
426
459
|
</sql>
|
427
460
|
|
428
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
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.
|
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
|
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
|
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
|
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,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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 <
|
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
|
-
|
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+.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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 +
|
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
|
869
|
-
|
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
|