rails 4.1.16 → 4.2.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/guides/CHANGELOG.md +13 -102
- data/guides/Rakefile +2 -2
- data/guides/assets/javascripts/guides.js +6 -0
- data/guides/assets/stylesheets/main.css +4 -1
- data/guides/bug_report_templates/action_controller_gem.rb +2 -2
- data/guides/bug_report_templates/action_controller_master.rb +5 -2
- data/guides/bug_report_templates/active_record_master.rb +2 -0
- data/guides/rails_guides.rb +2 -2
- data/guides/rails_guides/helpers.rb +1 -1
- data/guides/rails_guides/levenshtein.rb +29 -21
- data/guides/rails_guides/markdown.rb +6 -7
- data/guides/rails_guides/markdown/renderer.rb +1 -1
- data/guides/source/2_3_release_notes.md +3 -3
- data/guides/source/3_0_release_notes.md +4 -4
- data/guides/source/3_1_release_notes.md +2 -2
- data/guides/source/3_2_release_notes.md +2 -2
- data/guides/source/4_1_release_notes.md +8 -9
- data/guides/source/4_2_release_notes.md +572 -0
- data/guides/source/_license.html.erb +1 -1
- data/guides/source/_welcome.html.erb +2 -8
- data/guides/source/action_controller_overview.md +79 -7
- data/guides/source/action_mailer_basics.md +36 -11
- data/guides/source/action_view_overview.md +138 -119
- data/guides/source/active_job_basics.md +253 -0
- data/guides/source/active_model_basics.md +23 -0
- data/guides/source/active_record_basics.md +16 -15
- data/guides/source/active_record_callbacks.md +12 -9
- data/guides/source/{migrations.md → active_record_migrations.md} +90 -217
- data/guides/source/active_record_postgresql.md +437 -0
- data/guides/source/active_record_querying.md +261 -261
- data/guides/source/active_record_validations.md +7 -7
- data/guides/source/active_support_core_extensions.md +105 -44
- data/guides/source/active_support_instrumentation.md +3 -2
- data/guides/source/api_documentation_guidelines.md +62 -16
- data/guides/source/asset_pipeline.md +58 -46
- data/guides/source/association_basics.md +47 -38
- data/guides/source/caching_with_rails.md +31 -6
- data/guides/source/command_line.md +56 -25
- data/guides/source/configuring.md +98 -19
- data/guides/source/contributing_to_ruby_on_rails.md +174 -111
- data/guides/source/credits.html.erb +1 -1
- data/guides/source/debugging_rails_applications.md +438 -284
- data/guides/source/development_dependencies_install.md +17 -4
- data/guides/source/documents.yaml +11 -7
- data/guides/source/engines.md +192 -203
- data/guides/source/form_helpers.md +54 -45
- data/guides/source/generators.md +20 -11
- data/guides/source/getting_started.md +330 -191
- data/guides/source/i18n.md +92 -62
- data/guides/source/index.html.erb +1 -0
- data/guides/source/initialization.md +108 -59
- data/guides/source/layout.html.erb +1 -4
- data/guides/source/layouts_and_rendering.md +24 -23
- data/guides/source/nested_model_forms.md +3 -3
- data/guides/source/plugins.md +26 -26
- data/guides/source/rails_application_templates.md +21 -3
- data/guides/source/rails_on_rack.md +1 -1
- data/guides/source/routing.md +97 -71
- data/guides/source/ruby_on_rails_guides_guidelines.md +10 -12
- data/guides/source/security.md +39 -33
- data/guides/source/testing.md +111 -108
- data/guides/source/upgrading_ruby_on_rails.md +131 -14
- data/guides/source/working_with_javascript_in_rails.md +18 -16
- data/guides/w3c_validator.rb +2 -0
- metadata +37 -94
- data/guides/bug_report_templates/generic_gem.rb +0 -15
- data/guides/bug_report_templates/generic_master.rb +0 -26
- data/guides/code/getting_started/Gemfile +0 -40
- data/guides/code/getting_started/Gemfile.lock +0 -125
- data/guides/code/getting_started/README.rdoc +0 -28
- data/guides/code/getting_started/Rakefile +0 -6
- data/guides/code/getting_started/app/assets/javascripts/application.js +0 -15
- data/guides/code/getting_started/app/assets/javascripts/comments.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/posts.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/application.css +0 -13
- data/guides/code/getting_started/app/assets/stylesheets/comments.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/posts.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss +0 -3
- data/guides/code/getting_started/app/controllers/application_controller.rb +0 -5
- data/guides/code/getting_started/app/controllers/comments_controller.rb +0 -23
- data/guides/code/getting_started/app/controllers/posts_controller.rb +0 -53
- data/guides/code/getting_started/app/controllers/welcome_controller.rb +0 -4
- data/guides/code/getting_started/app/helpers/application_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/comments_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/posts_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/welcome_helper.rb +0 -2
- data/guides/code/getting_started/app/models/comment.rb +0 -3
- data/guides/code/getting_started/app/models/post.rb +0 -7
- data/guides/code/getting_started/app/views/comments/_comment.html.erb +0 -15
- data/guides/code/getting_started/app/views/comments/_form.html.erb +0 -13
- data/guides/code/getting_started/app/views/layouts/application.html.erb +0 -14
- data/guides/code/getting_started/app/views/posts/_form.html.erb +0 -27
- data/guides/code/getting_started/app/views/posts/edit.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/index.html.erb +0 -21
- data/guides/code/getting_started/app/views/posts/new.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/show.html.erb +0 -18
- data/guides/code/getting_started/app/views/welcome/index.html.erb +0 -4
- data/guides/code/getting_started/bin/bundle +0 -4
- data/guides/code/getting_started/bin/rails +0 -4
- data/guides/code/getting_started/bin/rake +0 -4
- data/guides/code/getting_started/config.ru +0 -4
- data/guides/code/getting_started/config/application.rb +0 -18
- data/guides/code/getting_started/config/boot.rb +0 -4
- data/guides/code/getting_started/config/database.yml +0 -25
- data/guides/code/getting_started/config/environment.rb +0 -5
- data/guides/code/getting_started/config/environments/development.rb +0 -30
- data/guides/code/getting_started/config/environments/production.rb +0 -80
- data/guides/code/getting_started/config/environments/test.rb +0 -36
- data/guides/code/getting_started/config/initializers/backtrace_silencers.rb +0 -7
- data/guides/code/getting_started/config/initializers/filter_parameter_logging.rb +0 -4
- data/guides/code/getting_started/config/initializers/inflections.rb +0 -16
- data/guides/code/getting_started/config/initializers/locale.rb +0 -9
- data/guides/code/getting_started/config/initializers/mime_types.rb +0 -5
- data/guides/code/getting_started/config/initializers/secret_token.rb +0 -12
- data/guides/code/getting_started/config/initializers/session_store.rb +0 -3
- data/guides/code/getting_started/config/initializers/wrap_parameters.rb +0 -14
- data/guides/code/getting_started/config/locales/en.yml +0 -23
- data/guides/code/getting_started/config/routes.rb +0 -7
- data/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb +0 -10
- data/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb +0 -11
- data/guides/code/getting_started/db/schema.rb +0 -33
- data/guides/code/getting_started/db/seeds.rb +0 -7
- data/guides/code/getting_started/public/404.html +0 -60
- data/guides/code/getting_started/public/422.html +0 -60
- data/guides/code/getting_started/public/500.html +0 -59
- data/guides/code/getting_started/public/favicon.ico +0 -0
- data/guides/code/getting_started/public/robots.txt +0 -5
- data/guides/code/getting_started/test/controllers/comments_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/posts_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/welcome_controller_test.rb +0 -9
- data/guides/code/getting_started/test/fixtures/comments.yml +0 -11
- data/guides/code/getting_started/test/fixtures/posts.yml +0 -9
- data/guides/code/getting_started/test/helpers/comments_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/posts_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/welcome_helper_test.rb +0 -4
- data/guides/code/getting_started/test/models/comment_test.rb +0 -7
- data/guides/code/getting_started/test/models/post_test.rb +0 -7
- data/guides/code/getting_started/test/test_helper.rb +0 -12
@@ -0,0 +1,253 @@
|
|
1
|
+
Active Job Basics
|
2
|
+
=================
|
3
|
+
|
4
|
+
This guide provides you with all you need to get started in creating,
|
5
|
+
enqueueing and executing background jobs.
|
6
|
+
|
7
|
+
After reading this guide, you will know:
|
8
|
+
|
9
|
+
* How to create jobs.
|
10
|
+
* How to enqueue jobs.
|
11
|
+
* How to run jobs in the background.
|
12
|
+
* How to send emails from your application async.
|
13
|
+
|
14
|
+
--------------------------------------------------------------------------------
|
15
|
+
|
16
|
+
Introduction
|
17
|
+
------------
|
18
|
+
|
19
|
+
Active Job is a framework for declaring jobs and making them run on a variety
|
20
|
+
of queueing backends. These jobs can be everything from regularly scheduled
|
21
|
+
clean-ups, billing charges, or mailings. Anything that can be chopped up
|
22
|
+
into small units of work and run in parallel, really.
|
23
|
+
|
24
|
+
|
25
|
+
The Purpose of the Active Job
|
26
|
+
-----------------------------
|
27
|
+
The main point is to ensure that all Rails apps will have a job infrastructure
|
28
|
+
in place, even if it's in the form of an "immediate runner". We can then have
|
29
|
+
framework features and other gems build on top of that, without having to
|
30
|
+
worry about API differences between various job runners such as Delayed Job
|
31
|
+
and Resque. Picking your queuing backend becomes more of an operational concern,
|
32
|
+
then. And you'll be able to switch between them without having to rewrite your jobs.
|
33
|
+
|
34
|
+
|
35
|
+
Creating a Job
|
36
|
+
--------------
|
37
|
+
|
38
|
+
This section will provide a step-by-step guide to creating a job and enqueue it.
|
39
|
+
|
40
|
+
### Create the Job
|
41
|
+
|
42
|
+
Active Job provides a Rails generator to create jobs. The following will create a
|
43
|
+
job in app/jobs:
|
44
|
+
|
45
|
+
```bash
|
46
|
+
$ bin/rails generate job guests_cleanup
|
47
|
+
create app/jobs/guests_cleanup_job.rb
|
48
|
+
```
|
49
|
+
|
50
|
+
You can also create a job that will run on a specific queue:
|
51
|
+
|
52
|
+
```bash
|
53
|
+
$ bin/rails generate job guests_cleanup --queue urgent
|
54
|
+
create app/jobs/guests_cleanup_job.rb
|
55
|
+
```
|
56
|
+
|
57
|
+
As you can see, you can generate jobs just like you use other generators with
|
58
|
+
Rails.
|
59
|
+
|
60
|
+
If you don't want to use a generator, you could create your own file inside of
|
61
|
+
app/jobs, just make sure that it inherits from `ActiveJob::Base`.
|
62
|
+
|
63
|
+
Here's how a job looks like:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
class GuestsCleanupJob < ActiveJob::Base
|
67
|
+
queue_as :default
|
68
|
+
|
69
|
+
def perform
|
70
|
+
# Do something later
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
### Enqueue the Job
|
76
|
+
|
77
|
+
Enqueue a job like so:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
MyJob.enqueue record # Enqueue a job to be performed as soon the queueing system is free.
|
81
|
+
```
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
MyJob.enqueue_at Date.tomorrow.noon, record # Enqueue a job to be performed tomorrow at noon.
|
85
|
+
```
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
MyJob.enqueue_in 1.week, record # Enqueue a job to be performed 1 week from now.
|
89
|
+
```
|
90
|
+
|
91
|
+
That's it!
|
92
|
+
|
93
|
+
|
94
|
+
Job Execution
|
95
|
+
-------------
|
96
|
+
|
97
|
+
If not adapter is set, the job is immediately executed.
|
98
|
+
|
99
|
+
### Backends
|
100
|
+
|
101
|
+
Active Job has adapters for the following queueing backends:
|
102
|
+
|
103
|
+
* [Backburner](https://github.com/nesquena/backburner)
|
104
|
+
* [Delayed Job](https://github.com/collectiveidea/delayed_job)
|
105
|
+
* [Qu](https://github.com/bkeepers/qu)
|
106
|
+
* [Que](https://github.com/chanks/que)
|
107
|
+
* [QueueClassic](https://github.com/ryandotsmith/queue_classic)
|
108
|
+
* [Resque 1.x](https://github.com/resque/resque)
|
109
|
+
* [Sidekiq](https://github.com/mperham/sidekiq)
|
110
|
+
* [Sneakers](https://github.com/jondot/sneakers)
|
111
|
+
* [Sucker Punch](https://github.com/brandonhilkert/sucker_punch)
|
112
|
+
|
113
|
+
#### Backends Features
|
114
|
+
|
115
|
+
| | Async | Queues | Delayed | Priorities | Timeout | Retries |
|
116
|
+
|-----------------------|-------|---------|---------|-------------|---------|---------|
|
117
|
+
| **Backburner** | Yes | Yes | Yes | Yes | Job | Global |
|
118
|
+
| **Delayed Job** | Yes | Yes | Yes | Job | Global | Global |
|
119
|
+
| **Que** | Yes | Yes | Yes | Job | No | Job |
|
120
|
+
| **Queue Classic** | Yes | Yes | Gem | No | No | No |
|
121
|
+
| **Resque** | Yes | Yes | Gem | Queue | Global | ? |
|
122
|
+
| **Sidekiq** | Yes | Yes | Yes | Queue | No | Job |
|
123
|
+
| **Sneakers** | Yes | Yes | No | Queue | Queue | No |
|
124
|
+
| **Sucker Punch** | Yes | Yes | Yes | No | No | No |
|
125
|
+
| **Active Job** | Yes | Yes | WIP | No | No | No |
|
126
|
+
| **Active Job Inline** | No | Yes | N/A | N/A | N/A | N/A |
|
127
|
+
|
128
|
+
### Change Backends
|
129
|
+
|
130
|
+
You can easy change your adapter:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# be sure to have the adapter gem in your Gemfile and follow the adapter specific
|
134
|
+
# installation and deployment instructions
|
135
|
+
YourApp::Application.config.active_job.queue_adapter = :sidekiq
|
136
|
+
```
|
137
|
+
|
138
|
+
Queues
|
139
|
+
------
|
140
|
+
|
141
|
+
Most of the adapters supports multiple queues. With Active Job you can schedule the job
|
142
|
+
to run on a specific queue:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
class GuestsCleanupJob < ActiveJob::Base
|
146
|
+
queue_as :low_priority
|
147
|
+
#....
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
NOTE: Make sure your queueing backend "listens" on your queue name. For some backends
|
152
|
+
you need to specify the queues to listen to.
|
153
|
+
|
154
|
+
|
155
|
+
Callbacks
|
156
|
+
---------
|
157
|
+
|
158
|
+
Active Job provides hooks during the lifecycle of a job. Callbacks allows you to trigger
|
159
|
+
logic during the lifecycle of a job.
|
160
|
+
|
161
|
+
### Available callbacks
|
162
|
+
|
163
|
+
* before_enqueue
|
164
|
+
* around_enqueue
|
165
|
+
* after_enqueue
|
166
|
+
* before_perform
|
167
|
+
* around_perform
|
168
|
+
* after_perform
|
169
|
+
|
170
|
+
### Usage
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
class GuestsCleanupJob < ActiveJob::Base
|
174
|
+
queue_as :default
|
175
|
+
|
176
|
+
before_enqueue do |job|
|
177
|
+
# do somthing with the job instance
|
178
|
+
end
|
179
|
+
|
180
|
+
around_perform do |job, block|
|
181
|
+
# do something before perform
|
182
|
+
block.call
|
183
|
+
# do something after perform
|
184
|
+
end
|
185
|
+
|
186
|
+
def perform
|
187
|
+
# Do something later
|
188
|
+
end
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
ActionMailer
|
193
|
+
------------
|
194
|
+
One of the most common jobs in a modern web application is sending emails outside
|
195
|
+
of the request-response cycle, so the user doesn't have to wait on it. Active Job
|
196
|
+
is integrated with Action Mailer so you can easily send emails async:
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
# Instead of the classic
|
200
|
+
UserMailer.welcome(@user).deliver
|
201
|
+
|
202
|
+
# use #deliver later to send the email async
|
203
|
+
UserMailer.welcome(@user).deliver_later
|
204
|
+
```
|
205
|
+
|
206
|
+
GlobalID
|
207
|
+
--------
|
208
|
+
Active Job supports GlobalID for parameters. This makes it possible
|
209
|
+
to pass live Active Record objects to your job instead of class/id pairs, which
|
210
|
+
you then have to manually deserialize. Before, jobs would look like this:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
class TrashableCleanupJob
|
214
|
+
def perform(trashable_class, trashable_id, depth)
|
215
|
+
trashable = trashable_class.constantize.find(trashable_id)
|
216
|
+
trashable.cleanup(depth)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
Now you can simply do:
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
class TrashableCleanupJob
|
225
|
+
def perform(trashable, depth)
|
226
|
+
trashable.cleanup(depth)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
```
|
230
|
+
|
231
|
+
This works with any class that mixes in ActiveModel::GlobalIdentification, which
|
232
|
+
by default has been mixed into Active Model classes.
|
233
|
+
|
234
|
+
|
235
|
+
Exceptions
|
236
|
+
----------
|
237
|
+
Active Job provides a way to catch exceptions raised during the execution of the
|
238
|
+
job:
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
|
242
|
+
class GuestsCleanupJob < ActiveJob::Base
|
243
|
+
queue_as :default
|
244
|
+
|
245
|
+
rescue_from(ActiveRecord:NotFound) do |exception|
|
246
|
+
# do something with the exception
|
247
|
+
end
|
248
|
+
|
249
|
+
def perform
|
250
|
+
# Do something later
|
251
|
+
end
|
252
|
+
end
|
253
|
+
```
|
@@ -198,3 +198,26 @@ person.valid? # => true
|
|
198
198
|
person.token = nil
|
199
199
|
person.valid? # => raises ActiveModel::StrictValidationFailed
|
200
200
|
```
|
201
|
+
|
202
|
+
### ActiveModel::Naming
|
203
|
+
|
204
|
+
Naming adds a number of class methods which make the naming and routing
|
205
|
+
easier to manage. The module defines the `model_name` class method which
|
206
|
+
will define a number of accessors using some `ActiveSupport::Inflector` methods.
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
class Person
|
210
|
+
extend ActiveModel::Naming
|
211
|
+
end
|
212
|
+
|
213
|
+
Person.model_name.name # => "Person"
|
214
|
+
Person.model_name.singular # => "person"
|
215
|
+
Person.model_name.plural # => "people"
|
216
|
+
Person.model_name.element # => "person"
|
217
|
+
Person.model_name.human # => "Person"
|
218
|
+
Person.model_name.collection # => "people"
|
219
|
+
Person.model_name.param_key # => "person"
|
220
|
+
Person.model_name.i18n_key # => :person
|
221
|
+
Person.model_name.route_key # => "people"
|
222
|
+
Person.model_name.singular_route_key # => "person"
|
223
|
+
```
|
@@ -82,13 +82,13 @@ by underscores. Examples:
|
|
82
82
|
* Model Class - Singular with the first letter of each word capitalized (e.g.,
|
83
83
|
`BookClub`).
|
84
84
|
|
85
|
-
| Model / Class
|
86
|
-
|
|
87
|
-
| `
|
88
|
-
| `LineItem`
|
89
|
-
| `Deer`
|
90
|
-
| `Mouse`
|
91
|
-
| `Person`
|
85
|
+
| Model / Class | Table / Schema |
|
86
|
+
| ---------------- | -------------- |
|
87
|
+
| `Article` | `articles` |
|
88
|
+
| `LineItem` | `line_items` |
|
89
|
+
| `Deer` | `deers` |
|
90
|
+
| `Mouse` | `mice` |
|
91
|
+
| `Person` | `people` |
|
92
92
|
|
93
93
|
|
94
94
|
### Schema Conventions
|
@@ -120,9 +120,9 @@ to Active Record instances:
|
|
120
120
|
* `(association_name)_type` - Stores the type for
|
121
121
|
[polymorphic associations](association_basics.html#polymorphic-associations).
|
122
122
|
* `(table_name)_count` - Used to cache the number of belonging objects on
|
123
|
-
associations. For example, a `comments_count` column in a `
|
123
|
+
associations. For example, a `comments_count` column in a `Articles` class that
|
124
124
|
has many instances of `Comment` will cache the number of existent comments
|
125
|
-
for each
|
125
|
+
for each article.
|
126
126
|
|
127
127
|
NOTE: While these column names are optional, they are in fact reserved by Active Record. Steer clear of reserved keywords unless you want the extra functionality. For example, `type` is a reserved keyword used to designate a table using Single Table Inheritance (STI). If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling.
|
128
128
|
|
@@ -309,11 +309,11 @@ into the database. There are several methods that you can use to check your
|
|
309
309
|
models and validate that an attribute value is not empty, is unique and not
|
310
310
|
already in the database, follows a specific format and many more.
|
311
311
|
|
312
|
-
Validation is a very important issue to consider when persisting to database, so
|
313
|
-
the methods `
|
312
|
+
Validation is a very important issue to consider when persisting to the database, so
|
313
|
+
the methods `save` and `update` take it into account when
|
314
314
|
running: they return `false` when validation fails and they didn't actually
|
315
|
-
perform any operation on database. All of these have a bang counterpart (that
|
316
|
-
is, `
|
315
|
+
perform any operation on the database. All of these have a bang counterpart (that
|
316
|
+
is, `save!` and `update!`), which are stricter in that
|
317
317
|
they raise the exception `ActiveRecord::RecordInvalid` if validation fails.
|
318
318
|
A quick example to illustrate:
|
319
319
|
|
@@ -322,8 +322,9 @@ class User < ActiveRecord::Base
|
|
322
322
|
validates :name, presence: true
|
323
323
|
end
|
324
324
|
|
325
|
-
User.
|
326
|
-
|
325
|
+
user = User.new
|
326
|
+
user.save # => false
|
327
|
+
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
327
328
|
```
|
328
329
|
|
329
330
|
You can learn more about validations in the [Active Record Validations
|
@@ -15,7 +15,7 @@ After reading this guide, you will know:
|
|
15
15
|
The Object Life Cycle
|
16
16
|
---------------------
|
17
17
|
|
18
|
-
During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this
|
18
|
+
During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this *object life cycle* so that you can control your application and its data.
|
19
19
|
|
20
20
|
Callbacks allow you to trigger logic before or after an alteration of an object's state.
|
21
21
|
|
@@ -92,6 +92,7 @@ Here is a list with all the available Active Record callbacks, listed in the sam
|
|
92
92
|
* `around_create`
|
93
93
|
* `after_create`
|
94
94
|
* `after_save`
|
95
|
+
* `after_commit/after_rollback`
|
95
96
|
|
96
97
|
### Updating an Object
|
97
98
|
|
@@ -103,12 +104,14 @@ Here is a list with all the available Active Record callbacks, listed in the sam
|
|
103
104
|
* `around_update`
|
104
105
|
* `after_update`
|
105
106
|
* `after_save`
|
107
|
+
* `after_commit/after_rollback`
|
106
108
|
|
107
109
|
### Destroying an Object
|
108
110
|
|
109
111
|
* `before_destroy`
|
110
112
|
* `around_destroy`
|
111
113
|
* `after_destroy`
|
114
|
+
* `after_commit/after_rollback`
|
112
115
|
|
113
116
|
WARNING. `after_save` runs both on create and update, but always _after_ the more specific callbacks `after_create` and `after_update`, no matter the order in which the macro calls were executed.
|
114
117
|
|
@@ -258,27 +261,27 @@ WARNING. Any exception that is not `ActiveRecord::Rollback` will be re-raised by
|
|
258
261
|
Relational Callbacks
|
259
262
|
--------------------
|
260
263
|
|
261
|
-
Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many
|
264
|
+
Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many articles. A user's articles should be destroyed if the user is destroyed. Let's add an `after_destroy` callback to the `User` model by way of its relationship to the `Article` model:
|
262
265
|
|
263
266
|
```ruby
|
264
267
|
class User < ActiveRecord::Base
|
265
|
-
has_many :
|
268
|
+
has_many :articles, dependent: :destroy
|
266
269
|
end
|
267
270
|
|
268
|
-
class
|
271
|
+
class Article < ActiveRecord::Base
|
269
272
|
after_destroy :log_destroy_action
|
270
273
|
|
271
274
|
def log_destroy_action
|
272
|
-
puts '
|
275
|
+
puts 'Article destroyed'
|
273
276
|
end
|
274
277
|
end
|
275
278
|
|
276
279
|
>> user = User.first
|
277
280
|
=> #<User id: 1>
|
278
|
-
>> user.
|
279
|
-
=> #<
|
281
|
+
>> user.articles.create!
|
282
|
+
=> #<Article id: 1, user_id: 1>
|
280
283
|
>> user.destroy
|
281
|
-
|
284
|
+
Article destroyed
|
282
285
|
=> #<User id: 1>
|
283
286
|
```
|
284
287
|
|
@@ -325,7 +328,7 @@ When writing conditional callbacks, it is possible to mix both `:if` and `:unles
|
|
325
328
|
```ruby
|
326
329
|
class Comment < ActiveRecord::Base
|
327
330
|
after_create :send_email_to_author, if: :author_wants_emails?,
|
328
|
-
unless: Proc.new { |comment| comment.
|
331
|
+
unless: Proc.new { |comment| comment.article.ignore_comments? }
|
329
332
|
end
|
330
333
|
```
|
331
334
|
|
@@ -18,9 +18,10 @@ After reading this guide, you will know:
|
|
18
18
|
Migration Overview
|
19
19
|
------------------
|
20
20
|
|
21
|
-
Migrations are a convenient way to
|
22
|
-
|
23
|
-
|
21
|
+
Migrations are a convenient way to
|
22
|
+
[alter your database schema over time](http://en.wikipedia.org/wiki/Schema_migration)
|
23
|
+
in a consistent and easy way. They use a Ruby DSL so that you don't have to
|
24
|
+
write SQL by hand, allowing your schema and changes to be database independent.
|
24
25
|
|
25
26
|
You can think of each migration as being a new 'version' of the database. A
|
26
27
|
schema starts off with nothing in it, and each migration modifies it to add or
|
@@ -433,21 +434,62 @@ change_column_default :products, :approved, false
|
|
433
434
|
This sets `:name` field on products to a `NOT NULL` column and the default
|
434
435
|
value of the `:approved` field to false.
|
435
436
|
|
437
|
+
TIP: Unlike `change_column` (and `change_column_default`), `change_column_null`
|
438
|
+
is reversible.
|
439
|
+
|
436
440
|
### Column Modifiers
|
437
441
|
|
438
442
|
Column modifiers can be applied when creating or changing a column:
|
439
443
|
|
440
444
|
* `limit` Sets the maximum size of the `string/text/binary/integer` fields.
|
441
|
-
* `precision` Defines the precision for the `decimal` fields, representing the
|
442
|
-
|
445
|
+
* `precision` Defines the precision for the `decimal` fields, representing the
|
446
|
+
total number of digits in the number.
|
447
|
+
* `scale` Defines the scale for the `decimal` fields, representing the
|
448
|
+
number of digits after the decimal point.
|
443
449
|
* `polymorphic` Adds a `type` column for `belongs_to` associations.
|
444
450
|
* `null` Allows or disallows `NULL` values in the column.
|
445
|
-
* `default` Allows to set a default value on the column.
|
451
|
+
* `default` Allows to set a default value on the column. Note that if you
|
452
|
+
are using a dynamic value (such as a date), the default will only be calculated
|
453
|
+
the first time (i.e. on the date the migration is applied).
|
446
454
|
* `index` Adds an index for the column.
|
447
455
|
|
448
456
|
Some adapters may support additional options; see the adapter specific API docs
|
449
457
|
for further information.
|
450
458
|
|
459
|
+
### Foreign Keys
|
460
|
+
|
461
|
+
While it's not required you might want to add foreign key constraints to
|
462
|
+
[guarantee referential integrity](#active-record-and-referential-integrity).
|
463
|
+
|
464
|
+
```ruby
|
465
|
+
add_foreign_key :articles, :authors
|
466
|
+
```
|
467
|
+
|
468
|
+
This adds a new foreign key to the `author_id` column of the `articles`
|
469
|
+
table. The key references the `id` column of the `articles` table. If the
|
470
|
+
column names can not be derived from the table names, you can use the
|
471
|
+
`:column` and `:primary_key` options.
|
472
|
+
|
473
|
+
Rails will generate a name for every foreign key starting with
|
474
|
+
`fk_rails_` followed by 10 random characters.
|
475
|
+
There is a `:name` option to specify a different name if needed.
|
476
|
+
|
477
|
+
NOTE: Active Record only supports single column foreign keys. `execute` and
|
478
|
+
`structure.sql` are required to use composite foreign keys.
|
479
|
+
|
480
|
+
Removing a foreign key is easy as well:
|
481
|
+
|
482
|
+
```ruby
|
483
|
+
# let Active Record figure out the column name
|
484
|
+
remove_foreign_key :accounts, :branches
|
485
|
+
|
486
|
+
# remove foreign key for a specific column
|
487
|
+
remove_foreign_key :accounts, column: :owner_id
|
488
|
+
|
489
|
+
# remove foreign key by name
|
490
|
+
remove_foreign_key :accounts, name: :special_fk_name
|
491
|
+
```
|
492
|
+
|
451
493
|
### When Helpers aren't Enough
|
452
494
|
|
453
495
|
If the helpers provided by Active Record aren't enough you can use the `execute`
|
@@ -478,6 +520,7 @@ definitions:
|
|
478
520
|
* `add_index`
|
479
521
|
* `add_reference`
|
480
522
|
* `add_timestamps`
|
523
|
+
* `add_foreign_key`
|
481
524
|
* `create_table`
|
482
525
|
* `create_join_table`
|
483
526
|
* `drop_table` (must supply a block)
|
@@ -503,24 +546,23 @@ migration what else to do when reverting it. For example:
|
|
503
546
|
```ruby
|
504
547
|
class ExampleMigration < ActiveRecord::Migration
|
505
548
|
def change
|
506
|
-
create_table :
|
507
|
-
t.
|
549
|
+
create_table :distributors do |t|
|
550
|
+
t.string :zipcode
|
508
551
|
end
|
509
552
|
|
510
553
|
reversible do |dir|
|
511
554
|
dir.up do
|
512
|
-
#add a
|
555
|
+
# add a CHECK constraint
|
513
556
|
execute <<-SQL
|
514
|
-
ALTER TABLE
|
515
|
-
ADD CONSTRAINT
|
516
|
-
|
517
|
-
REFERENCES categories(id)
|
557
|
+
ALTER TABLE distributors
|
558
|
+
ADD CONSTRAINT zipchk
|
559
|
+
CHECK (char_length(zipcode) = 5) NO INHERIT;
|
518
560
|
SQL
|
519
561
|
end
|
520
562
|
dir.down do
|
521
563
|
execute <<-SQL
|
522
|
-
ALTER TABLE
|
523
|
-
DROP
|
564
|
+
ALTER TABLE distributors
|
565
|
+
DROP CONSTRAINT zipchk
|
524
566
|
SQL
|
525
567
|
end
|
526
568
|
end
|
@@ -534,7 +576,7 @@ end
|
|
534
576
|
Using `reversible` will ensure that the instructions are executed in the
|
535
577
|
right order too. If the previous example migration is reverted,
|
536
578
|
the `down` block will be run after the `home_page_url` column is removed and
|
537
|
-
right before the table `
|
579
|
+
right before the table `distributors` is dropped.
|
538
580
|
|
539
581
|
Sometimes your migration will do something which is just plain irreversible; for
|
540
582
|
example, it might destroy some data. In such cases, you can raise
|
@@ -557,16 +599,15 @@ made in the `up` method. The example in the `reversible` section is equivalent t
|
|
557
599
|
```ruby
|
558
600
|
class ExampleMigration < ActiveRecord::Migration
|
559
601
|
def up
|
560
|
-
create_table :
|
561
|
-
t.
|
602
|
+
create_table :distributors do |t|
|
603
|
+
t.string :zipcode
|
562
604
|
end
|
563
605
|
|
564
|
-
# add a
|
606
|
+
# add a CHECK constraint
|
565
607
|
execute <<-SQL
|
566
|
-
ALTER TABLE
|
567
|
-
ADD CONSTRAINT
|
568
|
-
|
569
|
-
REFERENCES categories(id)
|
608
|
+
ALTER TABLE distributors
|
609
|
+
ADD CONSTRAINT zipchk
|
610
|
+
CHECK (char_length(zipcode) = 5);
|
570
611
|
SQL
|
571
612
|
|
572
613
|
add_column :users, :home_page_url, :string
|
@@ -578,11 +619,11 @@ class ExampleMigration < ActiveRecord::Migration
|
|
578
619
|
remove_column :users, :home_page_url
|
579
620
|
|
580
621
|
execute <<-SQL
|
581
|
-
ALTER TABLE
|
582
|
-
DROP
|
622
|
+
ALTER TABLE distributors
|
623
|
+
DROP CONSTRAINT zipchk
|
583
624
|
SQL
|
584
625
|
|
585
|
-
drop_table :
|
626
|
+
drop_table :distributors
|
586
627
|
end
|
587
628
|
end
|
588
629
|
```
|
@@ -613,43 +654,27 @@ end
|
|
613
654
|
The `revert` method also accepts a block of instructions to reverse.
|
614
655
|
This could be useful to revert selected parts of previous migrations.
|
615
656
|
For example, let's imagine that `ExampleMigration` is committed and it
|
616
|
-
is later decided it would be best to
|
617
|
-
|
657
|
+
is later decided it would be best to use Active Record validations,
|
658
|
+
in place of the `CHECK` constraint, to verify the zipcode.
|
618
659
|
|
619
660
|
```ruby
|
620
|
-
class
|
661
|
+
class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration
|
621
662
|
def change
|
622
|
-
add_column :categories, :product_list
|
623
|
-
|
624
|
-
reversible do |dir|
|
625
|
-
dir.up do
|
626
|
-
# transfer data from Products to Category#product_list
|
627
|
-
end
|
628
|
-
dir.down do
|
629
|
-
# create Products from Category#product_list
|
630
|
-
end
|
631
|
-
end
|
632
|
-
|
633
663
|
revert do
|
634
664
|
# copy-pasted code from ExampleMigration
|
635
|
-
create_table :products do |t|
|
636
|
-
t.references :category
|
637
|
-
end
|
638
|
-
|
639
665
|
reversible do |dir|
|
640
666
|
dir.up do
|
641
|
-
#add a
|
667
|
+
# add a CHECK constraint
|
642
668
|
execute <<-SQL
|
643
|
-
ALTER TABLE
|
644
|
-
ADD CONSTRAINT
|
645
|
-
|
646
|
-
REFERENCES categories(id)
|
669
|
+
ALTER TABLE distributors
|
670
|
+
ADD CONSTRAINT zipchk
|
671
|
+
CHECK (char_length(zipcode) = 5);
|
647
672
|
SQL
|
648
673
|
end
|
649
674
|
dir.down do
|
650
675
|
execute <<-SQL
|
651
|
-
ALTER TABLE
|
652
|
-
DROP
|
676
|
+
ALTER TABLE distributors
|
677
|
+
DROP CONSTRAINT zipchk
|
653
678
|
SQL
|
654
679
|
end
|
655
680
|
end
|
@@ -853,159 +878,6 @@ The `revert` method can be helpful when writing a new migration to undo
|
|
853
878
|
previous migrations in whole or in part
|
854
879
|
(see [Reverting Previous Migrations](#reverting-previous-migrations) above).
|
855
880
|
|
856
|
-
Using Models in Your Migrations
|
857
|
-
-------------------------------
|
858
|
-
|
859
|
-
When creating or updating data in a migration it is often tempting to use one
|
860
|
-
of your models. After all, they exist to provide easy access to the underlying
|
861
|
-
data. This can be done, but some caution should be observed.
|
862
|
-
|
863
|
-
For example, problems occur when the model uses database columns which are (1)
|
864
|
-
not currently in the database and (2) will be created by this or a subsequent
|
865
|
-
migration.
|
866
|
-
|
867
|
-
Consider this example, where Alice and Bob are working on the same code base
|
868
|
-
which contains a `Product` model:
|
869
|
-
|
870
|
-
Bob goes on vacation.
|
871
|
-
|
872
|
-
Alice creates a migration for the `products` table which adds a new column and
|
873
|
-
initializes it:
|
874
|
-
|
875
|
-
```ruby
|
876
|
-
# db/migrate/20100513121110_add_flag_to_product.rb
|
877
|
-
|
878
|
-
class AddFlagToProduct < ActiveRecord::Migration
|
879
|
-
def change
|
880
|
-
add_column :products, :flag, :boolean
|
881
|
-
reversible do |dir|
|
882
|
-
dir.up { Product.update_all flag: false }
|
883
|
-
end
|
884
|
-
end
|
885
|
-
end
|
886
|
-
```
|
887
|
-
|
888
|
-
She also adds a validation to the `Product` model for the new column:
|
889
|
-
|
890
|
-
```ruby
|
891
|
-
# app/models/product.rb
|
892
|
-
|
893
|
-
class Product < ActiveRecord::Base
|
894
|
-
validates :flag, inclusion: { in: [true, false] }
|
895
|
-
end
|
896
|
-
```
|
897
|
-
|
898
|
-
Alice adds a second migration which adds another column to the `products`
|
899
|
-
table and initializes it:
|
900
|
-
|
901
|
-
```ruby
|
902
|
-
# db/migrate/20100515121110_add_fuzz_to_product.rb
|
903
|
-
|
904
|
-
class AddFuzzToProduct < ActiveRecord::Migration
|
905
|
-
def change
|
906
|
-
add_column :products, :fuzz, :string
|
907
|
-
reversible do |dir|
|
908
|
-
dir.up { Product.update_all fuzz: 'fuzzy' }
|
909
|
-
end
|
910
|
-
end
|
911
|
-
end
|
912
|
-
```
|
913
|
-
|
914
|
-
She also adds a validation to the `Product` model for the new column:
|
915
|
-
|
916
|
-
```ruby
|
917
|
-
# app/models/product.rb
|
918
|
-
|
919
|
-
class Product < ActiveRecord::Base
|
920
|
-
validates :flag, inclusion: { in: [true, false] }
|
921
|
-
validates :fuzz, presence: true
|
922
|
-
end
|
923
|
-
```
|
924
|
-
|
925
|
-
Both migrations work for Alice.
|
926
|
-
|
927
|
-
Bob comes back from vacation and:
|
928
|
-
|
929
|
-
* Updates the source - which contains both migrations and the latest version
|
930
|
-
of the Product model.
|
931
|
-
* Runs outstanding migrations with `rake db:migrate`, which
|
932
|
-
includes the one that updates the `Product` model.
|
933
|
-
|
934
|
-
The migration crashes because when the model attempts to save, it tries to
|
935
|
-
validate the second added column, which is not in the database when the _first_
|
936
|
-
migration runs:
|
937
|
-
|
938
|
-
```
|
939
|
-
rake aborted!
|
940
|
-
An error has occurred, this and all later migrations canceled:
|
941
|
-
|
942
|
-
undefined method `fuzz' for #<Product:0x000001049b14a0>
|
943
|
-
```
|
944
|
-
|
945
|
-
A fix for this is to create a local model within the migration. This keeps
|
946
|
-
Rails from running the validations, so that the migrations run to completion.
|
947
|
-
|
948
|
-
When using a local model, it's a good idea to call
|
949
|
-
`Product.reset_column_information` to refresh the Active Record cache for the
|
950
|
-
`Product` model prior to updating data in the database.
|
951
|
-
|
952
|
-
If Alice had done this instead, there would have been no problem:
|
953
|
-
|
954
|
-
```ruby
|
955
|
-
# db/migrate/20100513121110_add_flag_to_product.rb
|
956
|
-
|
957
|
-
class AddFlagToProduct < ActiveRecord::Migration
|
958
|
-
class Product < ActiveRecord::Base
|
959
|
-
end
|
960
|
-
|
961
|
-
def change
|
962
|
-
add_column :products, :flag, :boolean
|
963
|
-
Product.reset_column_information
|
964
|
-
reversible do |dir|
|
965
|
-
dir.up { Product.update_all flag: false }
|
966
|
-
end
|
967
|
-
end
|
968
|
-
end
|
969
|
-
```
|
970
|
-
|
971
|
-
```ruby
|
972
|
-
# db/migrate/20100515121110_add_fuzz_to_product.rb
|
973
|
-
|
974
|
-
class AddFuzzToProduct < ActiveRecord::Migration
|
975
|
-
class Product < ActiveRecord::Base
|
976
|
-
end
|
977
|
-
|
978
|
-
def change
|
979
|
-
add_column :products, :fuzz, :string
|
980
|
-
Product.reset_column_information
|
981
|
-
reversible do |dir|
|
982
|
-
dir.up { Product.update_all fuzz: 'fuzzy' }
|
983
|
-
end
|
984
|
-
end
|
985
|
-
end
|
986
|
-
```
|
987
|
-
|
988
|
-
There are other ways in which the above example could have gone badly.
|
989
|
-
|
990
|
-
For example, imagine that Alice creates a migration that selectively
|
991
|
-
updates the `description` field on certain products. She runs the
|
992
|
-
migration, commits the code, and then begins working on the next feature,
|
993
|
-
which is to add a new column `fuzz` to the products table.
|
994
|
-
|
995
|
-
She creates two migrations for this new feature, one which adds the new
|
996
|
-
column, and a second which selectively updates the `fuzz` column based on
|
997
|
-
other product attributes.
|
998
|
-
|
999
|
-
These migrations run just fine, but when Bob comes back from his vacation
|
1000
|
-
and calls `rake db:migrate` to run all the outstanding migrations, he gets a
|
1001
|
-
subtle bug: The descriptions have defaults, and the `fuzz` column is present,
|
1002
|
-
but `fuzz` is `nil` on all products.
|
1003
|
-
|
1004
|
-
The solution is again to use `Product.reset_column_information` before
|
1005
|
-
referencing the Product model in a migration, ensuring the Active Record's
|
1006
|
-
knowledge of the table structure is current before manipulating data in those
|
1007
|
-
records.
|
1008
|
-
|
1009
881
|
Schema Dumping and You
|
1010
882
|
----------------------
|
1011
883
|
|
@@ -1067,10 +939,10 @@ that Active Record supports. This could be very useful if you were to
|
|
1067
939
|
distribute an application that is able to run against multiple databases.
|
1068
940
|
|
1069
941
|
There is however a trade-off: `db/schema.rb` cannot express database specific
|
1070
|
-
items such as
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
942
|
+
items such as triggers, or stored procedures. While in a migration you can
|
943
|
+
execute custom SQL statements, the schema dumper cannot reconstitute those
|
944
|
+
statements from the database. If you are using features like this, then you
|
945
|
+
should set the schema format to `:sql`.
|
1074
946
|
|
1075
947
|
Instead of using Active Record's schema dumper, the database's structure will
|
1076
948
|
be dumped using a tool specific to the database (via the `db:structure:dump`
|
@@ -1088,11 +960,16 @@ schema into a RDBMS other than the one used to create it.
|
|
1088
960
|
Because schema dumps are the authoritative source for your database schema, it
|
1089
961
|
is strongly recommended that you check them into source control.
|
1090
962
|
|
963
|
+
`db/schema.rb` contains the current version number of the database. This
|
964
|
+
ensures conflicts are going to happen in the case of a merge where both
|
965
|
+
branches touched the schema. When that happens, solve conflicts manually,
|
966
|
+
keeping the highest version number of the two.
|
967
|
+
|
1091
968
|
Active Record and Referential Integrity
|
1092
969
|
---------------------------------------
|
1093
970
|
|
1094
971
|
The Active Record way claims that intelligence belongs in your models, not in
|
1095
|
-
the database. As such, features such as triggers or
|
972
|
+
the database. As such, features such as triggers or constraints,
|
1096
973
|
which push some of that intelligence back into the database, are not heavily
|
1097
974
|
used.
|
1098
975
|
|
@@ -1101,14 +978,10 @@ which models can enforce data integrity. The `:dependent` option on
|
|
1101
978
|
associations allows models to automatically destroy child objects when the
|
1102
979
|
parent is destroyed. Like anything which operates at the application level,
|
1103
980
|
these cannot guarantee referential integrity and so some people augment them
|
1104
|
-
with foreign key constraints in the database.
|
1105
|
-
|
1106
|
-
Although Active Record does not provide
|
1107
|
-
such features, the `execute` method can be used to execute arbitrary SQL.
|
1108
|
-
can also use a gem like
|
1109
|
-
[foreigner](https://github.com/matthuhiggins/foreigner) which adds foreign key
|
1110
|
-
support to Active Record (including support for dumping foreign keys in
|
1111
|
-
`db/schema.rb`).
|
981
|
+
with [foreign key constraints](#foreign-keys) in the database.
|
982
|
+
|
983
|
+
Although Active Record does not provide all the tools for working directly with
|
984
|
+
such features, the `execute` method can be used to execute arbitrary SQL.
|
1112
985
|
|
1113
986
|
Migrations and Seed Data
|
1114
987
|
------------------------
|