rails 4.0.0 → 4.2.11.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +30 -23
- data/guides/CHANGELOG.md +108 -6
- data/guides/Rakefile +21 -6
- data/guides/assets/images/akshaysurve.jpg +0 -0
- data/guides/assets/images/edge_badge.png +0 -0
- data/guides/assets/images/feature_tile.gif +0 -0
- data/guides/assets/images/footer_tile.gif +0 -0
- data/guides/assets/images/fxn.png +0 -0
- data/guides/assets/images/getting_started/article_with_comments.png +0 -0
- data/guides/assets/images/getting_started/challenge.png +0 -0
- data/guides/assets/images/getting_started/confirm_dialog.png +0 -0
- data/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png +0 -0
- data/guides/assets/images/getting_started/form_with_errors.png +0 -0
- data/guides/assets/images/getting_started/index_action_with_edit_link.png +0 -0
- data/guides/assets/images/getting_started/new_article.png +0 -0
- data/guides/assets/images/getting_started/rails_welcome.png +0 -0
- data/guides/assets/images/getting_started/routing_error_no_controller.png +0 -0
- data/guides/assets/images/getting_started/routing_error_no_route_matches.png +0 -0
- data/guides/assets/images/getting_started/show_action_for_articles.png +0 -0
- data/guides/assets/images/getting_started/template_is_missing_articles_new.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_create_for_articles.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_new_for_articles.png +0 -0
- data/guides/assets/images/header_tile.gif +0 -0
- data/guides/assets/images/icons/README +1 -1
- data/guides/assets/images/icons/callouts/11.png +0 -0
- data/guides/assets/images/icons/callouts/12.png +0 -0
- data/guides/assets/images/icons/callouts/13.png +0 -0
- data/guides/assets/images/icons/callouts/15.png +0 -0
- data/guides/assets/images/icons/caution.png +0 -0
- data/guides/assets/images/icons/example.png +0 -0
- data/guides/assets/images/radar.png +0 -0
- data/guides/assets/images/rails4_features.png +0 -0
- data/guides/assets/images/rails_guides_kindle_cover.jpg +0 -0
- data/guides/assets/images/vijaydev.jpg +0 -0
- data/guides/assets/javascripts/guides.js +36 -34
- data/guides/assets/stylesheets/main.css +6 -2
- data/guides/assets/stylesheets/print.css +1 -1
- data/guides/bug_report_templates/action_controller_gem.rb +47 -0
- data/guides/bug_report_templates/action_controller_master.rb +54 -0
- data/guides/bug_report_templates/active_record_gem.rb +5 -2
- data/guides/bug_report_templates/active_record_master.rb +3 -2
- data/guides/bug_report_templates/generic_gem.rb +15 -0
- data/guides/bug_report_templates/generic_master.rb +26 -0
- data/guides/rails_guides.rb +23 -4
- data/guides/rails_guides/generator.rb +1 -1
- data/guides/rails_guides/helpers.rb +4 -2
- data/guides/rails_guides/levenshtein.rb +27 -21
- data/guides/rails_guides/markdown.rb +11 -7
- data/guides/rails_guides/markdown/renderer.rb +1 -1
- data/guides/source/2_2_release_notes.md +3 -3
- data/guides/source/2_3_release_notes.md +12 -12
- data/guides/source/3_0_release_notes.md +10 -13
- data/guides/source/3_1_release_notes.md +7 -4
- data/guides/source/3_2_release_notes.md +17 -14
- data/guides/source/4_0_release_notes.md +110 -54
- data/guides/source/4_1_release_notes.md +730 -0
- data/guides/source/4_2_release_notes.md +877 -0
- data/guides/source/_license.html.erb +1 -1
- data/guides/source/_welcome.html.erb +6 -2
- data/guides/source/action_controller_overview.md +223 -57
- data/guides/source/action_mailer_basics.md +129 -76
- data/guides/source/action_view_overview.md +247 -246
- data/guides/source/active_job_basics.md +339 -0
- data/guides/source/active_model_basics.md +374 -20
- data/guides/source/active_record_basics.md +46 -45
- data/guides/source/active_record_callbacks.md +83 -28
- data/guides/source/{migrations.md → active_record_migrations.md} +191 -275
- data/guides/source/active_record_postgresql.md +433 -0
- data/guides/source/active_record_querying.md +382 -300
- data/guides/source/active_record_validations.md +64 -55
- data/guides/source/active_support_core_extensions.md +229 -187
- data/guides/source/active_support_instrumentation.md +23 -22
- data/guides/source/api_documentation_guidelines.md +167 -15
- data/guides/source/asset_pipeline.md +768 -294
- data/guides/source/association_basics.md +188 -96
- data/guides/source/autoloading_and_reloading_constants.md +1311 -0
- data/guides/source/caching_with_rails.md +45 -11
- data/guides/source/command_line.md +96 -65
- data/guides/source/configuring.md +404 -70
- data/guides/source/contributing_to_ruby_on_rails.md +270 -130
- data/guides/source/credits.html.erb +7 -3
- data/guides/source/debugging_rails_applications.md +471 -284
- data/guides/source/development_dependencies_install.md +115 -21
- data/guides/source/documents.yaml +31 -9
- data/guides/source/engines.md +737 -291
- data/guides/source/form_helpers.md +137 -89
- data/guides/source/generators.md +60 -28
- data/guides/source/getting_started.md +1007 -596
- data/guides/source/i18n.md +178 -96
- data/guides/source/index.html.erb +2 -1
- data/guides/source/initialization.md +248 -104
- data/guides/source/kindle/toc.html.erb +1 -1
- data/guides/source/layout.html.erb +14 -22
- data/guides/source/layouts_and_rendering.md +78 -46
- data/guides/source/maintenance_policy.md +78 -0
- data/guides/source/nested_model_forms.md +10 -7
- data/guides/source/plugins.md +66 -57
- data/guides/source/rails_application_templates.md +49 -12
- data/guides/source/rails_on_rack.md +50 -60
- data/guides/source/routing.md +190 -139
- data/guides/source/ruby_on_rails_guides_guidelines.md +12 -13
- data/guides/source/security.md +134 -83
- data/guides/source/testing.md +322 -200
- data/guides/source/upgrading_ruby_on_rails.md +834 -37
- data/guides/source/working_with_javascript_in_rails.md +36 -26
- data/guides/w3c_validator.rb +2 -0
- metadata +93 -116
- data/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png +0 -0
- data/guides/assets/images/getting_started/new_post.png +0 -0
- data/guides/assets/images/getting_started/post_with_comments.png +0 -0
- data/guides/assets/images/getting_started/show_action_for_posts.png +0 -0
- data/guides/assets/images/getting_started/template_is_missing_posts_new.png +0 -0
- data/guides/assets/images/getting_started/undefined_method_post_path.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_create_for_posts.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_new_for_posts.png +0 -0
- data/guides/assets/images/jaimeiniesta.jpg +0 -0
- data/guides/code/getting_started/Gemfile +0 -43
- data/guides/code/getting_started/Gemfile.lock +0 -150
- 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 -16
- 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 -17
- data/guides/code/getting_started/app/controllers/posts_controller.rb +0 -47
- 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 -3
- 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 -58
- data/guides/code/getting_started/public/422.html +0 -58
- data/guides/code/getting_started/public/500.html +0 -57
- 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 -15
- data/guides/source/kindle/KINDLE.md +0 -26
@@ -1,2 +1,2 @@
|
|
1
|
-
<p>This work is licensed under a <a href="
|
1
|
+
<p>This work is licensed under a <a href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International</a> License</p>
|
2
2
|
<p>"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.</p>
|
@@ -10,10 +10,14 @@
|
|
10
10
|
</p>
|
11
11
|
<% else %>
|
12
12
|
<p>
|
13
|
-
These are the new guides for Rails
|
13
|
+
These are the new guides for Rails 4.2 based on <a href="https://github.com/rails/rails/tree/<%= @version %>"><%= @version %></a>.
|
14
14
|
These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together.
|
15
15
|
</p>
|
16
16
|
<% end %>
|
17
17
|
<p>
|
18
|
-
The guides for
|
18
|
+
The guides for earlier releases:
|
19
|
+
<a href="http://guides.rubyonrails.org/v4.1/">Rails 4.1</a>,
|
20
|
+
<a href="http://guides.rubyonrails.org/v4.0/">Rails 4.0</a>,
|
21
|
+
<a href="http://guides.rubyonrails.org/v3.2/">Rails 3.2</a>, and
|
22
|
+
<a href="http://guides.rubyonrails.org/v2.3/">Rails 2.3</a>.
|
19
23
|
</p>
|
@@ -34,7 +34,7 @@ The naming convention of controllers in Rails favors pluralization of the last w
|
|
34
34
|
|
35
35
|
Following this convention will allow you to use the default route generators (e.g. `resources`, etc) without needing to qualify each `:path` or `:controller`, and keeps URL and path helpers' usage consistent throughout your application. See [Layouts & Rendering Guide](layouts_and_rendering.html) for more details.
|
36
36
|
|
37
|
-
NOTE: The controller naming convention differs from the naming convention of models, which expected to be named in singular form.
|
37
|
+
NOTE: The controller naming convention differs from the naming convention of models, which are expected to be named in singular form.
|
38
38
|
|
39
39
|
|
40
40
|
Methods and Actions
|
@@ -112,6 +112,10 @@ NOTE: The actual URL in this example will be encoded as "/clients?ids%5b%5d=1&id
|
|
112
112
|
|
113
113
|
The value of `params[:ids]` will now be `["1", "2", "3"]`. Note that parameter values are always strings; Rails makes no attempt to guess or cast the type.
|
114
114
|
|
115
|
+
NOTE: Values such as `[]`, `[nil]` or `[nil, nil, ...]` in `params` are replaced
|
116
|
+
with `nil` for security reasons by default. See [Security Guide](security.html#unsafe-query-generation)
|
117
|
+
for more information.
|
118
|
+
|
115
119
|
To send a hash you include the key name inside the brackets:
|
116
120
|
|
117
121
|
```html
|
@@ -129,7 +133,7 @@ Note that the `params` hash is actually an instance of `ActiveSupport::HashWithI
|
|
129
133
|
|
130
134
|
### JSON parameters
|
131
135
|
|
132
|
-
If you're writing a web service application, you might find yourself more comfortable accepting parameters in JSON format. Rails will automatically convert your parameters into the `params` hash, which you can access as you would normally.
|
136
|
+
If you're writing a web service application, you might find yourself more comfortable accepting parameters in JSON format. If the "Content-Type" header of your request is set to "application/json", Rails will automatically convert your parameters into the `params` hash, which you can access as you would normally.
|
133
137
|
|
134
138
|
So for example, if you are sending this JSON content:
|
135
139
|
|
@@ -160,7 +164,7 @@ NOTE: Support for parsing XML parameters has been extracted into a gem named `ac
|
|
160
164
|
The `params` hash will always contain the `:controller` and `:action` keys, but you should use the methods `controller_name` and `action_name` instead to access these values. Any other parameters defined by the routing, such as `:id` will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the `:status` parameter in a "pretty" URL:
|
161
165
|
|
162
166
|
```ruby
|
163
|
-
|
167
|
+
get '/clients/:status' => 'clients#index', foo: 'bar'
|
164
168
|
```
|
165
169
|
|
166
170
|
In this case, when a user opens the URL `/clients/active`, `params[:status]` will be set to "active". When this route is used, `params[:foo]` will also be set to "bar" just like it was passed in the query string. In the same way `params[:action]` will contain "index".
|
@@ -209,7 +213,7 @@ class PeopleController < ActionController::Base
|
|
209
213
|
# Request reply.
|
210
214
|
def update
|
211
215
|
person = current_account.people.find(params[:id])
|
212
|
-
person.
|
216
|
+
person.update!(person_params)
|
213
217
|
redirect_to person
|
214
218
|
end
|
215
219
|
|
@@ -256,8 +260,8 @@ used:
|
|
256
260
|
params.require(:log_entry).permit!
|
257
261
|
```
|
258
262
|
|
259
|
-
This will mark the `:log_entry` parameters hash and any
|
260
|
-
permitted.
|
263
|
+
This will mark the `:log_entry` parameters hash and any sub-hash of it
|
264
|
+
permitted. Extreme care should be taken when using `permit!` as it
|
261
265
|
will allow all current and future model attributes to be
|
262
266
|
mass-assigned.
|
263
267
|
|
@@ -321,16 +325,16 @@ in mind. It is not meant as a silver bullet to handle all your
|
|
321
325
|
whitelisting problems. However you can easily mix the API with your
|
322
326
|
own code to adapt to your situation.
|
323
327
|
|
324
|
-
Imagine a scenario where you
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
+
Imagine a scenario where you have parameters representing a product
|
329
|
+
name and a hash of arbitrary data associated with that product, and
|
330
|
+
you want to whitelist the product name attribute but also the whole
|
331
|
+
data hash. The strong parameters API doesn't let you directly
|
332
|
+
whitelist the whole of a nested hash with any keys, but you can use
|
333
|
+
the keys of your nested hash to declare what to whitelist:
|
328
334
|
|
329
335
|
```ruby
|
330
336
|
def product_params
|
331
|
-
params.require(:product).permit(:name
|
332
|
-
whitelisted[:data] = params[:product][:data]
|
333
|
-
end
|
337
|
+
params.require(:product).permit(:name, data: params[:product][:data].try(:keys))
|
334
338
|
end
|
335
339
|
```
|
336
340
|
|
@@ -346,11 +350,11 @@ Your application has a session for each user in which you can store small amount
|
|
346
350
|
|
347
351
|
All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
|
348
352
|
|
349
|
-
For most stores, this ID is used to look up the session data on the server, e.g. in a database table. There is one exception, and that is the default and recommended session store - the CookieStore - which stores all session data in the cookie itself (the ID is still available to you if you need it). This has the advantage of being very lightweight and it requires zero setup in a new application in order to use the session. The cookie data is cryptographically signed to make it tamper-proof
|
353
|
+
For most stores, this ID is used to look up the session data on the server, e.g. in a database table. There is one exception, and that is the default and recommended session store - the CookieStore - which stores all session data in the cookie itself (the ID is still available to you if you need it). This has the advantage of being very lightweight and it requires zero setup in a new application in order to use the session. The cookie data is cryptographically signed to make it tamper-proof. And it is also encrypted so anyone with access to it can't read its contents. (Rails will not accept it if it has been edited).
|
350
354
|
|
351
|
-
The CookieStore can store around 4kB of data
|
355
|
+
The CookieStore can store around 4kB of data - much less than the others - but this is usually enough. Storing large amounts of data in the session is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the most common example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error.
|
352
356
|
|
353
|
-
If your user sessions don't store critical data or don't need to be around for long periods (for instance if you just use the flash for messaging), you can consider using ActionDispatch::Session::CacheStore
|
357
|
+
If your user sessions don't store critical data or don't need to be around for long periods (for instance if you just use the flash for messaging), you can consider using `ActionDispatch::Session::CacheStore`. This will store sessions using the cache implementation you have configured for your application. The advantage of this is that you can use your existing cache infrastructure for storing sessions without requiring any additional setup or administration. The downside, of course, is that the sessions will be ephemeral and could disappear at any time.
|
354
358
|
|
355
359
|
Read more about session storage in the [Security Guide](security.html).
|
356
360
|
|
@@ -360,33 +364,48 @@ If you need a different session storage mechanism, you can change it in the `con
|
|
360
364
|
# Use the database for sessions instead of the cookie-based default,
|
361
365
|
# which shouldn't be used to store highly confidential information
|
362
366
|
# (create the session table with "rails g active_record:session_migration")
|
363
|
-
#
|
367
|
+
# Rails.application.config.session_store :active_record_store
|
364
368
|
```
|
365
369
|
|
366
370
|
Rails sets up a session key (the name of the cookie) when signing the session data. These can also be changed in `config/initializers/session_store.rb`:
|
367
371
|
|
368
372
|
```ruby
|
369
373
|
# Be sure to restart your server when you modify this file.
|
370
|
-
|
374
|
+
Rails.application.config.session_store :cookie_store, key: '_your_app_session'
|
371
375
|
```
|
372
376
|
|
373
377
|
You can also pass a `:domain` key and specify the domain name for the cookie:
|
374
378
|
|
375
379
|
```ruby
|
376
380
|
# Be sure to restart your server when you modify this file.
|
377
|
-
|
381
|
+
Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"
|
378
382
|
```
|
379
383
|
|
380
|
-
Rails sets up (for the CookieStore) a secret key used for signing the session data. This can be changed in `config/
|
384
|
+
Rails sets up (for the CookieStore) a secret key used for signing the session data. This can be changed in `config/secrets.yml`
|
381
385
|
|
382
386
|
```ruby
|
383
387
|
# Be sure to restart your server when you modify this file.
|
384
388
|
|
385
|
-
# Your secret key for verifying the integrity of signed cookies.
|
389
|
+
# Your secret key is used for verifying the integrity of signed cookies.
|
386
390
|
# If you change this key, all old signed cookies will become invalid!
|
391
|
+
|
387
392
|
# Make sure the secret is at least 30 characters and all random,
|
388
393
|
# no regular words or you'll be exposed to dictionary attacks.
|
389
|
-
|
394
|
+
# You can use `rake secret` to generate a secure secret key.
|
395
|
+
|
396
|
+
# Make sure the secrets in this file are kept private
|
397
|
+
# if you're sharing your code publicly.
|
398
|
+
|
399
|
+
development:
|
400
|
+
secret_key_base: a75d...
|
401
|
+
|
402
|
+
test:
|
403
|
+
secret_key_base: 492f...
|
404
|
+
|
405
|
+
# Do not keep production secrets in the repository,
|
406
|
+
# instead read values from the environment.
|
407
|
+
production:
|
408
|
+
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
|
390
409
|
```
|
391
410
|
|
392
411
|
NOTE: Changing the secret when using the `CookieStore` will invalidate all existing sessions.
|
@@ -410,7 +429,7 @@ class ApplicationController < ActionController::Base
|
|
410
429
|
# logging out removes it.
|
411
430
|
def current_user
|
412
431
|
@_current_user ||= session[:current_user_id] &&
|
413
|
-
User.
|
432
|
+
User.find_by(id: session[:current_user_id])
|
414
433
|
end
|
415
434
|
end
|
416
435
|
```
|
@@ -538,7 +557,7 @@ end
|
|
538
557
|
Cookies
|
539
558
|
-------
|
540
559
|
|
541
|
-
Your application can store small amounts of data on the client
|
560
|
+
Your application can store small amounts of data on the client - called cookies - that will be persisted across requests and even sessions. Rails provides easy access to cookies via the `cookies` method, which - much like the `session` - works like a hash:
|
542
561
|
|
543
562
|
```ruby
|
544
563
|
class CommentsController < ApplicationController
|
@@ -568,10 +587,66 @@ end
|
|
568
587
|
|
569
588
|
Note that while for session values you set the key to `nil`, to delete a cookie value you should use `cookies.delete(:key)`.
|
570
589
|
|
571
|
-
|
590
|
+
Rails also provides a signed cookie jar and an encrypted cookie jar for storing
|
591
|
+
sensitive data. The signed cookie jar appends a cryptographic signature on the
|
592
|
+
cookie values to protect their integrity. The encrypted cookie jar encrypts the
|
593
|
+
values in addition to signing them, so that they cannot be read by the end user.
|
594
|
+
Refer to the [API documentation](http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html)
|
595
|
+
for more details.
|
596
|
+
|
597
|
+
These special cookie jars use a serializer to serialize the assigned values into
|
598
|
+
strings and deserializes them into Ruby objects on read.
|
599
|
+
|
600
|
+
You can specify what serializer to use:
|
601
|
+
|
602
|
+
```ruby
|
603
|
+
Rails.application.config.action_dispatch.cookies_serializer = :json
|
604
|
+
```
|
605
|
+
|
606
|
+
The default serializer for new applications is `:json`. For compatibility with
|
607
|
+
old applications with existing cookies, `:marshal` is used when `serializer`
|
608
|
+
option is not specified.
|
609
|
+
|
610
|
+
You may also set this option to `:hybrid`, in which case Rails would transparently
|
611
|
+
deserialize existing (`Marshal`-serialized) cookies on read and re-write them in
|
612
|
+
the `JSON` format. This is useful for migrating existing applications to the
|
613
|
+
`:json` serializer.
|
614
|
+
|
615
|
+
It is also possible to pass a custom serializer that responds to `load` and
|
616
|
+
`dump`:
|
617
|
+
|
618
|
+
```ruby
|
619
|
+
Rails.application.config.action_dispatch.cookies_serializer = MyCustomSerializer
|
620
|
+
```
|
621
|
+
|
622
|
+
When using the `:json` or `:hybrid` serializer, you should beware that not all
|
623
|
+
Ruby objects can be serialized as JSON. For example, `Date` and `Time` objects
|
624
|
+
will be serialized as strings, and `Hash`es will have their keys stringified.
|
625
|
+
|
626
|
+
```ruby
|
627
|
+
class CookiesController < ApplicationController
|
628
|
+
def set_cookie
|
629
|
+
cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
|
630
|
+
redirect_to action: 'read_cookie'
|
631
|
+
end
|
632
|
+
|
633
|
+
def read_cookie
|
634
|
+
cookies.encrypted[:expiration_date] # => "2014-03-20"
|
635
|
+
end
|
636
|
+
end
|
637
|
+
```
|
638
|
+
|
639
|
+
It's advisable that you only store simple data (strings and numbers) in cookies.
|
640
|
+
If you have to store complex objects, you would need to handle the conversion
|
641
|
+
manually when reading the values on subsequent requests.
|
642
|
+
|
643
|
+
If you use the cookie session store, this would apply to the `session` and
|
644
|
+
`flash` hash as well.
|
645
|
+
|
646
|
+
Rendering XML and JSON data
|
572
647
|
---------------------------
|
573
648
|
|
574
|
-
ActionController makes it extremely easy to render `
|
649
|
+
ActionController makes it extremely easy to render `XML` or `JSON` data. If you've generated a controller using scaffolding, it would look something like this:
|
575
650
|
|
576
651
|
```ruby
|
577
652
|
class UsersController < ApplicationController
|
@@ -660,19 +735,22 @@ You can choose not to yield and build the response yourself, in which case the a
|
|
660
735
|
|
661
736
|
While the most common way to use filters is by creating private methods and using *_action to add them, there are two other ways to do the same thing.
|
662
737
|
|
663
|
-
The first is to use a block directly with the
|
738
|
+
The first is to use a block directly with the *\_action methods. The block receives the controller as an argument, and the `require_login` filter from above could be rewritten to use a block:
|
664
739
|
|
665
740
|
```ruby
|
666
741
|
class ApplicationController < ActionController::Base
|
667
742
|
before_action do |controller|
|
668
|
-
|
743
|
+
unless controller.send(:logged_in?)
|
744
|
+
flash[:error] = "You must be logged in to access this section"
|
745
|
+
redirect_to new_login_url
|
746
|
+
end
|
669
747
|
end
|
670
748
|
end
|
671
749
|
```
|
672
750
|
|
673
751
|
Note that the filter in this case uses `send` because the `logged_in?` method is private and the filter is not run in the scope of the controller. This is not the recommended way to implement this particular filter, but in more simple cases it might be useful.
|
674
752
|
|
675
|
-
The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex and
|
753
|
+
The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex and cannot be implemented in a readable and reusable way using the two other methods. As an example, you could rewrite the login filter again to use a class:
|
676
754
|
|
677
755
|
```ruby
|
678
756
|
class ApplicationController < ActionController::Base
|
@@ -680,16 +758,16 @@ class ApplicationController < ActionController::Base
|
|
680
758
|
end
|
681
759
|
|
682
760
|
class LoginFilter
|
683
|
-
def self.
|
761
|
+
def self.before(controller)
|
684
762
|
unless controller.send(:logged_in?)
|
685
|
-
controller.flash[:error] = "You must be logged in"
|
763
|
+
controller.flash[:error] = "You must be logged in to access this section"
|
686
764
|
controller.redirect_to controller.new_login_url
|
687
765
|
end
|
688
766
|
end
|
689
767
|
end
|
690
768
|
```
|
691
769
|
|
692
|
-
Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets the controller passed as an argument. The filter class
|
770
|
+
Again, this is not an ideal example for this filter, because it's not run in the scope of the controller but gets the controller passed as an argument. The filter class must implement a method with the same name as the filter, so for the `before_action` filter the class must implement a `before` method, and so on. The `around` method must `yield` to execute the action.
|
693
771
|
|
694
772
|
Request Forgery Protection
|
695
773
|
--------------------------
|
@@ -794,7 +872,7 @@ class AdminsController < ApplicationController
|
|
794
872
|
end
|
795
873
|
```
|
796
874
|
|
797
|
-
With this in place, you can create namespaced controllers that inherit from `
|
875
|
+
With this in place, you can create namespaced controllers that inherit from `AdminsController`. The filter will thus be run for all actions in those controllers, protecting them with HTTP basic authentication.
|
798
876
|
|
799
877
|
### HTTP Digest Authentication
|
800
878
|
|
@@ -808,11 +886,11 @@ class AdminsController < ApplicationController
|
|
808
886
|
|
809
887
|
private
|
810
888
|
|
811
|
-
|
812
|
-
|
813
|
-
|
889
|
+
def authenticate
|
890
|
+
authenticate_or_request_with_http_digest do |username|
|
891
|
+
USERS[username]
|
892
|
+
end
|
814
893
|
end
|
815
|
-
end
|
816
894
|
end
|
817
895
|
```
|
818
896
|
|
@@ -839,13 +917,13 @@ class ClientsController < ApplicationController
|
|
839
917
|
|
840
918
|
private
|
841
919
|
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
920
|
+
def generate_pdf(client)
|
921
|
+
Prawn::Document.new do
|
922
|
+
text client.name, align: :center
|
923
|
+
text "Address: #{client.address}"
|
924
|
+
text "Email: #{client.email}"
|
925
|
+
end.render
|
926
|
+
end
|
849
927
|
end
|
850
928
|
```
|
851
929
|
|
@@ -907,6 +985,92 @@ Now the user can request to get a PDF version of a client just by adding ".pdf"
|
|
907
985
|
GET /clients/1.pdf
|
908
986
|
```
|
909
987
|
|
988
|
+
### Live Streaming of Arbitrary Data
|
989
|
+
|
990
|
+
Rails allows you to stream more than just files. In fact, you can stream anything
|
991
|
+
you would like in a response object. The `ActionController::Live` module allows
|
992
|
+
you to create a persistent connection with a browser. Using this module, you will
|
993
|
+
be able to send arbitrary data to the browser at specific points in time.
|
994
|
+
|
995
|
+
#### Incorporating Live Streaming
|
996
|
+
|
997
|
+
Including `ActionController::Live` inside of your controller class will provide
|
998
|
+
all actions inside of the controller the ability to stream data. You can mix in
|
999
|
+
the module like so:
|
1000
|
+
|
1001
|
+
```ruby
|
1002
|
+
class MyController < ActionController::Base
|
1003
|
+
include ActionController::Live
|
1004
|
+
|
1005
|
+
def stream
|
1006
|
+
response.headers['Content-Type'] = 'text/event-stream'
|
1007
|
+
100.times {
|
1008
|
+
response.stream.write "hello world\n"
|
1009
|
+
sleep 1
|
1010
|
+
}
|
1011
|
+
ensure
|
1012
|
+
response.stream.close
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
```
|
1016
|
+
|
1017
|
+
The above code will keep a persistent connection with the browser and send 100
|
1018
|
+
messages of `"hello world\n"`, each one second apart.
|
1019
|
+
|
1020
|
+
There are a couple of things to notice in the above example. We need to make
|
1021
|
+
sure to close the response stream. Forgetting to close the stream will leave
|
1022
|
+
the socket open forever. We also have to set the content type to `text/event-stream`
|
1023
|
+
before we write to the response stream. This is because headers cannot be written
|
1024
|
+
after the response has been committed (when `response.committed` returns a truthy
|
1025
|
+
value), which occurs when you `write` or `commit` the response stream.
|
1026
|
+
|
1027
|
+
#### Example Usage
|
1028
|
+
|
1029
|
+
Let's suppose that you were making a Karaoke machine and a user wants to get the
|
1030
|
+
lyrics for a particular song. Each `Song` has a particular number of lines and
|
1031
|
+
each line takes time `num_beats` to finish singing.
|
1032
|
+
|
1033
|
+
If we wanted to return the lyrics in Karaoke fashion (only sending the line when
|
1034
|
+
the singer has finished the previous line), then we could use `ActionController::Live`
|
1035
|
+
as follows:
|
1036
|
+
|
1037
|
+
```ruby
|
1038
|
+
class LyricsController < ActionController::Base
|
1039
|
+
include ActionController::Live
|
1040
|
+
|
1041
|
+
def show
|
1042
|
+
response.headers['Content-Type'] = 'text/event-stream'
|
1043
|
+
song = Song.find(params[:id])
|
1044
|
+
|
1045
|
+
song.each do |line|
|
1046
|
+
response.stream.write line.lyrics
|
1047
|
+
sleep line.num_beats
|
1048
|
+
end
|
1049
|
+
ensure
|
1050
|
+
response.stream.close
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
```
|
1054
|
+
|
1055
|
+
The above code sends the next line only after the singer has completed the previous
|
1056
|
+
line.
|
1057
|
+
|
1058
|
+
#### Streaming Considerations
|
1059
|
+
|
1060
|
+
Streaming arbitrary data is an extremely powerful tool. As shown in the previous
|
1061
|
+
examples, you can choose when and what to send across a response stream. However,
|
1062
|
+
you should also note the following things:
|
1063
|
+
|
1064
|
+
* Each response stream creates a new thread and copies over the thread local
|
1065
|
+
variables from the original thread. Having too many thread local variables can
|
1066
|
+
negatively impact performance. Similarly, a large number of threads can also
|
1067
|
+
hinder performance.
|
1068
|
+
* Failing to close the response stream will leave the corresponding socket open
|
1069
|
+
forever. Make sure to call `close` whenever you are using a response stream.
|
1070
|
+
* WEBrick servers buffer all responses, and so including `ActionController::Live`
|
1071
|
+
will not work. You must use a web server which does not automatically buffer
|
1072
|
+
responses.
|
1073
|
+
|
910
1074
|
Log Filtering
|
911
1075
|
-------------
|
912
1076
|
|
@@ -914,7 +1078,7 @@ Rails keeps a log file for each environment in the `log` folder. These are extre
|
|
914
1078
|
|
915
1079
|
### Parameters Filtering
|
916
1080
|
|
917
|
-
You can filter
|
1081
|
+
You can filter out sensitive request parameters from your log files by appending them to `config.filter_parameters` in the application configuration. These parameters will be marked [FILTERED] in the log.
|
918
1082
|
|
919
1083
|
```ruby
|
920
1084
|
config.filter_parameters << :password
|
@@ -922,7 +1086,7 @@ config.filter_parameters << :password
|
|
922
1086
|
|
923
1087
|
### Redirects Filtering
|
924
1088
|
|
925
|
-
Sometimes it's desirable to filter out from log files some
|
1089
|
+
Sometimes it's desirable to filter out from log files some sensitive locations your application is redirecting to.
|
926
1090
|
You can do that by using the `config.filter_redirect` configuration option:
|
927
1091
|
|
928
1092
|
```ruby
|
@@ -962,9 +1126,9 @@ class ApplicationController < ActionController::Base
|
|
962
1126
|
|
963
1127
|
private
|
964
1128
|
|
965
|
-
|
966
|
-
|
967
|
-
|
1129
|
+
def record_not_found
|
1130
|
+
render plain: "404 Not Found", status: 404
|
1131
|
+
end
|
968
1132
|
end
|
969
1133
|
```
|
970
1134
|
|
@@ -976,10 +1140,10 @@ class ApplicationController < ActionController::Base
|
|
976
1140
|
|
977
1141
|
private
|
978
1142
|
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
1143
|
+
def user_not_authorized
|
1144
|
+
flash[:error] = "You don't have access to this section."
|
1145
|
+
redirect_to :back
|
1146
|
+
end
|
983
1147
|
end
|
984
1148
|
|
985
1149
|
class ClientsController < ApplicationController
|
@@ -993,13 +1157,15 @@ class ClientsController < ApplicationController
|
|
993
1157
|
|
994
1158
|
private
|
995
1159
|
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1160
|
+
# If the user is not authorized, just throw the exception.
|
1161
|
+
def check_authorization
|
1162
|
+
raise User::NotAuthorized unless current_user.admin?
|
1163
|
+
end
|
1000
1164
|
end
|
1001
1165
|
```
|
1002
1166
|
|
1167
|
+
WARNING: You shouldn't do `rescue_from Exception` or `rescue_from StandardError` unless you have a particular reason as it will cause serious side-effects (e.g. you won't be able to see exception details and tracebacks during development).
|
1168
|
+
|
1003
1169
|
NOTE: Certain exceptions are only rescuable from the `ApplicationController` class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's [article](http://m.onkey.org/2008/7/20/rescue-from-dispatching) on the subject for more information.
|
1004
1170
|
|
1005
1171
|
Force HTTPS protocol
|