rails 4.1.16 → 4.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/guides/CHANGELOG.md +13 -102
- data/guides/Rakefile +2 -2
- data/guides/assets/javascripts/guides.js +6 -0
- data/guides/assets/stylesheets/main.css +4 -1
- data/guides/bug_report_templates/action_controller_gem.rb +2 -2
- data/guides/bug_report_templates/action_controller_master.rb +5 -2
- data/guides/bug_report_templates/active_record_master.rb +2 -0
- data/guides/rails_guides.rb +2 -2
- data/guides/rails_guides/helpers.rb +1 -1
- data/guides/rails_guides/levenshtein.rb +29 -21
- data/guides/rails_guides/markdown.rb +6 -7
- data/guides/rails_guides/markdown/renderer.rb +1 -1
- data/guides/source/2_3_release_notes.md +3 -3
- data/guides/source/3_0_release_notes.md +4 -4
- data/guides/source/3_1_release_notes.md +2 -2
- data/guides/source/3_2_release_notes.md +2 -2
- data/guides/source/4_1_release_notes.md +8 -9
- data/guides/source/4_2_release_notes.md +572 -0
- data/guides/source/_license.html.erb +1 -1
- data/guides/source/_welcome.html.erb +2 -8
- data/guides/source/action_controller_overview.md +79 -7
- data/guides/source/action_mailer_basics.md +36 -11
- data/guides/source/action_view_overview.md +138 -119
- data/guides/source/active_job_basics.md +253 -0
- data/guides/source/active_model_basics.md +23 -0
- data/guides/source/active_record_basics.md +16 -15
- data/guides/source/active_record_callbacks.md +12 -9
- data/guides/source/{migrations.md → active_record_migrations.md} +90 -217
- data/guides/source/active_record_postgresql.md +437 -0
- data/guides/source/active_record_querying.md +261 -261
- data/guides/source/active_record_validations.md +7 -7
- data/guides/source/active_support_core_extensions.md +105 -44
- data/guides/source/active_support_instrumentation.md +3 -2
- data/guides/source/api_documentation_guidelines.md +62 -16
- data/guides/source/asset_pipeline.md +58 -46
- data/guides/source/association_basics.md +47 -38
- data/guides/source/caching_with_rails.md +31 -6
- data/guides/source/command_line.md +56 -25
- data/guides/source/configuring.md +98 -19
- data/guides/source/contributing_to_ruby_on_rails.md +174 -111
- data/guides/source/credits.html.erb +1 -1
- data/guides/source/debugging_rails_applications.md +438 -284
- data/guides/source/development_dependencies_install.md +17 -4
- data/guides/source/documents.yaml +11 -7
- data/guides/source/engines.md +192 -203
- data/guides/source/form_helpers.md +54 -45
- data/guides/source/generators.md +20 -11
- data/guides/source/getting_started.md +330 -191
- data/guides/source/i18n.md +92 -62
- data/guides/source/index.html.erb +1 -0
- data/guides/source/initialization.md +108 -59
- data/guides/source/layout.html.erb +1 -4
- data/guides/source/layouts_and_rendering.md +24 -23
- data/guides/source/nested_model_forms.md +3 -3
- data/guides/source/plugins.md +26 -26
- data/guides/source/rails_application_templates.md +21 -3
- data/guides/source/rails_on_rack.md +1 -1
- data/guides/source/routing.md +97 -71
- data/guides/source/ruby_on_rails_guides_guidelines.md +10 -12
- data/guides/source/security.md +39 -33
- data/guides/source/testing.md +111 -108
- data/guides/source/upgrading_ruby_on_rails.md +131 -14
- data/guides/source/working_with_javascript_in_rails.md +18 -16
- data/guides/w3c_validator.rb +2 -0
- metadata +37 -94
- data/guides/bug_report_templates/generic_gem.rb +0 -15
- data/guides/bug_report_templates/generic_master.rb +0 -26
- data/guides/code/getting_started/Gemfile +0 -40
- data/guides/code/getting_started/Gemfile.lock +0 -125
- data/guides/code/getting_started/README.rdoc +0 -28
- data/guides/code/getting_started/Rakefile +0 -6
- data/guides/code/getting_started/app/assets/javascripts/application.js +0 -15
- data/guides/code/getting_started/app/assets/javascripts/comments.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/posts.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/application.css +0 -13
- data/guides/code/getting_started/app/assets/stylesheets/comments.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/posts.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss +0 -3
- data/guides/code/getting_started/app/controllers/application_controller.rb +0 -5
- data/guides/code/getting_started/app/controllers/comments_controller.rb +0 -23
- data/guides/code/getting_started/app/controllers/posts_controller.rb +0 -53
- data/guides/code/getting_started/app/controllers/welcome_controller.rb +0 -4
- data/guides/code/getting_started/app/helpers/application_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/comments_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/posts_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/welcome_helper.rb +0 -2
- data/guides/code/getting_started/app/models/comment.rb +0 -3
- data/guides/code/getting_started/app/models/post.rb +0 -7
- data/guides/code/getting_started/app/views/comments/_comment.html.erb +0 -15
- data/guides/code/getting_started/app/views/comments/_form.html.erb +0 -13
- data/guides/code/getting_started/app/views/layouts/application.html.erb +0 -14
- data/guides/code/getting_started/app/views/posts/_form.html.erb +0 -27
- data/guides/code/getting_started/app/views/posts/edit.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/index.html.erb +0 -21
- data/guides/code/getting_started/app/views/posts/new.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/show.html.erb +0 -18
- data/guides/code/getting_started/app/views/welcome/index.html.erb +0 -4
- data/guides/code/getting_started/bin/bundle +0 -4
- data/guides/code/getting_started/bin/rails +0 -4
- data/guides/code/getting_started/bin/rake +0 -4
- data/guides/code/getting_started/config.ru +0 -4
- data/guides/code/getting_started/config/application.rb +0 -18
- data/guides/code/getting_started/config/boot.rb +0 -4
- data/guides/code/getting_started/config/database.yml +0 -25
- data/guides/code/getting_started/config/environment.rb +0 -5
- data/guides/code/getting_started/config/environments/development.rb +0 -30
- data/guides/code/getting_started/config/environments/production.rb +0 -80
- data/guides/code/getting_started/config/environments/test.rb +0 -36
- data/guides/code/getting_started/config/initializers/backtrace_silencers.rb +0 -7
- data/guides/code/getting_started/config/initializers/filter_parameter_logging.rb +0 -4
- data/guides/code/getting_started/config/initializers/inflections.rb +0 -16
- data/guides/code/getting_started/config/initializers/locale.rb +0 -9
- data/guides/code/getting_started/config/initializers/mime_types.rb +0 -5
- data/guides/code/getting_started/config/initializers/secret_token.rb +0 -12
- data/guides/code/getting_started/config/initializers/session_store.rb +0 -3
- data/guides/code/getting_started/config/initializers/wrap_parameters.rb +0 -14
- data/guides/code/getting_started/config/locales/en.yml +0 -23
- data/guides/code/getting_started/config/routes.rb +0 -7
- data/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb +0 -10
- data/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb +0 -11
- data/guides/code/getting_started/db/schema.rb +0 -33
- data/guides/code/getting_started/db/seeds.rb +0 -7
- data/guides/code/getting_started/public/404.html +0 -60
- data/guides/code/getting_started/public/422.html +0 -60
- data/guides/code/getting_started/public/500.html +0 -59
- data/guides/code/getting_started/public/favicon.ico +0 -0
- data/guides/code/getting_started/public/robots.txt +0 -5
- data/guides/code/getting_started/test/controllers/comments_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/posts_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/welcome_controller_test.rb +0 -9
- data/guides/code/getting_started/test/fixtures/comments.yml +0 -11
- data/guides/code/getting_started/test/fixtures/posts.yml +0 -9
- data/guides/code/getting_started/test/helpers/comments_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/posts_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/welcome_helper_test.rb +0 -4
- data/guides/code/getting_started/test/models/comment_test.rb +0 -7
- data/guides/code/getting_started/test/models/post_test.rb +0 -7
- data/guides/code/getting_started/test/test_helper.rb +0 -12
data/guides/source/i18n.md
CHANGED
@@ -92,7 +92,7 @@ Rails adds all `.rb` and `.yml` files from the `config/locales` directory to you
|
|
92
92
|
|
93
93
|
The default `en.yml` locale in this directory contains a sample pair of translation strings:
|
94
94
|
|
95
|
-
```
|
95
|
+
```yaml
|
96
96
|
en:
|
97
97
|
hello: "Hello world"
|
98
98
|
```
|
@@ -107,7 +107,7 @@ The **translations load path** (`I18n.load_path`) is just a Ruby Array of paths
|
|
107
107
|
|
108
108
|
NOTE: The backend will lazy-load these translations when a translation is looked up for the first time. This makes it possible to just swap the backend with something else even after translations have already been announced.
|
109
109
|
|
110
|
-
The default `application.rb`
|
110
|
+
The default `application.rb` file has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
|
111
111
|
|
112
112
|
```ruby
|
113
113
|
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
@@ -137,7 +137,7 @@ If you want to translate your Rails application to a **single language other tha
|
|
137
137
|
|
138
138
|
However, you would probably like to **provide support for more locales** in your application. In such case, you need to set and pass the locale between requests.
|
139
139
|
|
140
|
-
WARNING: You may be tempted to store the chosen locale in a _session_ or a
|
140
|
+
WARNING: You may be tempted to store the chosen locale in a _session_ or a *cookie*. However, **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [*RESTful*](http://en.wikipedia.org/wiki/Representational_State_Transfer). Read more about the RESTful approach in [Stefan Tilkov's articles](http://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below.
|
141
141
|
|
142
142
|
The _setting part_ is easy. You can set the locale in a `before_action` in the `ApplicationController` like this:
|
143
143
|
|
@@ -212,17 +212,16 @@ The most usual way of setting (and passing) the locale would be to include it in
|
|
212
212
|
|
213
213
|
This approach has almost the same set of advantages as setting the locale from the domain name: namely that it's RESTful and in accord with the rest of the World Wide Web. It does require a little bit more work to implement, though.
|
214
214
|
|
215
|
-
Getting the locale from `params` and setting it accordingly is not hard; including it in every URL and thus **passing it through the requests** is. To include an explicit option in every URL
|
215
|
+
Getting the locale from `params` and setting it accordingly is not hard; including it in every URL and thus **passing it through the requests** is. To include an explicit option in every URL, e.g. `link_to(books_url(locale: I18n.locale))`, would be tedious and probably impossible, of course.
|
216
216
|
|
217
|
-
Rails contains infrastructure for "centralizing dynamic decisions about the URLs" in its [`ApplicationController#default_url_options`](http://api.rubyonrails.org/classes/
|
217
|
+
Rails contains infrastructure for "centralizing dynamic decisions about the URLs" in its [`ApplicationController#default_url_options`](http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Base.html#method-i-default_url_options), which is useful precisely in this scenario: it enables us to set "defaults" for [`url_for`](http://api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html#method-i-url_for) and helper methods dependent on it (by implementing/overriding this method).
|
218
218
|
|
219
219
|
We can include something like this in our `ApplicationController` then:
|
220
220
|
|
221
221
|
```ruby
|
222
222
|
# app/controllers/application_controller.rb
|
223
|
-
def default_url_options(options={})
|
224
|
-
|
225
|
-
{ locale: I18n.locale }
|
223
|
+
def default_url_options(options = {})
|
224
|
+
{ locale: I18n.locale }.merge options
|
226
225
|
end
|
227
226
|
```
|
228
227
|
|
@@ -263,7 +262,7 @@ get '/:locale' => 'dashboard#index'
|
|
263
262
|
|
264
263
|
Do take special care about the **order of your routes**, so this route declaration does not "eat" other ones. (You may want to add it directly before the `root :to` declaration.)
|
265
264
|
|
266
|
-
NOTE: Have a look at two plugins which simplify
|
265
|
+
NOTE: Have a look at two plugins which simplify working with routes in this way: Sven Fuchs's [routing_filter](https://github.com/svenfuchs/routing-filter/tree/master) and Raul Murciano's [translate_routes](https://github.com/raul/translate_routes/tree/master).
|
267
266
|
|
268
267
|
### Setting the Locale from the Client Supplied Information
|
269
268
|
|
@@ -370,7 +369,7 @@ NOTE: Rails adds a `t` (`translate`) helper method to your views so that you do
|
|
370
369
|
|
371
370
|
So let's add the missing translations into the dictionary files (i.e. do the "localization" part):
|
372
371
|
|
373
|
-
```
|
372
|
+
```yaml
|
374
373
|
# config/locales/en.yml
|
375
374
|
en:
|
376
375
|
hello_world: Hello world!
|
@@ -422,7 +421,7 @@ OK! Now let's add a timestamp to the view, so we can demo the **date/time locali
|
|
422
421
|
|
423
422
|
And in our pirate translations file let's add a time format (it's already there in Rails' defaults for English):
|
424
423
|
|
425
|
-
```
|
424
|
+
```yaml
|
426
425
|
# config/locales/pirate.yml
|
427
426
|
pirate:
|
428
427
|
time:
|
@@ -681,62 +680,13 @@ NOTE: Automatic conversion to HTML safe translate text is only available from th
|
|
681
680
|
|
682
681
|

|
683
682
|
|
684
|
-
How to Store your Custom Translations
|
685
|
-
-------------------------------------
|
686
|
-
|
687
|
-
The Simple backend shipped with Active Support allows you to store translations in both plain Ruby and YAML format.[^2]
|
688
|
-
|
689
|
-
For example a Ruby Hash providing translations can look like this:
|
690
|
-
|
691
|
-
```ruby
|
692
|
-
{
|
693
|
-
pt: {
|
694
|
-
foo: {
|
695
|
-
bar: "baz"
|
696
|
-
}
|
697
|
-
}
|
698
|
-
}
|
699
|
-
```
|
700
|
-
|
701
|
-
The equivalent YAML file would look like this:
|
702
|
-
|
703
|
-
```ruby
|
704
|
-
pt:
|
705
|
-
foo:
|
706
|
-
bar: baz
|
707
|
-
```
|
708
|
-
|
709
|
-
As you see, in both cases the top level key is the locale. `:foo` is a namespace key and `:bar` is the key for the translation "baz".
|
710
|
-
|
711
|
-
Here is a "real" example from the Active Support `en.yml` translations YAML file:
|
712
|
-
|
713
|
-
```ruby
|
714
|
-
en:
|
715
|
-
date:
|
716
|
-
formats:
|
717
|
-
default: "%Y-%m-%d"
|
718
|
-
short: "%b %d"
|
719
|
-
long: "%B %d, %Y"
|
720
|
-
```
|
721
|
-
|
722
|
-
So, all of the following equivalent lookups will return the `:short` date format `"%b %d"`:
|
723
|
-
|
724
|
-
```ruby
|
725
|
-
I18n.t 'date.formats.short'
|
726
|
-
I18n.t 'formats.short', scope: :date
|
727
|
-
I18n.t :short, scope: 'date.formats'
|
728
|
-
I18n.t :short, scope: [:date, :formats]
|
729
|
-
```
|
730
|
-
|
731
|
-
Generally we recommend using YAML as a format for storing translations. There are cases, though, where you want to store Ruby lambdas as part of your locale data, e.g. for special date formats.
|
732
|
-
|
733
683
|
### Translations for Active Record Models
|
734
684
|
|
735
685
|
You can use the methods `Model.model_name.human` and `Model.human_attribute_name(attribute)` to transparently look up translations for your model and attribute names.
|
736
686
|
|
737
687
|
For example when you add the following translations:
|
738
688
|
|
739
|
-
```
|
689
|
+
```yaml
|
740
690
|
en:
|
741
691
|
activerecord:
|
742
692
|
models:
|
@@ -751,7 +701,7 @@ Then `User.model_name.human` will return "Dude" and `User.human_attribute_name("
|
|
751
701
|
|
752
702
|
You can also set a plural form for model names, adding as following:
|
753
703
|
|
754
|
-
```
|
704
|
+
```yaml
|
755
705
|
en:
|
756
706
|
activerecord:
|
757
707
|
models:
|
@@ -762,6 +712,19 @@ en:
|
|
762
712
|
|
763
713
|
Then `User.model_name.human(count: 2)` will return "Dudes". With `count: 1` or without params will return "Dude".
|
764
714
|
|
715
|
+
In the event you need to access nested attributes within a given model, you should nest these under `model/attribute` at the model level of your translation file:
|
716
|
+
|
717
|
+
```yaml
|
718
|
+
en:
|
719
|
+
activerecord:
|
720
|
+
attributes:
|
721
|
+
user/gender:
|
722
|
+
female: "Female"
|
723
|
+
male: "Male"
|
724
|
+
```
|
725
|
+
|
726
|
+
Then `User.human_attribute_name("gender.female")` will return "Female".
|
727
|
+
|
765
728
|
#### Error Message Scopes
|
766
729
|
|
767
730
|
Active Record validation error messages can also be translated easily. Active Record gives you a couple of namespaces where you can place your message translations in order to provide different messages and translation for certain models, attributes, and/or validations. It also transparently takes single table inheritance into account.
|
@@ -897,6 +860,24 @@ en:
|
|
897
860
|
subject: "Welcome to Rails Guides!"
|
898
861
|
```
|
899
862
|
|
863
|
+
To send parameters to interpolation use the `default_i18n_subject` method on the mailer.
|
864
|
+
|
865
|
+
```ruby
|
866
|
+
# user_mailer.rb
|
867
|
+
class UserMailer < ActionMailer::Base
|
868
|
+
def welcome(user)
|
869
|
+
mail(to: user.email, subject: default_i18n_subject(user: user.name))
|
870
|
+
end
|
871
|
+
end
|
872
|
+
```
|
873
|
+
|
874
|
+
```yaml
|
875
|
+
en:
|
876
|
+
user_mailer:
|
877
|
+
welcome:
|
878
|
+
subject: "%{user}, welcome to Rails Guides!"
|
879
|
+
```
|
880
|
+
|
900
881
|
### Overview of Other Built-In Methods that Provide I18n Support
|
901
882
|
|
902
883
|
Rails uses fixed strings and other localizations, such as format strings and other format information in a couple of helpers. Here's a brief overview.
|
@@ -921,6 +902,55 @@ Rails uses fixed strings and other localizations, such as format strings and oth
|
|
921
902
|
|
922
903
|
* `Array#to_sentence` uses format settings as given in the [support.array](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L33) scope.
|
923
904
|
|
905
|
+
How to Store your Custom Translations
|
906
|
+
-------------------------------------
|
907
|
+
|
908
|
+
The Simple backend shipped with Active Support allows you to store translations in both plain Ruby and YAML format.[^2]
|
909
|
+
|
910
|
+
For example a Ruby Hash providing translations can look like this:
|
911
|
+
|
912
|
+
```yaml
|
913
|
+
{
|
914
|
+
pt: {
|
915
|
+
foo: {
|
916
|
+
bar: "baz"
|
917
|
+
}
|
918
|
+
}
|
919
|
+
}
|
920
|
+
```
|
921
|
+
|
922
|
+
The equivalent YAML file would look like this:
|
923
|
+
|
924
|
+
```yaml
|
925
|
+
pt:
|
926
|
+
foo:
|
927
|
+
bar: baz
|
928
|
+
```
|
929
|
+
|
930
|
+
As you see, in both cases the top level key is the locale. `:foo` is a namespace key and `:bar` is the key for the translation "baz".
|
931
|
+
|
932
|
+
Here is a "real" example from the Active Support `en.yml` translations YAML file:
|
933
|
+
|
934
|
+
```yaml
|
935
|
+
en:
|
936
|
+
date:
|
937
|
+
formats:
|
938
|
+
default: "%Y-%m-%d"
|
939
|
+
short: "%b %d"
|
940
|
+
long: "%B %d, %Y"
|
941
|
+
```
|
942
|
+
|
943
|
+
So, all of the following equivalent lookups will return the `:short` date format `"%b %d"`:
|
944
|
+
|
945
|
+
```ruby
|
946
|
+
I18n.t 'date.formats.short'
|
947
|
+
I18n.t 'formats.short', scope: :date
|
948
|
+
I18n.t :short, scope: 'date.formats'
|
949
|
+
I18n.t :short, scope: [:date, :formats]
|
950
|
+
```
|
951
|
+
|
952
|
+
Generally we recommend using YAML as a format for storing translations. There are cases, though, where you want to store Ruby lambdas as part of your locale data, e.g. for special date formats.
|
953
|
+
|
924
954
|
Customize your I18n Setup
|
925
955
|
-------------------------
|
926
956
|
|
@@ -9,6 +9,7 @@ Ruby on Rails Guides
|
|
9
9
|
<% content_for :index_section do %>
|
10
10
|
<div id="subCol">
|
11
11
|
<dl>
|
12
|
+
<dt></dt>
|
12
13
|
<dd class="kindle">Rails Guides are also available for <%= link_to 'Kindle', @mobi %>.</dd>
|
13
14
|
<dd class="work-in-progress">Guides marked with this icon are currently being worked on and will not be available in the Guides Index menu. While still useful, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections.</dd>
|
14
15
|
</dl>
|
@@ -98,9 +98,9 @@ configure the load path for your Gemfile's dependencies.
|
|
98
98
|
|
99
99
|
A standard Rails application depends on several gems, specifically:
|
100
100
|
|
101
|
-
* abstract
|
102
101
|
* actionmailer
|
103
102
|
* actionpack
|
103
|
+
* actionview
|
104
104
|
* activemodel
|
105
105
|
* activerecord
|
106
106
|
* activesupport
|
@@ -119,7 +119,7 @@ A standard Rails application depends on several gems, specifically:
|
|
119
119
|
* rails
|
120
120
|
* railties
|
121
121
|
* rake
|
122
|
-
* sqlite3
|
122
|
+
* sqlite3
|
123
123
|
* thor
|
124
124
|
* treetop
|
125
125
|
* tzinfo
|
@@ -166,6 +166,7 @@ is called.
|
|
166
166
|
COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help)
|
167
167
|
|
168
168
|
def run_command!(command)
|
169
|
+
command = parse_command(command)
|
169
170
|
if COMMAND_WHITELIST.include?(command)
|
170
171
|
send(command)
|
171
172
|
else
|
@@ -178,8 +179,7 @@ With the `server` command, Rails will further run the following code:
|
|
178
179
|
|
179
180
|
```ruby
|
180
181
|
def set_application_directory!
|
181
|
-
Dir.chdir(File.expand_path('../../', APP_PATH)) unless
|
182
|
-
File.exist?(File.expand_path("config.ru"))
|
182
|
+
Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
|
183
183
|
end
|
184
184
|
|
185
185
|
def server
|
@@ -187,6 +187,8 @@ def server
|
|
187
187
|
require_command!("server")
|
188
188
|
|
189
189
|
Rails::Server.new.tap do |server|
|
190
|
+
# We need to require application after the server sets environment,
|
191
|
+
# otherwise the --environment option given to the server won't propagate.
|
190
192
|
require APP_PATH
|
191
193
|
Dir.chdir(Rails.application.root)
|
192
194
|
server.start
|
@@ -207,6 +209,7 @@ sets up the `Rails::Server` class.
|
|
207
209
|
require 'fileutils'
|
208
210
|
require 'optparse'
|
209
211
|
require 'action_dispatch'
|
212
|
+
require 'rails'
|
210
213
|
|
211
214
|
module Rails
|
212
215
|
class Server < ::Rack::Server
|
@@ -273,7 +276,7 @@ def parse_options(args)
|
|
273
276
|
# http://www.meb.uni-bonn.de/docs/cgi/cl.html
|
274
277
|
args.clear if ENV.include?("REQUEST_METHOD")
|
275
278
|
|
276
|
-
options.merge! opt_parser.parse!
|
279
|
+
options.merge! opt_parser.parse!(args)
|
277
280
|
options[:config] = ::File.expand_path(options[:config])
|
278
281
|
ENV["RACK_ENV"] = options[:environment]
|
279
282
|
options
|
@@ -284,18 +287,21 @@ With the `default_options` set to this:
|
|
284
287
|
|
285
288
|
```ruby
|
286
289
|
def default_options
|
290
|
+
environment = ENV['RACK_ENV'] || 'development'
|
291
|
+
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
|
292
|
+
|
287
293
|
{
|
288
|
-
environment
|
289
|
-
pid
|
290
|
-
Port
|
291
|
-
Host
|
292
|
-
AccessLog
|
293
|
-
config
|
294
|
+
:environment => environment,
|
295
|
+
:pid => nil,
|
296
|
+
:Port => 9292,
|
297
|
+
:Host => default_host,
|
298
|
+
:AccessLog => [],
|
299
|
+
:config => "config.ru"
|
294
300
|
}
|
295
301
|
end
|
296
302
|
```
|
297
303
|
|
298
|
-
There is no `REQUEST_METHOD` key in `ENV` so we can skip over that line. The next line merges in the options from `opt_parser` which is defined plainly in `Rack::Server
|
304
|
+
There is no `REQUEST_METHOD` key in `ENV` so we can skip over that line. The next line merges in the options from `opt_parser` which is defined plainly in `Rack::Server`:
|
299
305
|
|
300
306
|
```ruby
|
301
307
|
def opt_parser
|
@@ -348,6 +354,7 @@ private
|
|
348
354
|
def print_boot_information
|
349
355
|
...
|
350
356
|
puts "=> Run `rails server -h` for more startup options"
|
357
|
+
...
|
351
358
|
puts "=> Ctrl-C to shutdown server" unless options[:daemonize]
|
352
359
|
end
|
353
360
|
|
@@ -434,7 +441,11 @@ The `app` method here is defined like so:
|
|
434
441
|
|
435
442
|
```ruby
|
436
443
|
def app
|
437
|
-
@app ||=
|
444
|
+
@app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
|
445
|
+
end
|
446
|
+
...
|
447
|
+
private
|
448
|
+
def build_app_and_options_from_config
|
438
449
|
if !::File.exist? options[:config]
|
439
450
|
abort "configuration #{options[:config]} not found"
|
440
451
|
end
|
@@ -443,7 +454,10 @@ def app
|
|
443
454
|
self.options.merge! options
|
444
455
|
app
|
445
456
|
end
|
446
|
-
|
457
|
+
|
458
|
+
def build_app_from_string
|
459
|
+
Rack::Builder.new_from_string(self.options[:builder])
|
460
|
+
end
|
447
461
|
```
|
448
462
|
|
449
463
|
The `options[:config]` value defaults to `config.ru` which contains this:
|
@@ -459,8 +473,14 @@ run <%= app_const %>
|
|
459
473
|
The `Rack::Builder.parse_file` method here takes the content from this `config.ru` file and parses it using this code:
|
460
474
|
|
461
475
|
```ruby
|
462
|
-
app =
|
463
|
-
|
476
|
+
app = new_from_string cfgfile, config
|
477
|
+
|
478
|
+
...
|
479
|
+
|
480
|
+
def self.new_from_string(builder_script, file="(rackup)")
|
481
|
+
eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
|
482
|
+
TOPLEVEL_BINDING, file, 0
|
483
|
+
end
|
464
484
|
```
|
465
485
|
|
466
486
|
The `initialize` method of `Rack::Builder` will take the block here and execute it within an instance of `Rack::Builder`. This is where the majority of the initialization process of Rails happens. The `require` line for `config/environment.rb` in `config.ru` is the first to run:
|
@@ -473,11 +493,22 @@ require ::File.expand_path('../config/environment', __FILE__)
|
|
473
493
|
|
474
494
|
This file is the common file required by `config.ru` (`rails server`) and Passenger. This is where these two ways to run the server meet; everything before this point has been Rack and Rails setup.
|
475
495
|
|
476
|
-
This file begins with requiring `config/application.rb
|
496
|
+
This file begins with requiring `config/application.rb`:
|
497
|
+
|
498
|
+
```ruby
|
499
|
+
require File.expand_path('../application', __FILE__)
|
500
|
+
```
|
477
501
|
|
478
502
|
### `config/application.rb`
|
479
503
|
|
480
|
-
This file requires `config/boot.rb
|
504
|
+
This file requires `config/boot.rb`:
|
505
|
+
|
506
|
+
```ruby
|
507
|
+
require File.expand_path('../boot', __FILE__)
|
508
|
+
```
|
509
|
+
|
510
|
+
But only if it hasn't been required before, which would be the case in `rails server`
|
511
|
+
but **wouldn't** be the case with Passenger.
|
481
512
|
|
482
513
|
Then the fun begins!
|
483
514
|
|
@@ -498,11 +529,12 @@ This file is responsible for requiring all the individual frameworks of Rails:
|
|
498
529
|
require "rails"
|
499
530
|
|
500
531
|
%w(
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
532
|
+
active_record
|
533
|
+
action_controller
|
534
|
+
action_view
|
535
|
+
action_mailer
|
536
|
+
rails/test_unit
|
537
|
+
sprockets
|
506
538
|
).each do |framework|
|
507
539
|
begin
|
508
540
|
require "#{framework}/railtie"
|
@@ -527,7 +559,7 @@ initialized. When `config/application.rb` has finished loading Rails and defined
|
|
527
559
|
the application namespace, we go back to `config/environment.rb`,
|
528
560
|
where the application is initialized. For example, if the application was called
|
529
561
|
`Blog`, here we would find `Rails.application.initialize!`, which is
|
530
|
-
defined in `rails/application.rb
|
562
|
+
defined in `rails/application.rb`.
|
531
563
|
|
532
564
|
### `railties/lib/rails/application.rb`
|
533
565
|
|
@@ -543,7 +575,7 @@ end
|
|
543
575
|
```
|
544
576
|
|
545
577
|
As you can see, you can only initialize an app once. The initializers are run through
|
546
|
-
the `run_initializers` method which is defined in `railties/lib/rails/initializable.rb
|
578
|
+
the `run_initializers` method which is defined in `railties/lib/rails/initializable.rb`:
|
547
579
|
|
548
580
|
```ruby
|
549
581
|
def run_initializers(group=:default, *args)
|
@@ -555,7 +587,7 @@ def run_initializers(group=:default, *args)
|
|
555
587
|
end
|
556
588
|
```
|
557
589
|
|
558
|
-
The run_initializers code itself is tricky. What Rails is doing here is
|
590
|
+
The `run_initializers` code itself is tricky. What Rails is doing here is
|
559
591
|
traversing all the class ancestors looking for those that respond to an
|
560
592
|
`initializers` method. It then sorts the ancestors by name, and runs them.
|
561
593
|
For example, the `Engine` class will make all the engines available by
|
@@ -568,7 +600,7 @@ initializers (like building the middleware stack) are run last. The `railtie`
|
|
568
600
|
initializers are the initializers which have been defined on the `Rails::Application`
|
569
601
|
itself and are run between the `bootstrap` and `finishers`.
|
570
602
|
|
571
|
-
After this is done we go back to `Rack::Server
|
603
|
+
After this is done we go back to `Rack::Server`.
|
572
604
|
|
573
605
|
### Rack: lib/rack/server.rb
|
574
606
|
|
@@ -576,7 +608,11 @@ Last time we left when the `app` method was being defined:
|
|
576
608
|
|
577
609
|
```ruby
|
578
610
|
def app
|
579
|
-
@app ||=
|
611
|
+
@app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
|
612
|
+
end
|
613
|
+
...
|
614
|
+
private
|
615
|
+
def build_app_and_options_from_config
|
580
616
|
if !::File.exist? options[:config]
|
581
617
|
abort "configuration #{options[:config]} not found"
|
582
618
|
end
|
@@ -585,7 +621,10 @@ def app
|
|
585
621
|
self.options.merge! options
|
586
622
|
app
|
587
623
|
end
|
588
|
-
|
624
|
+
|
625
|
+
def build_app_from_string
|
626
|
+
Rack::Builder.new_from_string(self.options[:builder])
|
627
|
+
end
|
589
628
|
```
|
590
629
|
|
591
630
|
At this point `app` is the Rails app itself (a middleware), and what
|
@@ -603,7 +642,7 @@ def build_app(app)
|
|
603
642
|
end
|
604
643
|
```
|
605
644
|
|
606
|
-
Remember, `build_app` was called (by wrapped_app) in the last line of `Server#start`.
|
645
|
+
Remember, `build_app` was called (by `wrapped_app`) in the last line of `Server#start`.
|
607
646
|
Here's how it looked like when we left:
|
608
647
|
|
609
648
|
```ruby
|
@@ -611,40 +650,50 @@ server.run wrapped_app, options, &blk
|
|
611
650
|
```
|
612
651
|
|
613
652
|
At this point, the implementation of `server.run` will depend on the
|
614
|
-
server you're using. For example, if you were using
|
653
|
+
server you're using. For example, if you were using Puma, here's what
|
615
654
|
the `run` method would look like:
|
616
655
|
|
617
656
|
```ruby
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
path = '/'+path unless path[0] == ?/
|
632
|
-
server.register(path, Rack::Handler::Mongrel.new(appl))
|
633
|
-
end
|
634
|
-
elsif app.is_a? URLMap
|
635
|
-
app.instance_variable_get(:@mapping).each do |(host, path, appl)|
|
636
|
-
next if !host.nil? && !options[:Host].nil? && options[:Host] != host
|
637
|
-
path = '/'+path unless path[0] == ?/
|
638
|
-
server.register(path, Rack::Handler::Mongrel.new(appl))
|
639
|
-
end
|
640
|
-
else
|
641
|
-
raise ArgumentError, "first argument should be a Hash or URLMap"
|
642
|
-
end
|
643
|
-
else
|
644
|
-
server.register('/', Rack::Handler::Mongrel.new(app))
|
657
|
+
...
|
658
|
+
DEFAULT_OPTIONS = {
|
659
|
+
:Host => '0.0.0.0',
|
660
|
+
:Port => 8080,
|
661
|
+
:Threads => '0:16',
|
662
|
+
:Verbose => false
|
663
|
+
}
|
664
|
+
|
665
|
+
def self.run(app, options = {})
|
666
|
+
options = DEFAULT_OPTIONS.merge(options)
|
667
|
+
|
668
|
+
if options[:Verbose]
|
669
|
+
app = Rack::CommonLogger.new(app, STDOUT)
|
645
670
|
end
|
671
|
+
|
672
|
+
if options[:environment]
|
673
|
+
ENV['RACK_ENV'] = options[:environment].to_s
|
674
|
+
end
|
675
|
+
|
676
|
+
server = ::Puma::Server.new(app)
|
677
|
+
min, max = options[:Threads].split(':', 2)
|
678
|
+
|
679
|
+
puts "Puma #{::Puma::Const::PUMA_VERSION} starting..."
|
680
|
+
puts "* Min threads: #{min}, max threads: #{max}"
|
681
|
+
puts "* Environment: #{ENV['RACK_ENV']}"
|
682
|
+
puts "* Listening on tcp://#{options[:Host]}:#{options[:Port]}"
|
683
|
+
|
684
|
+
server.add_tcp_listener options[:Host], options[:Port]
|
685
|
+
server.min_threads = min
|
686
|
+
server.max_threads = max
|
646
687
|
yield server if block_given?
|
647
|
-
|
688
|
+
|
689
|
+
begin
|
690
|
+
server.run.join
|
691
|
+
rescue Interrupt
|
692
|
+
puts "* Gracefully stopping, waiting for requests to finish"
|
693
|
+
server.stop(true)
|
694
|
+
puts "* Goodbye!"
|
695
|
+
end
|
696
|
+
|
648
697
|
end
|
649
698
|
```
|
650
699
|
|