rails 4.0.13 → 4.1.16
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 +22 -17
- data/guides/CHANGELOG.md +68 -34
- 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 +30 -34
- data/guides/assets/stylesheets/main.css +2 -1
- data/guides/assets/stylesheets/print.css +1 -1
- data/guides/bug_report_templates/action_controller_gem.rb +9 -4
- data/guides/bug_report_templates/action_controller_master.rb +4 -2
- data/guides/bug_report_templates/active_record_gem.rb +5 -2
- data/guides/bug_report_templates/active_record_master.rb +2 -1
- data/guides/bug_report_templates/generic_gem.rb +15 -0
- data/guides/bug_report_templates/generic_master.rb +26 -0
- data/guides/code/getting_started/Gemfile +21 -24
- data/guides/code/getting_started/Gemfile.lock +78 -73
- data/guides/code/getting_started/Rakefile +1 -1
- data/guides/code/getting_started/app/assets/javascripts/application.js +1 -2
- data/guides/code/getting_started/app/views/layouts/application.html.erb +2 -2
- data/guides/code/getting_started/config/environment.rb +1 -1
- data/guides/code/getting_started/config/environments/development.rb +2 -2
- data/guides/code/getting_started/config/environments/production.rb +3 -3
- data/guides/code/getting_started/config/environments/test.rb +2 -2
- data/guides/code/getting_started/config/initializers/secret_token.rb +1 -1
- data/guides/code/getting_started/config/initializers/session_store.rb +1 -1
- data/guides/code/getting_started/config/routes.rb +1 -1
- data/guides/code/getting_started/config.ru +1 -1
- data/guides/code/getting_started/public/404.html +2 -0
- data/guides/code/getting_started/public/422.html +2 -0
- data/guides/code/getting_started/public/500.html +2 -0
- data/guides/code/getting_started/test/test_helper.rb +0 -3
- data/guides/rails_guides/helpers.rb +3 -1
- data/guides/source/2_2_release_notes.md +2 -2
- data/guides/source/2_3_release_notes.md +8 -8
- data/guides/source/3_0_release_notes.md +2 -3
- data/guides/source/3_1_release_notes.md +2 -2
- data/guides/source/3_2_release_notes.md +12 -12
- data/guides/source/4_0_release_notes.md +79 -46
- data/guides/source/4_1_release_notes.md +731 -0
- data/guides/source/_welcome.html.erb +5 -2
- data/guides/source/action_controller_overview.md +189 -40
- data/guides/source/action_mailer_basics.md +27 -27
- data/guides/source/action_view_overview.md +131 -20
- data/guides/source/active_model_basics.md +6 -6
- data/guides/source/active_record_basics.md +15 -15
- data/guides/source/active_record_callbacks.md +18 -16
- data/guides/source/active_record_querying.md +93 -51
- data/guides/source/active_record_validations.md +26 -24
- data/guides/source/active_support_core_extensions.md +72 -118
- data/guides/source/active_support_instrumentation.md +13 -4
- data/guides/source/api_documentation_guidelines.md +104 -6
- data/guides/source/asset_pipeline.md +573 -244
- data/guides/source/association_basics.md +94 -22
- data/guides/source/caching_with_rails.md +15 -6
- data/guides/source/command_line.md +55 -46
- data/guides/source/configuring.md +248 -52
- data/guides/source/contributing_to_ruby_on_rails.md +18 -17
- data/guides/source/credits.html.erb +2 -2
- data/guides/source/debugging_rails_applications.md +39 -8
- data/guides/source/development_dependencies_install.md +91 -8
- data/guides/source/documents.yaml +4 -0
- data/guides/source/engines.md +678 -232
- data/guides/source/form_helpers.md +53 -35
- data/guides/source/generators.md +19 -15
- data/guides/source/getting_started.md +758 -497
- data/guides/source/i18n.md +64 -28
- data/guides/source/index.html.erb +1 -1
- data/guides/source/initialization.md +155 -58
- data/guides/source/kindle/toc.html.erb +1 -1
- data/guides/source/layout.html.erb +2 -2
- data/guides/source/layouts_and_rendering.md +59 -26
- data/guides/source/maintenance_policy.md +3 -3
- data/guides/source/migrations.md +101 -62
- data/guides/source/nested_model_forms.md +3 -3
- data/guides/source/plugins.md +34 -31
- data/guides/source/rails_application_templates.md +27 -8
- data/guides/source/rails_on_rack.md +41 -58
- data/guides/source/routing.md +115 -104
- data/guides/source/ruby_on_rails_guides_guidelines.md +2 -2
- data/guides/source/security.md +81 -36
- data/guides/source/testing.md +56 -79
- data/guides/source/upgrading_ruby_on_rails.md +531 -21
- data/guides/source/working_with_javascript_in_rails.md +19 -11
- metadata +51 -23
- 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/source/kindle/KINDLE.md +0 -26
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
</p>
|
|
11
11
|
<% else %>
|
|
12
12
|
<p>
|
|
13
|
-
These are the new guides for Rails 4.
|
|
13
|
+
These are the new guides for Rails 4.1 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 Rails
|
|
18
|
+
The guides for Rails 4.0.x are available at <a href="http://guides.rubyonrails.org/v4.0.8/">http://guides.rubyonrails.org/v4.0.8/</a>.
|
|
19
|
+
</p>
|
|
20
|
+
<p>
|
|
21
|
+
The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.19/">http://guides.rubyonrails.org/v3.2.19/</a>.
|
|
19
22
|
</p>
|
|
20
23
|
<p>
|
|
21
24
|
The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>.
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -257,7 +261,7 @@ params.require(:log_entry).permit!
|
|
|
257
261
|
```
|
|
258
262
|
|
|
259
263
|
This will mark the `:log_entry` parameters hash and any subhash of it
|
|
260
|
-
permitted.
|
|
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
|
|
|
@@ -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,21 +364,21 @@ 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
384
|
Rails sets up (for the CookieStore) a secret key used for signing the session data. This can be changed in `config/initializers/secret_token.rb`
|
|
@@ -538,7 +542,7 @@ end
|
|
|
538
542
|
Cookies
|
|
539
543
|
-------
|
|
540
544
|
|
|
541
|
-
Your application can store small amounts of data on the client
|
|
545
|
+
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
546
|
|
|
543
547
|
```ruby
|
|
544
548
|
class CommentsController < ApplicationController
|
|
@@ -568,10 +572,66 @@ end
|
|
|
568
572
|
|
|
569
573
|
Note that while for session values you set the key to `nil`, to delete a cookie value you should use `cookies.delete(:key)`.
|
|
570
574
|
|
|
571
|
-
|
|
575
|
+
Rails also provides a signed cookie jar and an encrypted cookie jar for storing
|
|
576
|
+
sensitive data. The signed cookie jar appends a cryptographic signature on the
|
|
577
|
+
cookie values to protect their integrity. The encrypted cookie jar encrypts the
|
|
578
|
+
values in addition to signing them, so that they cannot be read by the end user.
|
|
579
|
+
Refer to the [API documentation](http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html)
|
|
580
|
+
for more details.
|
|
581
|
+
|
|
582
|
+
These special cookie jars use a serializer to serialize the assigned values into
|
|
583
|
+
strings and deserializes them into Ruby objects on read.
|
|
584
|
+
|
|
585
|
+
You can specify what serializer to use:
|
|
586
|
+
|
|
587
|
+
```ruby
|
|
588
|
+
Rails.application.config.action_dispatch.cookies_serializer = :json
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
The default serializer for new applications is `:json`. For compatibility with
|
|
592
|
+
old applications with existing cookies, `:marshal` is used when `serializer`
|
|
593
|
+
option is not specified.
|
|
594
|
+
|
|
595
|
+
You may also set this option to `:hybrid`, in which case Rails would transparently
|
|
596
|
+
deserialize existing (`Marshal`-serialized) cookies on read and re-write them in
|
|
597
|
+
the `JSON` format. This is useful for migrating existing applications to the
|
|
598
|
+
`:json` serializer.
|
|
599
|
+
|
|
600
|
+
It is also possible to pass a custom serializer that responds to `load` and
|
|
601
|
+
`dump`:
|
|
602
|
+
|
|
603
|
+
```ruby
|
|
604
|
+
Rails.application.config.action_dispatch.cookies_serializer = MyCustomSerializer
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
When using the `:json` or `:hybrid` serializer, you should beware that not all
|
|
608
|
+
Ruby objects can be serialized as JSON. For example, `Date` and `Time` objects
|
|
609
|
+
will be serialized as strings, and `Hash`es will have their keys stringified.
|
|
610
|
+
|
|
611
|
+
```ruby
|
|
612
|
+
class CookiesController < ApplicationController
|
|
613
|
+
def set_cookie
|
|
614
|
+
cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
|
|
615
|
+
redirect_to action: 'read_cookie'
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
def read_cookie
|
|
619
|
+
cookies.encrypted[:expiration_date] # => "2014-03-20"
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
It's advisable that you only store simple data (strings and numbers) in cookies.
|
|
625
|
+
If you have to store complex objects, you would need to handle the conversion
|
|
626
|
+
manually when reading the values on subsequent requests.
|
|
627
|
+
|
|
628
|
+
If you use the cookie session store, this would apply to the `session` and
|
|
629
|
+
`flash` hash as well.
|
|
630
|
+
|
|
631
|
+
Rendering XML and JSON data
|
|
572
632
|
---------------------------
|
|
573
633
|
|
|
574
|
-
ActionController makes it extremely easy to render `
|
|
634
|
+
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
635
|
|
|
576
636
|
```ruby
|
|
577
637
|
class UsersController < ApplicationController
|
|
@@ -665,14 +725,17 @@ The first is to use a block directly with the *_action methods. The block receiv
|
|
|
665
725
|
```ruby
|
|
666
726
|
class ApplicationController < ActionController::Base
|
|
667
727
|
before_action do |controller|
|
|
668
|
-
|
|
728
|
+
unless controller.send(:logged_in?)
|
|
729
|
+
flash[:error] = "You must be logged in to access this section"
|
|
730
|
+
redirect_to new_login_url
|
|
731
|
+
end
|
|
669
732
|
end
|
|
670
733
|
end
|
|
671
734
|
```
|
|
672
735
|
|
|
673
736
|
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
737
|
|
|
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
|
|
738
|
+
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
739
|
|
|
677
740
|
```ruby
|
|
678
741
|
class ApplicationController < ActionController::Base
|
|
@@ -680,16 +743,16 @@ class ApplicationController < ActionController::Base
|
|
|
680
743
|
end
|
|
681
744
|
|
|
682
745
|
class LoginFilter
|
|
683
|
-
def self.
|
|
746
|
+
def self.before(controller)
|
|
684
747
|
unless controller.send(:logged_in?)
|
|
685
|
-
controller.flash[:error] = "You must be logged in"
|
|
748
|
+
controller.flash[:error] = "You must be logged in to access this section"
|
|
686
749
|
controller.redirect_to controller.new_login_url
|
|
687
750
|
end
|
|
688
751
|
end
|
|
689
752
|
end
|
|
690
753
|
```
|
|
691
754
|
|
|
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
|
|
755
|
+
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
756
|
|
|
694
757
|
Request Forgery Protection
|
|
695
758
|
--------------------------
|
|
@@ -794,7 +857,7 @@ class AdminsController < ApplicationController
|
|
|
794
857
|
end
|
|
795
858
|
```
|
|
796
859
|
|
|
797
|
-
With this in place, you can create namespaced controllers that inherit from `
|
|
860
|
+
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
861
|
|
|
799
862
|
### HTTP Digest Authentication
|
|
800
863
|
|
|
@@ -808,11 +871,11 @@ class AdminsController < ApplicationController
|
|
|
808
871
|
|
|
809
872
|
private
|
|
810
873
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
874
|
+
def authenticate
|
|
875
|
+
authenticate_or_request_with_http_digest do |username|
|
|
876
|
+
USERS[username]
|
|
877
|
+
end
|
|
814
878
|
end
|
|
815
|
-
end
|
|
816
879
|
end
|
|
817
880
|
```
|
|
818
881
|
|
|
@@ -839,13 +902,13 @@ class ClientsController < ApplicationController
|
|
|
839
902
|
|
|
840
903
|
private
|
|
841
904
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
905
|
+
def generate_pdf(client)
|
|
906
|
+
Prawn::Document.new do
|
|
907
|
+
text client.name, align: :center
|
|
908
|
+
text "Address: #{client.address}"
|
|
909
|
+
text "Email: #{client.email}"
|
|
910
|
+
end.render
|
|
911
|
+
end
|
|
849
912
|
end
|
|
850
913
|
```
|
|
851
914
|
|
|
@@ -907,6 +970,92 @@ Now the user can request to get a PDF version of a client just by adding ".pdf"
|
|
|
907
970
|
GET /clients/1.pdf
|
|
908
971
|
```
|
|
909
972
|
|
|
973
|
+
### Live Streaming of Arbitrary Data
|
|
974
|
+
|
|
975
|
+
Rails allows you to stream more than just files. In fact, you can stream anything
|
|
976
|
+
you would like in a response object. The `ActionController::Live` module allows
|
|
977
|
+
you to create a persistent connection with a browser. Using this module, you will
|
|
978
|
+
be able to send arbitrary data to the browser at specific points in time.
|
|
979
|
+
|
|
980
|
+
#### Incorporating Live Streaming
|
|
981
|
+
|
|
982
|
+
Including `ActionController::Live` inside of your controller class will provide
|
|
983
|
+
all actions inside of the controller the ability to stream data. You can mix in
|
|
984
|
+
the module like so:
|
|
985
|
+
|
|
986
|
+
```ruby
|
|
987
|
+
class MyController < ActionController::Base
|
|
988
|
+
include ActionController::Live
|
|
989
|
+
|
|
990
|
+
def stream
|
|
991
|
+
response.headers['Content-Type'] = 'text/event-stream'
|
|
992
|
+
100.times {
|
|
993
|
+
response.stream.write "hello world\n"
|
|
994
|
+
sleep 1
|
|
995
|
+
}
|
|
996
|
+
ensure
|
|
997
|
+
response.stream.close
|
|
998
|
+
end
|
|
999
|
+
end
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
The above code will keep a persistent connection with the browser and send 100
|
|
1003
|
+
messages of `"hello world\n"`, each one second apart.
|
|
1004
|
+
|
|
1005
|
+
There are a couple of things to notice in the above example. We need to make
|
|
1006
|
+
sure to close the response stream. Forgetting to close the stream will leave
|
|
1007
|
+
the socket open forever. We also have to set the content type to `text/event-stream`
|
|
1008
|
+
before we write to the response stream. This is because headers cannot be written
|
|
1009
|
+
after the response has been committed (when `response.committed` returns a truthy
|
|
1010
|
+
value), which occurs when you `write` or `commit` the response stream.
|
|
1011
|
+
|
|
1012
|
+
#### Example Usage
|
|
1013
|
+
|
|
1014
|
+
Let's suppose that you were making a Karaoke machine and a user wants to get the
|
|
1015
|
+
lyrics for a particular song. Each `Song` has a particular number of lines and
|
|
1016
|
+
each line takes time `num_beats` to finish singing.
|
|
1017
|
+
|
|
1018
|
+
If we wanted to return the lyrics in Karaoke fashion (only sending the line when
|
|
1019
|
+
the singer has finished the previous line), then we could use `ActionController::Live`
|
|
1020
|
+
as follows:
|
|
1021
|
+
|
|
1022
|
+
```ruby
|
|
1023
|
+
class LyricsController < ActionController::Base
|
|
1024
|
+
include ActionController::Live
|
|
1025
|
+
|
|
1026
|
+
def show
|
|
1027
|
+
response.headers['Content-Type'] = 'text/event-stream'
|
|
1028
|
+
song = Song.find(params[:id])
|
|
1029
|
+
|
|
1030
|
+
song.each do |line|
|
|
1031
|
+
response.stream.write line.lyrics
|
|
1032
|
+
sleep line.num_beats
|
|
1033
|
+
end
|
|
1034
|
+
ensure
|
|
1035
|
+
response.stream.close
|
|
1036
|
+
end
|
|
1037
|
+
end
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
The above code sends the next line only after the singer has completed the previous
|
|
1041
|
+
line.
|
|
1042
|
+
|
|
1043
|
+
#### Streaming Considerations
|
|
1044
|
+
|
|
1045
|
+
Streaming arbitrary data is an extremely powerful tool. As shown in the previous
|
|
1046
|
+
examples, you can choose when and what to send across a response stream. However,
|
|
1047
|
+
you should also note the following things:
|
|
1048
|
+
|
|
1049
|
+
* Each response stream creates a new thread and copies over the thread local
|
|
1050
|
+
variables from the original thread. Having too many thread local variables can
|
|
1051
|
+
negatively impact performance. Similarly, a large number of threads can also
|
|
1052
|
+
hinder performance.
|
|
1053
|
+
* Failing to close the response stream will leave the corresponding socket open
|
|
1054
|
+
forever. Make sure to call `close` whenever you are using a response stream.
|
|
1055
|
+
* WEBrick servers buffer all responses, and so including `ActionController::Live`
|
|
1056
|
+
will not work. You must use a web server which does not automatically buffer
|
|
1057
|
+
responses.
|
|
1058
|
+
|
|
910
1059
|
Log Filtering
|
|
911
1060
|
-------------
|
|
912
1061
|
|
|
@@ -962,9 +1111,9 @@ class ApplicationController < ActionController::Base
|
|
|
962
1111
|
|
|
963
1112
|
private
|
|
964
1113
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1114
|
+
def record_not_found
|
|
1115
|
+
render plain: "404 Not Found", status: 404
|
|
1116
|
+
end
|
|
968
1117
|
end
|
|
969
1118
|
```
|
|
970
1119
|
|
|
@@ -976,10 +1125,10 @@ class ApplicationController < ActionController::Base
|
|
|
976
1125
|
|
|
977
1126
|
private
|
|
978
1127
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1128
|
+
def user_not_authorized
|
|
1129
|
+
flash[:error] = "You don't have access to this section."
|
|
1130
|
+
redirect_to :back
|
|
1131
|
+
end
|
|
983
1132
|
end
|
|
984
1133
|
|
|
985
1134
|
class ClientsController < ApplicationController
|
|
@@ -993,10 +1142,10 @@ class ClientsController < ApplicationController
|
|
|
993
1142
|
|
|
994
1143
|
private
|
|
995
1144
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1145
|
+
# If the user is not authorized, just throw the exception.
|
|
1146
|
+
def check_authorization
|
|
1147
|
+
raise User::NotAuthorized unless current_user.admin?
|
|
1148
|
+
end
|
|
1000
1149
|
end
|
|
1001
1150
|
```
|
|
1002
1151
|
|
|
@@ -30,7 +30,7 @@ views.
|
|
|
30
30
|
#### Create the Mailer
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
$ rails generate mailer UserMailer
|
|
33
|
+
$ bin/rails generate mailer UserMailer
|
|
34
34
|
create app/mailers/user_mailer.rb
|
|
35
35
|
invoke erb
|
|
36
36
|
create app/views/user_mailer
|
|
@@ -105,7 +105,7 @@ will be the template used for the email, formatted in HTML:
|
|
|
105
105
|
<h1>Welcome to example.com, <%= @user.name %></h1>
|
|
106
106
|
<p>
|
|
107
107
|
You have successfully signed up to example.com,
|
|
108
|
-
your username is: <%= @user.login %>.<br
|
|
108
|
+
your username is: <%= @user.login %>.<br>
|
|
109
109
|
</p>
|
|
110
110
|
<p>
|
|
111
111
|
To login to the site, just follow this link: <%= @url %>.
|
|
@@ -138,7 +138,7 @@ When you call the `mail` method now, Action Mailer will detect the two templates
|
|
|
138
138
|
|
|
139
139
|
Mailers are really just another way to render a view. Instead of rendering a
|
|
140
140
|
view and sending out the HTTP protocol, they are just sending it out through the
|
|
141
|
-
|
|
141
|
+
email protocols instead. Due to this, it makes sense to just have your
|
|
142
142
|
controller tell the Mailer to send an email when a user is successfully created.
|
|
143
143
|
|
|
144
144
|
Setting this up is painfully simple.
|
|
@@ -146,8 +146,8 @@ Setting this up is painfully simple.
|
|
|
146
146
|
First, let's create a simple `User` scaffold:
|
|
147
147
|
|
|
148
148
|
```bash
|
|
149
|
-
$ rails generate scaffold user name email login
|
|
150
|
-
$ rake db:migrate
|
|
149
|
+
$ bin/rails generate scaffold user name email login
|
|
150
|
+
$ bin/rake db:migrate
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
Now that we have a user model to play with, we will just edit the
|
|
@@ -164,7 +164,7 @@ class UsersController < ApplicationController
|
|
|
164
164
|
|
|
165
165
|
respond_to do |format|
|
|
166
166
|
if @user.save
|
|
167
|
-
# Tell the UserMailer to send a welcome
|
|
167
|
+
# Tell the UserMailer to send a welcome email after save
|
|
168
168
|
UserMailer.welcome_email(@user).deliver
|
|
169
169
|
|
|
170
170
|
format.html { redirect_to(@user, notice: 'User was successfully created.') }
|
|
@@ -378,7 +378,7 @@ Just like with controller views, use `yield` to render the view inside the
|
|
|
378
378
|
layout.
|
|
379
379
|
|
|
380
380
|
You can also pass in a `layout: 'layout_name'` option to the render call inside
|
|
381
|
-
the format block to specify different layouts for different
|
|
381
|
+
the format block to specify different layouts for different formats:
|
|
382
382
|
|
|
383
383
|
```ruby
|
|
384
384
|
class UserMailer < ActionMailer::Base
|
|
@@ -481,7 +481,7 @@ end
|
|
|
481
481
|
|
|
482
482
|
There may be cases in which you want to skip the template rendering step and
|
|
483
483
|
supply the email body as a string. You can achieve this using the `:body`
|
|
484
|
-
option.
|
|
484
|
+
option. In such cases don't forget to add the `:content_type` option. Rails
|
|
485
485
|
will default to `text/plain` otherwise.
|
|
486
486
|
|
|
487
487
|
```ruby
|
|
@@ -569,25 +569,25 @@ class UserMailer < ActionMailer::Base
|
|
|
569
569
|
|
|
570
570
|
private
|
|
571
571
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
572
|
+
def set_delivery_options
|
|
573
|
+
# You have access to the mail instance,
|
|
574
|
+
# @business and @user instance variables here
|
|
575
|
+
if @business && @business.has_smtp_settings?
|
|
576
|
+
mail.delivery_method.settings.merge!(@business.smtp_settings)
|
|
577
|
+
end
|
|
577
578
|
end
|
|
578
|
-
end
|
|
579
579
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
580
|
+
def prevent_delivery_to_guests
|
|
581
|
+
if @user && @user.guest?
|
|
582
|
+
mail.perform_deliveries = false
|
|
583
|
+
end
|
|
583
584
|
end
|
|
584
|
-
end
|
|
585
585
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
586
|
+
def set_business_headers
|
|
587
|
+
if @business
|
|
588
|
+
headers["X-SMTPAPI-CATEGORY"] = @business.code
|
|
589
|
+
end
|
|
589
590
|
end
|
|
590
|
-
end
|
|
591
591
|
end
|
|
592
592
|
```
|
|
593
593
|
|
|
@@ -608,10 +608,10 @@ files (environment.rb, production.rb, etc...)
|
|
|
608
608
|
| Configuration | Description |
|
|
609
609
|
|---------------|-------------|
|
|
610
610
|
|`logger`|Generates information on the mailing run if available. Can be set to `nil` for no logging. Compatible with both Ruby's own `Logger` and `Log4r` loggers.|
|
|
611
|
-
|`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li>`:port`
|
|
611
|
+
|`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li>`:port` - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li>`:domain` - If you need to specify a HELO domain, you can do it here.</li><li>`:user_name` - If your mail server requires authentication, set the username in this setting.</li><li>`:password` - If your mail server requires authentication, set the password in this setting.</li><li>`:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain`, `:login`, `:cram_md5`.</li><li>`:enable_starttls_auto` - Set this to `false` if there is a problem with your server certificate that you cannot resolve.</li></ul>|
|
|
612
612
|
|`sendmail_settings`|Allows you to override options for the `:sendmail` delivery method.<ul><li>`:location` - The location of the sendmail executable. Defaults to `/usr/sbin/sendmail`.</li><li>`:arguments` - The command line arguments to be passed to sendmail. Defaults to `-i -t`.</li></ul>|
|
|
613
613
|
|`raise_delivery_errors`|Whether or not errors should be raised if the email fails to be delivered. This only works if the external email server is configured for immediate delivery.|
|
|
614
|
-
|`delivery_method`|Defines a delivery method. Possible values are
|
|
614
|
+
|`delivery_method`|Defines a delivery method. Possible values are:<ul><li>`:smtp` (default), can be configured by using `config.action_mailer.smtp_settings`.</li><li>`:sendmail`, can be configured by using `config.action_mailer.sendmail_settings`.</li><li>`:file`: save emails to files; can be configured by using `config.action_mailer.file_settings`.</li><li>`:test`: save emails to `ActionMailer::Base.deliveries` array.</li></ul>See [API docs](http://api.rubyonrails.org/classes/ActionMailer/Base.html) for more info.|
|
|
615
615
|
|`perform_deliveries`|Determines whether deliveries are actually carried out when the `deliver` method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.|
|
|
616
616
|
|`deliveries`|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
|
|
617
617
|
|`default_options`|Allows you to set default values for the `mail` method options (`:from`, `:reply_to`, etc.).|
|
|
@@ -634,13 +634,13 @@ config.action_mailer.delivery_method = :sendmail
|
|
|
634
634
|
# }
|
|
635
635
|
config.action_mailer.perform_deliveries = true
|
|
636
636
|
config.action_mailer.raise_delivery_errors = true
|
|
637
|
-
config.action_mailer.default_options = {from: 'no-
|
|
637
|
+
config.action_mailer.default_options = {from: 'no-reply@example.com'}
|
|
638
638
|
```
|
|
639
639
|
|
|
640
640
|
### Action Mailer Configuration for Gmail
|
|
641
641
|
|
|
642
|
-
As Action Mailer now uses the Mail gem, this
|
|
643
|
-
`config/environments/$RAILS_ENV.rb` file:
|
|
642
|
+
As Action Mailer now uses the [Mail gem](https://github.com/mikel/mail), this
|
|
643
|
+
becomes as simple as adding to your `config/environments/$RAILS_ENV.rb` file:
|
|
644
644
|
|
|
645
645
|
```ruby
|
|
646
646
|
config.action_mailer.delivery_method = :smtp
|