railties 3.1.12 → 3.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +2292 -41
- data/README.rdoc +14 -5
- data/bin/rails +7 -0
- data/guides/code/getting_started/Gemfile +27 -0
- data/guides/code/getting_started/README.rdoc +261 -0
- data/guides/code/getting_started/Rakefile +7 -0
- data/guides/code/getting_started/app/assets/images/rails.png +0 -0
- data/guides/code/getting_started/app/assets/javascripts/application.js +9 -0
- data/guides/code/getting_started/app/assets/javascripts/comments.js.coffee +3 -0
- data/guides/code/getting_started/app/assets/javascripts/home.js.coffee +3 -0
- data/guides/code/getting_started/app/assets/javascripts/posts.js.coffee +3 -0
- data/guides/code/getting_started/app/assets/stylesheets/application.css +7 -0
- data/guides/code/getting_started/app/assets/stylesheets/comments.css.scss +3 -0
- data/guides/code/getting_started/app/assets/stylesheets/home.css.scss +3 -0
- data/guides/code/getting_started/app/assets/stylesheets/posts.css.scss +3 -0
- data/guides/code/getting_started/app/assets/stylesheets/scaffolds.css.scss +56 -0
- data/guides/code/getting_started/app/controllers/application_controller.rb +3 -0
- data/guides/code/getting_started/app/controllers/comments_controller.rb +16 -0
- data/guides/code/getting_started/app/controllers/home_controller.rb +5 -0
- data/guides/code/getting_started/app/controllers/posts_controller.rb +84 -0
- data/guides/code/getting_started/app/helpers/application_helper.rb +2 -0
- data/guides/code/getting_started/app/helpers/comments_helper.rb +2 -0
- data/guides/code/getting_started/app/helpers/home_helper.rb +2 -0
- data/guides/code/getting_started/app/helpers/posts_helper.rb +5 -0
- data/guides/code/getting_started/app/models/comment.rb +3 -0
- data/guides/code/getting_started/app/models/post.rb +11 -0
- data/guides/code/getting_started/app/models/tag.rb +3 -0
- data/guides/code/getting_started/app/views/comments/_comment.html.erb +15 -0
- data/guides/code/getting_started/app/views/comments/_form.html.erb +13 -0
- data/guides/code/getting_started/app/views/home/index.html.erb +2 -0
- data/guides/code/getting_started/app/views/layouts/application.html.erb +14 -0
- data/guides/code/getting_started/app/views/posts/_form.html.erb +32 -0
- data/guides/code/getting_started/app/views/posts/edit.html.erb +6 -0
- data/guides/code/getting_started/app/views/posts/index.html.erb +27 -0
- data/guides/code/getting_started/app/views/posts/new.html.erb +5 -0
- data/guides/code/getting_started/app/views/posts/show.html.erb +31 -0
- data/guides/code/getting_started/app/views/tags/_form.html.erb +12 -0
- data/guides/code/getting_started/config.ru +4 -0
- data/guides/code/getting_started/config/application.rb +53 -0
- data/guides/code/getting_started/config/boot.rb +6 -0
- data/guides/code/getting_started/config/database.yml +25 -0
- data/guides/code/getting_started/config/environment.rb +5 -0
- data/guides/code/getting_started/config/environments/development.rb +37 -0
- data/guides/code/getting_started/config/environments/production.rb +67 -0
- data/guides/code/getting_started/config/environments/test.rb +37 -0
- data/guides/code/getting_started/config/initializers/backtrace_silencers.rb +7 -0
- data/guides/code/getting_started/config/initializers/inflections.rb +10 -0
- data/guides/code/getting_started/config/initializers/mime_types.rb +5 -0
- data/guides/code/getting_started/config/initializers/secret_token.rb +7 -0
- data/guides/code/getting_started/config/initializers/session_store.rb +8 -0
- data/guides/code/getting_started/config/initializers/wrap_parameters.rb +14 -0
- data/guides/code/getting_started/config/locales/en.yml +5 -0
- data/guides/code/getting_started/config/routes.rb +64 -0
- data/guides/code/getting_started/db/migrate/20110901012504_create_posts.rb +11 -0
- data/guides/code/getting_started/db/migrate/20110901012815_create_comments.rb +12 -0
- data/guides/code/getting_started/db/migrate/20110901013701_create_tags.rb +11 -0
- data/guides/code/getting_started/db/schema.rb +43 -0
- data/guides/code/getting_started/db/seeds.rb +7 -0
- data/guides/code/getting_started/doc/README_FOR_APP +2 -0
- data/guides/code/getting_started/public/404.html +26 -0
- data/guides/code/getting_started/public/422.html +26 -0
- data/guides/code/getting_started/public/500.html +26 -0
- data/guides/code/getting_started/public/favicon.ico +0 -0
- data/guides/code/getting_started/public/robots.txt +5 -0
- data/guides/code/getting_started/script/rails +6 -0
- data/guides/code/getting_started/test/fixtures/comments.yml +11 -0
- data/guides/code/getting_started/test/fixtures/posts.yml +11 -0
- data/guides/code/getting_started/test/fixtures/tags.yml +9 -0
- data/guides/code/getting_started/test/functional/comments_controller_test.rb +7 -0
- data/guides/code/getting_started/test/functional/home_controller_test.rb +9 -0
- data/guides/code/getting_started/test/functional/posts_controller_test.rb +49 -0
- data/guides/code/getting_started/test/performance/browsing_test.rb +12 -0
- data/guides/code/getting_started/test/test_helper.rb +13 -0
- data/guides/code/getting_started/test/unit/comment_test.rb +7 -0
- data/guides/code/getting_started/test/unit/helpers/comments_helper_test.rb +4 -0
- data/guides/code/getting_started/test/unit/helpers/home_helper_test.rb +4 -0
- data/guides/code/getting_started/test/unit/helpers/posts_helper_test.rb +4 -0
- data/guides/code/getting_started/test/unit/post_test.rb +7 -0
- data/guides/code/getting_started/test/unit/tag_test.rb +7 -0
- data/guides/rails_guides/generator.rb +2 -1
- data/guides/source/3_0_release_notes.textile +2 -2
- data/guides/source/3_1_release_notes.textile +3 -110
- data/guides/source/action_controller_overview.textile +11 -13
- data/guides/source/action_mailer_basics.textile +7 -18
- data/guides/source/action_view_overview.textile +78 -9
- data/guides/source/active_model_basics.textile +205 -0
- data/guides/source/active_record_basics.textile +31 -31
- data/guides/source/active_record_querying.textile +288 -67
- data/guides/source/active_record_validations_callbacks.textile +69 -75
- data/guides/source/active_resource_basics.textile +48 -2
- data/guides/source/active_support_core_extensions.textile +145 -24
- data/guides/source/ajax_on_rails.textile +65 -7
- data/guides/source/api_documentation_guidelines.textile +0 -6
- data/guides/source/asset_pipeline.textile +2 -2
- data/guides/source/association_basics.textile +25 -34
- data/guides/source/caching_with_rails.textile +12 -17
- data/guides/source/command_line.textile +29 -19
- data/guides/source/configuring.textile +40 -18
- data/guides/source/contributing_to_ruby_on_rails.textile +11 -18
- data/guides/source/debugging_rails_applications.textile +10 -21
- data/guides/source/engines.textile +618 -0
- data/guides/source/form_helpers.textile +1 -12
- data/guides/source/generators.textile +9 -11
- data/guides/source/getting_started.textile +152 -152
- data/guides/source/i18n.textile +4 -5
- data/guides/source/index.html.erb +0 -1
- data/guides/source/initialization.textile +26 -26
- data/guides/source/layouts_and_rendering.textile +97 -61
- data/guides/source/migrations.textile +380 -161
- data/guides/source/performance_testing.textile +4 -10
- data/guides/source/plugins.textile +11 -19
- data/guides/source/rails_application_templates.textile +12 -4
- data/guides/source/rails_on_rack.textile +25 -19
- data/guides/source/routing.textile +6 -13
- data/guides/source/ruby_on_rails_guides_guidelines.textile +0 -5
- data/guides/source/security.textile +11 -15
- data/guides/source/testing.textile +1 -9
- data/lib/rails/application.rb +107 -42
- data/lib/rails/application/bootstrap.rb +12 -11
- data/lib/rails/application/configuration.rb +27 -21
- data/lib/rails/application/finisher.rb +40 -17
- data/lib/rails/application/route_inspector.rb +75 -0
- data/lib/rails/application/routes_reloader.rb +15 -4
- data/lib/rails/code_statistics.rb +16 -5
- data/lib/rails/commands.rb +6 -5
- data/lib/rails/commands/application.rb +8 -1
- data/lib/rails/commands/console.rb +2 -0
- data/lib/rails/commands/dbconsole.rb +2 -2
- data/lib/rails/commands/destroy.rb +0 -2
- data/lib/rails/commands/generate.rb +3 -3
- data/lib/rails/commands/plugin.rb +161 -159
- data/lib/rails/commands/plugin_new.rb +3 -2
- data/lib/rails/commands/runner.rb +4 -0
- data/lib/rails/console/app.rb +26 -22
- data/lib/rails/console/helpers.rb +9 -5
- data/lib/rails/engine.rb +70 -34
- data/lib/rails/engine/commands.rb +39 -0
- data/lib/rails/engine/configuration.rb +1 -1
- data/lib/rails/generators.rb +3 -14
- data/lib/rails/generators/actions.rb +36 -9
- data/lib/rails/generators/app_base.rb +34 -38
- data/lib/rails/generators/base.rb +4 -4
- data/lib/rails/generators/generated_attribute.rb +1 -1
- data/lib/rails/generators/named_base.rb +1 -3
- data/lib/rails/generators/rails/app/USAGE +6 -0
- data/lib/rails/generators/rails/app/app_generator.rb +6 -2
- data/lib/rails/generators/rails/app/templates/Gemfile +4 -3
- data/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt +9 -3
- data/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css +11 -5
- 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 +1 -1
- data/lib/rails/generators/rails/app/templates/config/application.rb +11 -0
- data/lib/rails/generators/rails/app/templates/config/databases/jdbcpostgresql.yml +1 -1
- data/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml +1 -1
- data/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +10 -1
- data/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +10 -1
- data/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +6 -6
- data/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb +5 -0
- data/lib/rails/generators/rails/app/templates/config/routes.rb +1 -1
- data/lib/rails/generators/rails/app/templates/public/500.html +0 -1
- data/lib/rails/generators/rails/app/templates/public/index.html +1 -1
- 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/unit/.empty_directory +0 -0
- data/lib/rails/generators/rails/controller/templates/controller.rb +1 -1
- data/lib/rails/generators/rails/generator/templates/templates/.empty_directory +0 -0
- data/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +17 -5
- data/lib/rails/generators/rails/plugin_new/templates/Rakefile +1 -0
- data/lib/rails/generators/rails/plugin_new/templates/app/mailers/.empty_directory +0 -0
- data/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory +0 -0
- data/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt +1 -1
- data/lib/rails/generators/rails/plugin_new/templates/gitignore +4 -3
- data/lib/rails/generators/rails/plugin_new/templates/lib/%name%/engine.rb +1 -1
- data/lib/rails/generators/rails/plugin_new/templates/rails/application.rb +1 -1
- data/lib/rails/generators/rails/plugin_new/templates/script/rails.tt +5 -3
- data/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +2 -2
- data/lib/rails/generators/rails/task/USAGE +9 -0
- data/lib/rails/generators/rails/task/task_generator.rb +12 -0
- data/lib/rails/generators/rails/task/templates/task.rb +8 -0
- data/lib/rails/generators/resource_helpers.rb +3 -3
- data/lib/rails/generators/test_unit/integration/templates/integration_test.rb +0 -2
- data/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +4 -4
- data/lib/rails/paths.rb +11 -38
- data/lib/rails/rack/debugger.rb +3 -4
- data/lib/rails/rack/logger.rb +26 -12
- data/lib/rails/railtie.rb +6 -1
- data/lib/rails/railtie/configuration.rb +12 -5
- data/lib/rails/source_annotation_extractor.rb +12 -10
- data/lib/rails/tasks/documentation.rake +3 -1
- data/lib/rails/tasks/engine.rake +1 -0
- data/lib/rails/tasks/misc.rake +1 -1
- data/lib/rails/tasks/routes.rake +3 -23
- data/lib/rails/test_help.rb +1 -2
- data/lib/rails/test_unit/testing.rake +8 -4
- data/lib/rails/version.rb +3 -3
- metadata +131 -61
- checksums.yaml +0 -7
- data/lib/rails/generators/rails/plugin/USAGE +0 -13
- data/lib/rails/generators/rails/plugin/plugin_generator.rb +0 -54
- data/lib/rails/generators/rails/plugin/templates/MIT-LICENSE.tt +0 -20
- data/lib/rails/generators/rails/plugin/templates/README.tt +0 -13
- data/lib/rails/generators/rails/plugin/templates/Rakefile.tt +0 -23
- data/lib/rails/generators/rails/plugin/templates/init.rb +0 -1
- data/lib/rails/generators/rails/plugin/templates/install.rb +0 -1
- data/lib/rails/generators/rails/plugin/templates/lib/%file_name%.rb.tt +0 -1
- data/lib/rails/generators/rails/plugin/templates/lib/tasks/%file_name%_tasks.rake.tt +0 -4
- data/lib/rails/generators/rails/plugin/templates/uninstall.rb +0 -1
@@ -8,12 +8,13 @@ This guide covers different ways to retrieve data from the database using Active
|
|
8
8
|
* Use dynamic finders methods
|
9
9
|
* Check for the existence of particular records
|
10
10
|
* Perform various calculations on Active Record models
|
11
|
+
* Run EXPLAIN on relations
|
11
12
|
|
12
13
|
endprologue.
|
13
14
|
|
14
15
|
WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in other versions of Rails.
|
15
16
|
|
16
|
-
If you're used to using raw SQL to find database records then
|
17
|
+
If you're used to using raw SQL to find database records, then you will generally find that there are better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases.
|
17
18
|
|
18
19
|
Code examples throughout this guide will refer to one or more of the following models:
|
19
20
|
|
@@ -69,28 +70,28 @@ The methods are:
|
|
69
70
|
|
70
71
|
All of the above methods return an instance of <tt>ActiveRecord::Relation</tt>.
|
71
72
|
|
72
|
-
|
73
|
+
The primary operation of <tt>Model.find(options)</tt> can be summarized as:
|
73
74
|
|
74
75
|
* Convert the supplied options to an equivalent SQL query.
|
75
76
|
* Fire the SQL query and retrieve the corresponding results from the database.
|
76
77
|
* Instantiate the equivalent Ruby object of the appropriate model for every resulting row.
|
77
|
-
* Run +after_find+ callbacks if any.
|
78
|
+
* Run +after_find+ callbacks, if any.
|
78
79
|
|
79
80
|
h4. Retrieving a Single Object
|
80
81
|
|
81
|
-
Active Record
|
82
|
+
Active Record provides five different ways of retrieving a single object.
|
82
83
|
|
83
84
|
h5. Using a Primary Key
|
84
85
|
|
85
|
-
Using <tt>Model.find(primary_key)</tt>, you can retrieve the object corresponding to the
|
86
|
+
Using <tt>Model.find(primary_key)</tt>, you can retrieve the object corresponding to the specified _primary key_ that matches any supplied options. For example:
|
86
87
|
|
87
88
|
<ruby>
|
88
89
|
# Find the client with primary key (id) 10.
|
89
90
|
client = Client.find(10)
|
90
|
-
=> #<Client id: 10, first_name:
|
91
|
+
# => #<Client id: 10, first_name: "Ryan">
|
91
92
|
</ruby>
|
92
93
|
|
93
|
-
SQL equivalent of the above is:
|
94
|
+
The SQL equivalent of the above is:
|
94
95
|
|
95
96
|
<sql>
|
96
97
|
SELECT * FROM clients WHERE (clients.id = 10)
|
@@ -100,14 +101,14 @@ SELECT * FROM clients WHERE (clients.id = 10)
|
|
100
101
|
|
101
102
|
h5. +first+
|
102
103
|
|
103
|
-
<tt>Model.first</tt> finds the first record matched by the supplied options. For example:
|
104
|
+
<tt>Model.first</tt> finds the first record matched by the supplied options, if any. For example:
|
104
105
|
|
105
106
|
<ruby>
|
106
107
|
client = Client.first
|
107
|
-
=> #<Client id: 1, first_name: "Lifo">
|
108
|
+
# => #<Client id: 1, first_name: "Lifo">
|
108
109
|
</ruby>
|
109
110
|
|
110
|
-
SQL equivalent of the above is:
|
111
|
+
The SQL equivalent of the above is:
|
111
112
|
|
112
113
|
<sql>
|
113
114
|
SELECT * FROM clients LIMIT 1
|
@@ -121,10 +122,10 @@ h5. +last+
|
|
121
122
|
|
122
123
|
<ruby>
|
123
124
|
client = Client.last
|
124
|
-
=> #<Client id: 221, first_name: "Russel">
|
125
|
+
# => #<Client id: 221, first_name: "Russel">
|
125
126
|
</ruby>
|
126
127
|
|
127
|
-
SQL equivalent of the above is:
|
128
|
+
The SQL equivalent of the above is:
|
128
129
|
|
129
130
|
<sql>
|
130
131
|
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
|
@@ -132,16 +133,16 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
|
|
132
133
|
|
133
134
|
<tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised.
|
134
135
|
|
135
|
-
h5. +first!+
|
136
|
+
h5(#first_1). +first!+
|
136
137
|
|
137
138
|
<tt>Model.first!</tt> finds the first record. For example:
|
138
139
|
|
139
140
|
<ruby>
|
140
141
|
client = Client.first!
|
141
|
-
=> #<Client id: 1, first_name: "Lifo">
|
142
|
+
# => #<Client id: 1, first_name: "Lifo">
|
142
143
|
</ruby>
|
143
144
|
|
144
|
-
SQL equivalent of the above is:
|
145
|
+
The SQL equivalent of the above is:
|
145
146
|
|
146
147
|
<sql>
|
147
148
|
SELECT * FROM clients LIMIT 1
|
@@ -149,16 +150,16 @@ SELECT * FROM clients LIMIT 1
|
|
149
150
|
|
150
151
|
<tt>Model.first!</tt> raises +RecordNotFound+ if no matching record is found.
|
151
152
|
|
152
|
-
h5. +last!+
|
153
|
+
h5(#last_1). +last!+
|
153
154
|
|
154
155
|
<tt>Model.last!</tt> finds the last record. For example:
|
155
156
|
|
156
157
|
<ruby>
|
157
158
|
client = Client.last!
|
158
|
-
=> #<Client id: 221, first_name: "Russel">
|
159
|
+
# => #<Client id: 221, first_name: "Russel">
|
159
160
|
</ruby>
|
160
161
|
|
161
|
-
SQL equivalent of the above is:
|
162
|
+
The SQL equivalent of the above is:
|
162
163
|
|
163
164
|
<sql>
|
164
165
|
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
|
@@ -170,15 +171,15 @@ h4. Retrieving Multiple Objects
|
|
170
171
|
|
171
172
|
h5. Using Multiple Primary Keys
|
172
173
|
|
173
|
-
<tt>Model.find(array_of_primary_key)</tt>
|
174
|
+
<tt>Model.find(array_of_primary_key)</tt> accepts an array of _primary keys_, returning an array containing all of the matching records for the supplied _primary keys_. For example:
|
174
175
|
|
175
176
|
<ruby>
|
176
177
|
# Find the clients with primary keys 1 and 10.
|
177
|
-
client = Client.find(1, 10) # Or even Client.find(
|
178
|
-
=> [#<Client id: 1, first_name:
|
178
|
+
client = Client.find([1, 10]) # Or even Client.find(1, 10)
|
179
|
+
# => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, first_name: "Ryan">]
|
179
180
|
</ruby>
|
180
181
|
|
181
|
-
SQL equivalent of the above is:
|
182
|
+
The SQL equivalent of the above is:
|
182
183
|
|
183
184
|
<sql>
|
184
185
|
SELECT * FROM clients WHERE (clients.id IN (1,10))
|
@@ -188,24 +189,26 @@ WARNING: <tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord::
|
|
188
189
|
|
189
190
|
h4. Retrieving Multiple Objects in Batches
|
190
191
|
|
191
|
-
|
192
|
+
We often need to iterate over a large set of records, as when we send a newsletter to a large set of users, or when we export data.
|
192
193
|
|
193
|
-
|
194
|
+
This may appear straightforward:
|
194
195
|
|
195
196
|
<ruby>
|
196
|
-
#
|
197
|
+
# This is very inefficient when the users table has thousands of rows.
|
197
198
|
User.all.each do |user|
|
198
199
|
NewsLetter.weekly_deliver(user)
|
199
200
|
end
|
200
201
|
</ruby>
|
201
202
|
|
202
|
-
But
|
203
|
+
But this approach becomes increasingly impractical as the table size increases, since +User.all.each+ instructs Active Record to fetch _the entire table_ in a single pass, build a model object per row, and then keep the entire array of model objects in memory. Indeed, if we have a large number of records, the entire collection may exceed the amount of memory available.
|
203
204
|
|
204
|
-
|
205
|
+
Rails provides two methods that address this problem by dividing records into memory-friendly batches for processing. The first method, +find_each+, retrieves a batch of records and then yields _each_ record to the block individually as a model. The second method, +find_in_batches+, retrieves a batch of records and then yields _the entire batch_ to the block as an array of models.
|
206
|
+
|
207
|
+
TIP: The +find_each+ and +find_in_batches+ methods are intended for use in the batch processing of a large number of records that wouldn't fit in memory all at once. If you just need to loop over a thousand records the regular find methods are the preferred option.
|
205
208
|
|
206
209
|
h5. +find_each+
|
207
210
|
|
208
|
-
|
211
|
+
The +find_each+ method retrieves a batch of records and then yields _each_ record to the block individually as a model. In the following example, +find_each+ will retrieve 1000 records (the current default for both +find_each+ and +find_in_batches+) and then yield each record individually to the block as a model. This process is repeated until all of the records have been processed:
|
209
212
|
|
210
213
|
<ruby>
|
211
214
|
User.find_each do |user|
|
@@ -213,11 +216,15 @@ User.find_each do |user|
|
|
213
216
|
end
|
214
217
|
</ruby>
|
215
218
|
|
216
|
-
|
219
|
+
h6. Options for +find_each+
|
220
|
+
|
221
|
+
The +find_each+ method accepts most of the options allowed by the regular +find+ method, except for +:order+ and +:limit+, which are reserved for internal use by +find_each+.
|
217
222
|
|
218
|
-
|
223
|
+
Two additional options, +:batch_size+ and +:start+, are available as well.
|
219
224
|
|
220
|
-
|
225
|
+
*+:batch_size+*
|
226
|
+
|
227
|
+
The +:batch_size+ option allows you to specify the number of records to be retrieved in each batch, before being passed individually to the block. For example, to retrieve records in batches of 5000:
|
221
228
|
|
222
229
|
<ruby>
|
223
230
|
User.find_each(:batch_size => 5000) do |user|
|
@@ -225,34 +232,38 @@ User.find_each(:batch_size => 5000) do |user|
|
|
225
232
|
end
|
226
233
|
</ruby>
|
227
234
|
|
228
|
-
|
235
|
+
*+:start+*
|
229
236
|
|
230
|
-
|
237
|
+
By default, records are fetched in ascending order of the primary key, which must be an integer. The +:start+ option allows you to configure the first ID of the sequence whenever the lowest ID is not the one you need. This would be useful, for example, if you wanted to resume an interrupted batch process, provided you saved the last processed ID as a checkpoint.
|
231
238
|
|
232
|
-
|
239
|
+
For example, to send newsletters only to users with the primary key starting from 2000, and to retrieve them in batches of 5000:
|
233
240
|
|
234
241
|
<ruby>
|
235
|
-
User.find_each(:
|
242
|
+
User.find_each(:start => 2000, :batch_size => 5000) do |user|
|
236
243
|
NewsLetter.weekly_deliver(user)
|
237
244
|
end
|
238
245
|
</ruby>
|
239
246
|
|
240
|
-
|
247
|
+
Another example would be if you wanted multiple workers handling the same processing queue. You could have each worker handle 10000 records by setting the appropriate <tt>:start</tt> option on each worker.
|
241
248
|
|
242
|
-
|
249
|
+
NOTE: The +:include+ option allows you to name associations that should be loaded alongside with the models.
|
243
250
|
|
244
251
|
h5. +find_in_batches+
|
245
252
|
|
246
|
-
|
253
|
+
The +find_in_batches+ method is similar to +find_each+, since both retrieve batches of records. The difference is that +find_in_batches+ yields _batches_ to the block as an array of models, instead of individually. The following example will yield to the supplied block an array of up to 1000 invoices at a time, with the final block containing any remaining invoices:
|
247
254
|
|
248
255
|
<ruby>
|
249
|
-
#
|
256
|
+
# Give add_invoices an array of 1000 invoices at a time
|
250
257
|
Invoice.find_in_batches(:include => :invoice_lines) do |invoices|
|
251
258
|
export.add_invoices(invoices)
|
252
259
|
end
|
253
260
|
</ruby>
|
254
261
|
|
255
|
-
The
|
262
|
+
NOTE: The +:include+ option allows you to name associations that should be loaded alongside with the models.
|
263
|
+
|
264
|
+
h6. Options for +find_in_batches+
|
265
|
+
|
266
|
+
The +find_in_batches+ method accepts the same +:batch_size+ and +:start+ options as +find_each+, as well as most of the options allowed by the regular +find+ method, except for +:order+ and +:limit+, which are reserved for internal use by +find_in_batches+.
|
256
267
|
|
257
268
|
h3. Conditions
|
258
269
|
|
@@ -266,7 +277,7 @@ WARNING: Building your own conditions as pure strings can leave you vulnerable t
|
|
266
277
|
|
267
278
|
h4. Array Conditions
|
268
279
|
|
269
|
-
Now what if that number could vary, say as an argument from somewhere? The find then
|
280
|
+
Now what if that number could vary, say as an argument from somewhere? The find would then take the form:
|
270
281
|
|
271
282
|
<ruby>
|
272
283
|
Client.where("orders_count = ?", params[:orders])
|
@@ -274,7 +285,7 @@ Client.where("orders_count = ?", params[:orders])
|
|
274
285
|
|
275
286
|
Active Record will go through the first element in the conditions value and any additional elements will replace the question marks +(?)+ in the first element.
|
276
287
|
|
277
|
-
|
288
|
+
If you want to specify multiple conditions:
|
278
289
|
|
279
290
|
<ruby>
|
280
291
|
Client.where("orders_count = ? AND locked = ?", params[:orders], false)
|
@@ -282,19 +293,19 @@ Client.where("orders_count = ? AND locked = ?", params[:orders], false)
|
|
282
293
|
|
283
294
|
In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with the SQL representation of +false+, which depends on the adapter.
|
284
295
|
|
285
|
-
|
296
|
+
This code is highly preferable:
|
286
297
|
|
287
298
|
<ruby>
|
288
299
|
Client.where("orders_count = ?", params[:orders])
|
289
300
|
</ruby>
|
290
301
|
|
291
|
-
|
302
|
+
to this code:
|
292
303
|
|
293
304
|
<ruby>
|
294
305
|
Client.where("orders_count = #{params[:orders]}")
|
295
306
|
</ruby>
|
296
307
|
|
297
|
-
|
308
|
+
because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.
|
298
309
|
|
299
310
|
TIP: For more information on the dangers of SQL injection, see the "Ruby on Rails Security Guide":security.html#sql-injection.
|
300
311
|
|
@@ -425,10 +436,26 @@ ActiveModel::MissingAttributeError: missing attribute: <attribute>
|
|
425
436
|
|
426
437
|
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.
|
427
438
|
|
428
|
-
|
439
|
+
If you would like to only grab a single record per unique value in a certain field, you can use +uniq+:
|
429
440
|
|
430
441
|
<ruby>
|
431
|
-
Client.select(
|
442
|
+
Client.select(:name).uniq
|
443
|
+
</ruby>
|
444
|
+
|
445
|
+
This would generate SQL like:
|
446
|
+
|
447
|
+
<sql>
|
448
|
+
SELECT DISTINCT name FROM clients
|
449
|
+
</sql>
|
450
|
+
|
451
|
+
You can also remove the uniqueness constraint:
|
452
|
+
|
453
|
+
<ruby>
|
454
|
+
query = Client.select(:name).uniq
|
455
|
+
# => Returns unique names
|
456
|
+
|
457
|
+
query.uniq(false)
|
458
|
+
# => Returns all names, even if there are duplicates
|
432
459
|
</ruby>
|
433
460
|
|
434
461
|
h3. Limit and Offset
|
@@ -616,7 +643,7 @@ c1.first_name = "Michael"
|
|
616
643
|
c1.save
|
617
644
|
|
618
645
|
c2.name = "should fail"
|
619
|
-
c2.save # Raises
|
646
|
+
c2.save # Raises an ActiveRecord::StaleObjectError
|
620
647
|
</ruby>
|
621
648
|
|
622
649
|
You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, or otherwise apply the business logic needed to resolve the conflict.
|
@@ -731,7 +758,7 @@ SELECT categories.* FROM categories
|
|
731
758
|
INNER JOIN posts ON posts.category_id = categories.id
|
732
759
|
</sql>
|
733
760
|
|
734
|
-
Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:post).select("distinct(categories.id)").
|
761
|
+
Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:post).select("distinct(categories.id)").
|
735
762
|
|
736
763
|
h5. Joining Multiple Associations
|
737
764
|
|
@@ -911,14 +938,14 @@ end
|
|
911
938
|
To call this +published+ scope we can call it on either the class:
|
912
939
|
|
913
940
|
<ruby>
|
914
|
-
Post.published => [published posts]
|
941
|
+
Post.published # => [published posts]
|
915
942
|
</ruby>
|
916
943
|
|
917
944
|
Or on an association consisting of +Post+ objects:
|
918
945
|
|
919
946
|
<ruby>
|
920
947
|
category = Category.first
|
921
|
-
category.posts.published => [published posts belonging to this category]
|
948
|
+
category.posts.published # => [published posts belonging to this category]
|
922
949
|
</ruby>
|
923
950
|
|
924
951
|
h4. Working with times
|
@@ -939,7 +966,7 @@ When a +lambda+ is used for a +scope+, it can take arguments:
|
|
939
966
|
|
940
967
|
<ruby>
|
941
968
|
class Post < ActiveRecord::Base
|
942
|
-
scope :1_week_before, lambda { |time| where("created_at < ?", time)
|
969
|
+
scope :1_week_before, lambda { |time| where("created_at < ?", time)
|
943
970
|
end
|
944
971
|
</ruby>
|
945
972
|
|
@@ -1014,27 +1041,90 @@ You can also use +find_last_by_*+ methods which will find the last record matchi
|
|
1014
1041
|
|
1015
1042
|
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")+
|
1016
1043
|
|
1017
|
-
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)+.
|
1044
|
+
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)+.
|
1018
1045
|
|
1019
1046
|
WARNING: Up to and including Rails 3.1, when the number of arguments passed to a dynamic finder method is lesser than the number of fields, say <tt>Client.find_by_name_and_locked("Ryan")</tt>, the behavior is to pass +nil+ as the missing argument. This is *unintentional* and this behavior will be changed in Rails 3.2 to throw an +ArgumentError+.
|
1020
1047
|
|
1021
|
-
|
1048
|
+
h3. Find or build a new object
|
1049
|
+
|
1050
|
+
It's common that you need to find a record or create it if it doesn't exist. You can do that with the +first_or_create+ and +first_or_create!+ methods.
|
1051
|
+
|
1052
|
+
h4. +first_or_create+
|
1053
|
+
|
1054
|
+
The +first_or_create+ method checks whether +first+ returns +nil+ or not. If it does return +nil+, then +create+ is called. This is very powerful when coupled with the +where+ method. Let's see an example.
|
1055
|
+
|
1056
|
+
Suppose you want to find a client named 'Andy', and if there's none, create one and additionally set his +locked+ attribute to false. You can do so by running:
|
1057
|
+
|
1058
|
+
<ruby>
|
1059
|
+
Client.where(:first_name => 'Andy').first_or_create(:locked => false)
|
1060
|
+
# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
|
1061
|
+
</ruby>
|
1062
|
+
|
1063
|
+
The SQL generated by this method looks like this:
|
1022
1064
|
|
1023
1065
|
<sql>
|
1024
|
-
SELECT * FROM clients WHERE (clients.first_name = '
|
1066
|
+
SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1
|
1025
1067
|
BEGIN
|
1026
|
-
INSERT INTO clients (first_name, updated_at,
|
1027
|
-
VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', 0, '0')
|
1068
|
+
INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES ('2011-08-30 05:22:57', 'Andy', 0, NULL, '2011-08-30 05:22:57')
|
1028
1069
|
COMMIT
|
1029
1070
|
</sql>
|
1030
1071
|
|
1031
|
-
+
|
1072
|
+
+first_or_create+ returns either the record that already exists or the new record. In our case, we didn't already have a client named Andy so the record is created and returned.
|
1073
|
+
|
1074
|
+
The new record might not be saved to the database; that depends on whether validations passed or not (just like +create+).
|
1075
|
+
|
1076
|
+
It's also worth noting that +first_or_create+ takes into account the arguments of the +where+ method. In the example above we didn't explicitly pass a +:first_name => 'Andy'+ argument to +first_or_create+. However, that was used when creating the new record because it was already passed before to the +where+ method.
|
1077
|
+
|
1078
|
+
You can do the same with the +find_or_create_by+ method:
|
1032
1079
|
|
1033
1080
|
<ruby>
|
1034
|
-
|
1081
|
+
Client.find_or_create_by_first_name(:first_name => "Andy", :locked => false)
|
1035
1082
|
</ruby>
|
1036
1083
|
|
1037
|
-
|
1084
|
+
This method still works, but it's encouraged to use +first_or_create+ because it's more explicit on which arguments are used to _find_ the record and which are used to _create_, resulting in less confusion overall.
|
1085
|
+
|
1086
|
+
h4. +first_or_create!+
|
1087
|
+
|
1088
|
+
You can also use +first_or_create!+ to raise an exception if the new record is invalid. Validations are not covered on this guide, but let's assume for a moment that you temporarily add
|
1089
|
+
|
1090
|
+
<ruby>
|
1091
|
+
validates :orders_count, :presence => true
|
1092
|
+
</ruby>
|
1093
|
+
|
1094
|
+
to your +Client+ model. If you try to create a new +Client+ without passing an +orders_count+, the record will be invalid and an exception will be raised:
|
1095
|
+
|
1096
|
+
<ruby>
|
1097
|
+
Client.where(:first_name => 'Andy').first_or_create!(:locked => false)
|
1098
|
+
# => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank
|
1099
|
+
</ruby>
|
1100
|
+
|
1101
|
+
h4. +first_or_initialize+
|
1102
|
+
|
1103
|
+
The +first_or_initialize+ method will work just like +first_or_create+ but it will not call +create+ but +new+. This means that a new model instance will be created in memory but won't be saved to the database. Continuing with the +first_or_create+ example, we now want the client named 'Nick':
|
1104
|
+
|
1105
|
+
<ruby>
|
1106
|
+
nick = Client.where(:first_name => 'Nick').first_or_initialize(:locked => false)
|
1107
|
+
# => <Client id: nil, first_name: "Nick", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
|
1108
|
+
|
1109
|
+
nick.persisted?
|
1110
|
+
# => false
|
1111
|
+
|
1112
|
+
nick.new_record?
|
1113
|
+
# => true
|
1114
|
+
</ruby>
|
1115
|
+
|
1116
|
+
Because the object is not yet stored in the database, the SQL generated looks like this:
|
1117
|
+
|
1118
|
+
<sql>
|
1119
|
+
SELECT * FROM clients WHERE (clients.first_name = 'Nick') LIMIT 1
|
1120
|
+
</sql>
|
1121
|
+
|
1122
|
+
When you want to save it to the database, just call +save+:
|
1123
|
+
|
1124
|
+
<ruby>
|
1125
|
+
nick.save
|
1126
|
+
# => true
|
1127
|
+
</ruby>
|
1038
1128
|
|
1039
1129
|
h3. Finding by SQL
|
1040
1130
|
|
@@ -1056,6 +1146,30 @@ h3. +select_all+
|
|
1056
1146
|
Client.connection.select_all("SELECT * FROM clients WHERE id = '1'")
|
1057
1147
|
</ruby>
|
1058
1148
|
|
1149
|
+
h3. +pluck+
|
1150
|
+
|
1151
|
+
<tt>pluck</tt> can be used to query a single column from the underlying table of a model. It accepts a column name as argument and returns an array of values of the specified column with the corresponding data type.
|
1152
|
+
|
1153
|
+
<ruby>
|
1154
|
+
Client.where(:active => true).pluck(:id)
|
1155
|
+
# SELECT id FROM clients WHERE active = 1
|
1156
|
+
|
1157
|
+
Client.uniq.pluck(:role)
|
1158
|
+
# SELECT DISTINCT role FROM clients
|
1159
|
+
</ruby>
|
1160
|
+
|
1161
|
+
+pluck+ makes it possible to replace code like
|
1162
|
+
|
1163
|
+
<ruby>
|
1164
|
+
Client.select(:id).map { |c| c.id }
|
1165
|
+
</ruby>
|
1166
|
+
|
1167
|
+
with
|
1168
|
+
|
1169
|
+
<ruby>
|
1170
|
+
Client.pluck(:id)
|
1171
|
+
</ruby>
|
1172
|
+
|
1059
1173
|
h3. Existence of Objects
|
1060
1174
|
|
1061
1175
|
If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or +false+.
|
@@ -1186,11 +1300,118 @@ Client.sum("orders_count")
|
|
1186
1300
|
|
1187
1301
|
For options, please see the parent section, "Calculations":#calculations.
|
1188
1302
|
|
1189
|
-
h3.
|
1303
|
+
h3. Running EXPLAIN
|
1304
|
+
|
1305
|
+
You can run EXPLAIN on the queries triggered by relations. For example,
|
1306
|
+
|
1307
|
+
<ruby>
|
1308
|
+
User.where(:id => 1).joins(:posts).explain
|
1309
|
+
</ruby>
|
1310
|
+
|
1311
|
+
may yield
|
1312
|
+
|
1313
|
+
<plain>
|
1314
|
+
EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id` WHERE `users`.`id` = 1
|
1315
|
+
<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus>
|
1316
|
+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
1317
|
+
<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus>
|
1318
|
+
| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
|
1319
|
+
| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
1320
|
+
<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------------<plus>
|
1321
|
+
2 rows in set (0.00 sec)
|
1322
|
+
</plain>
|
1323
|
+
|
1324
|
+
under MySQL.
|
1325
|
+
|
1326
|
+
Active Record performs a pretty printing that emulates the one of the database
|
1327
|
+
shells. So, the same query running with the PostgreSQL adapter would yield instead
|
1328
|
+
|
1329
|
+
<plain>
|
1330
|
+
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "users"."id" = 1
|
1331
|
+
QUERY PLAN
|
1332
|
+
------------------------------------------------------------------------------
|
1333
|
+
Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
|
1334
|
+
Join Filter: (posts.user_id = users.id)
|
1335
|
+
-> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
|
1336
|
+
Index Cond: (id = 1)
|
1337
|
+
-> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
|
1338
|
+
Filter: (posts.user_id = 1)
|
1339
|
+
(6 rows)
|
1340
|
+
</plain>
|
1341
|
+
|
1342
|
+
Eager loading may trigger more than one query under the hood, and some queries
|
1343
|
+
may need the results of previous ones. Because of that, +explain+ actually
|
1344
|
+
executes the query, and then asks for the query plans. For example,
|
1345
|
+
|
1346
|
+
<ruby>
|
1347
|
+
User.where(:id => 1).includes(:posts).explain
|
1348
|
+
</ruby>
|
1349
|
+
|
1350
|
+
yields
|
1351
|
+
|
1352
|
+
<plain>
|
1353
|
+
EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
|
1354
|
+
<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus>
|
1355
|
+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
1356
|
+
<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus>
|
1357
|
+
| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
|
1358
|
+
<plus>----<plus>-------------<plus>-------<plus>-------<plus>---------------<plus>---------<plus>---------<plus>-------<plus>------<plus>-------<plus>
|
1359
|
+
1 row in set (0.00 sec)
|
1360
|
+
|
1361
|
+
EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1)
|
1362
|
+
<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus>
|
1363
|
+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|
1364
|
+
<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus>
|
1365
|
+
| 1 | SIMPLE | posts | ALL | NULL | NULL | NULL | NULL | 1 | Using where |
|
1366
|
+
<plus>----<plus>-------------<plus>-------<plus>------<plus>---------------<plus>------<plus>---------<plus>------<plus>------<plus>-------------<plus>
|
1367
|
+
1 row in set (0.00 sec)
|
1368
|
+
</plain>
|
1369
|
+
|
1370
|
+
under MySQL.
|
1371
|
+
|
1372
|
+
h4. Automatic EXPLAIN
|
1373
|
+
|
1374
|
+
Active Record is able to run EXPLAIN automatically on slow queries and log its
|
1375
|
+
output. This feature is controlled by the configuration parameter
|
1376
|
+
|
1377
|
+
<ruby>
|
1378
|
+
config.active_record.auto_explain_threshold_in_seconds
|
1379
|
+
</ruby>
|
1380
|
+
|
1381
|
+
If set to a number, any query exceeding those many seconds will have its EXPLAIN
|
1382
|
+
automatically triggered and logged. In the case of relations, the threshold is
|
1383
|
+
compared to the total time needed to fetch records. So, a relation is seen as a
|
1384
|
+
unit of work, no matter whether the implementation of eager loading involves
|
1385
|
+
several queries under the hood.
|
1386
|
+
|
1387
|
+
A threshold of +nil+ disables automatic EXPLAINs.
|
1388
|
+
|
1389
|
+
The default threshold in development mode is 0.5 seconds, and +nil+ in test and
|
1390
|
+
production modes.
|
1391
|
+
|
1392
|
+
h5. Disabling Automatic EXPLAIN
|
1393
|
+
|
1394
|
+
Automatic EXPLAIN can be selectively silenced with +ActiveRecord::Base.silence_auto_explain+:
|
1395
|
+
|
1396
|
+
<ruby>
|
1397
|
+
ActiveRecord::Base.silence_auto_explain do
|
1398
|
+
# no automatic EXPLAIN is triggered here
|
1399
|
+
end
|
1400
|
+
</ruby>
|
1401
|
+
|
1402
|
+
That may be useful for queries you know are slow but fine, like a heavyweight
|
1403
|
+
report of an admin interface.
|
1404
|
+
|
1405
|
+
As its name suggests, +silence_auto_explain+ only silences automatic EXPLAINs.
|
1406
|
+
Explicit calls to +ActiveRecord::Relation#explain+ run.
|
1407
|
+
|
1408
|
+
h4. Interpreting EXPLAIN
|
1409
|
+
|
1410
|
+
Interpretation of the output of EXPLAIN is beyond the scope of this guide. The
|
1411
|
+
following pointers may be helpful:
|
1412
|
+
|
1413
|
+
* SQLite3: "EXPLAIN QUERY PLAN":http://www.sqlite.org/eqp.html
|
1414
|
+
|
1415
|
+
* MySQL: "EXPLAIN Output Format":http://dev.mysql.com/doc/refman/5.6/en/explain-output.html
|
1190
1416
|
|
1191
|
-
*
|
1192
|
-
* December 23 2010: Add documentation for the +scope+ method. "Ryan Bigg":credits.html#radar
|
1193
|
-
* April 7, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
|
1194
|
-
* February 3, 2010: Update to Rails 3 by "James Miller":credits.html#bensie
|
1195
|
-
* February 7, 2009: Second version by "Pratik":credits.html#lifo
|
1196
|
-
* December 29 2008: Initial version by "Ryan Bigg":credits.html#radar
|
1417
|
+
* PostgreSQL: "Using EXPLAIN":http://www.postgresql.org/docs/current/static/using-explain.html
|