rails 4.0.13 → 4.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rails might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +20 -15
- data/guides/CHANGELOG.md +5 -74
- 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/challenge.png +0 -0
- 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/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/template_is_missing_posts_new.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/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 +2 -0
- data/guides/bug_report_templates/action_controller_master.rb +2 -0
- data/guides/bug_report_templates/active_record_gem.rb +1 -1
- data/guides/bug_report_templates/active_record_master.rb +2 -1
- data/guides/code/getting_started/Gemfile +1 -1
- data/guides/code/getting_started/app/assets/javascripts/application.js +1 -2
- data/guides/code/getting_started/config/environments/development.rb +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/rails_guides/helpers.rb +1 -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 +1 -2
- data/guides/source/3_1_release_notes.md +1 -1
- 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 +601 -0
- data/guides/source/_welcome.html.erb +1 -1
- data/guides/source/action_controller_overview.md +117 -31
- data/guides/source/action_mailer_basics.md +19 -19
- data/guides/source/action_view_overview.md +131 -12
- 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 +67 -39
- data/guides/source/active_record_validations.md +31 -31
- data/guides/source/active_support_core_extensions.md +63 -74
- data/guides/source/active_support_instrumentation.md +13 -4
- data/guides/source/api_documentation_guidelines.md +19 -5
- data/guides/source/asset_pipeline.md +544 -249
- data/guides/source/association_basics.md +81 -22
- data/guides/source/caching_with_rails.md +15 -6
- data/guides/source/command_line.md +28 -19
- data/guides/source/configuring.md +98 -50
- data/guides/source/contributing_to_ruby_on_rails.md +11 -11
- data/guides/source/credits.html.erb +2 -2
- data/guides/source/debugging_rails_applications.md +36 -5
- data/guides/source/development_dependencies_install.md +89 -8
- data/guides/source/documents.yaml +7 -1
- data/guides/source/engines.md +648 -224
- data/guides/source/form_helpers.md +56 -45
- data/guides/source/generators.md +7 -3
- data/guides/source/getting_started.md +379 -164
- data/guides/source/i18n.md +59 -23
- data/guides/source/index.html.erb +1 -1
- data/guides/source/initialization.md +153 -56
- data/guides/source/kindle/toc.html.erb +1 -1
- data/guides/source/layout.html.erb +3 -3
- data/guides/source/layouts_and_rendering.md +12 -11
- data/guides/source/maintenance_policy.md +4 -23
- data/guides/source/migrations.md +41 -37
- data/guides/source/nested_model_forms.md +3 -3
- data/guides/source/plugins.md +27 -23
- data/guides/source/rails_application_templates.md +25 -6
- data/guides/source/rails_on_rack.md +35 -51
- data/guides/source/routing.md +108 -99
- data/guides/source/ruby_on_rails_guides_guidelines.md +2 -2
- data/guides/source/security.md +33 -31
- data/guides/source/testing.md +37 -34
- data/guides/source/upgrading_ruby_on_rails.md +335 -16
- data/guides/source/working_with_javascript_in_rails.md +18 -10
- metadata +66 -39
- data/guides/assets/images/jaimeiniesta.jpg +0 -0
- data/guides/source/kindle/KINDLE.md +0 -26
data/guides/source/i18n.md
CHANGED
@@ -13,17 +13,22 @@ So, in the process of _internationalizing_ your Rails application you have to:
|
|
13
13
|
|
14
14
|
In the process of _localizing_ your application you'll probably want to do the following three things:
|
15
15
|
|
16
|
-
* Replace or supplement Rails' default locale
|
17
|
-
* Abstract strings in your application into keyed dictionaries
|
16
|
+
* Replace or supplement Rails' default locale - e.g. date and time formats, month names, Active Record model names, etc.
|
17
|
+
* Abstract strings in your application into keyed dictionaries - e.g. flash messages, static text in your views, etc.
|
18
18
|
* Store the resulting dictionaries somewhere.
|
19
19
|
|
20
20
|
This guide will walk you through the I18n API and contains a tutorial on how to internationalize a Rails application from the start.
|
21
21
|
|
22
22
|
After reading this guide, you will know:
|
23
23
|
|
24
|
+
* How I18n works in Ruby on Rails
|
25
|
+
* How to correctly use I18n into a RESTful application in various ways
|
26
|
+
* How to use I18n to translate ActiveRecord errors or ActionMailer E-mail subjects
|
27
|
+
* Some other tools to go further with the translation process of your application
|
28
|
+
|
24
29
|
--------------------------------------------------------------------------------
|
25
30
|
|
26
|
-
NOTE: The Ruby I18n framework provides you with all necessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available, which add additional functionality or features. See the
|
31
|
+
NOTE: The Ruby I18n framework provides you with all necessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available, which add additional functionality or features. See the Ruby [I18n Wiki](http://ruby-i18n.org/wiki) for more information.
|
27
32
|
|
28
33
|
How I18n in Ruby on Rails Works
|
29
34
|
-------------------------------
|
@@ -33,13 +38,13 @@ Internationalization is a complex problem. Natural languages differ in so many w
|
|
33
38
|
* providing support for English and similar languages out of the box
|
34
39
|
* making it easy to customize and extend everything for other languages
|
35
40
|
|
36
|
-
As part of this solution, **every static string in the Rails framework**
|
41
|
+
As part of this solution, **every static string in the Rails framework** - e.g. Active Record validation messages, time and date formats - **has been internationalized**, so _localization_ of a Rails application means "over-riding" these defaults.
|
37
42
|
|
38
43
|
### The Overall Architecture of the Library
|
39
44
|
|
40
45
|
Thus, the Ruby I18n gem is split into two parts:
|
41
46
|
|
42
|
-
* The public API of the i18n framework
|
47
|
+
* The public API of the i18n framework - a Ruby module with public methods that define how the library works
|
43
48
|
* A default backend (which is intentionally named _Simple_ backend) that implements these methods
|
44
49
|
|
45
50
|
As a user you should always only access the public methods on the I18n module, but it is useful to know about the capabilities of the backend.
|
@@ -174,7 +179,7 @@ end
|
|
174
179
|
# in your /etc/hosts file to try this out locally
|
175
180
|
def extract_locale_from_tld
|
176
181
|
parsed_locale = request.host.split('.').last
|
177
|
-
I18n.available_locales.
|
182
|
+
I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil
|
178
183
|
end
|
179
184
|
```
|
180
185
|
|
@@ -187,7 +192,7 @@ We can also set the locale from the _subdomain_ in a very similar way:
|
|
187
192
|
# in your /etc/hosts file to try this out locally
|
188
193
|
def extract_locale_from_subdomain
|
189
194
|
parsed_locale = request.subdomains.first
|
190
|
-
I18n.available_locales.
|
195
|
+
I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil
|
191
196
|
end
|
192
197
|
```
|
193
198
|
|
@@ -262,7 +267,7 @@ NOTE: Have a look at two plugins which simplify work with routes in this way: Sv
|
|
262
267
|
|
263
268
|
### Setting the Locale from the Client Supplied Information
|
264
269
|
|
265
|
-
In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' preferred language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites
|
270
|
+
In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users' preferred language (set in their browser), can be based on the users' geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites - see the box about _sessions_, _cookies_ and RESTful architecture above.
|
266
271
|
|
267
272
|
|
268
273
|
#### Using `Accept-Language`
|
@@ -277,21 +282,22 @@ def set_locale
|
|
277
282
|
I18n.locale = extract_locale_from_accept_language_header
|
278
283
|
logger.debug "* Locale set to '#{I18n.locale}'"
|
279
284
|
end
|
285
|
+
|
280
286
|
private
|
281
|
-
def extract_locale_from_accept_language_header
|
282
|
-
|
283
|
-
end
|
287
|
+
def extract_locale_from_accept_language_header
|
288
|
+
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
|
289
|
+
end
|
284
290
|
```
|
285
291
|
|
286
292
|
Of course, in a production environment you would need much more robust code, and could use a plugin such as Iain Hecker's [http_accept_language](https://github.com/iain/http_accept_language/tree/master) or even Rack middleware such as Ryan Tomayko's [locale](https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb).
|
287
293
|
|
288
294
|
#### Using GeoIP (or Similar) Database
|
289
295
|
|
290
|
-
Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as [GeoIP Lite Country](http://www.maxmind.com/app/geolitecountry). The mechanics of the code would be very similar to the code above
|
296
|
+
Another way of choosing the locale from client information would be to use a database for mapping the client IP to the region, such as [GeoIP Lite Country](http://www.maxmind.com/app/geolitecountry). The mechanics of the code would be very similar to the code above - you would need to query the database for the user's IP, and look up your preferred locale for the country/region/city returned.
|
291
297
|
|
292
298
|
#### User Profile
|
293
299
|
|
294
|
-
You can also provide users of your application with means to set (and possibly over-ride) the locale in your application interface, as well. Again, mechanics for this approach would be very similar to the code above
|
300
|
+
You can also provide users of your application with means to set (and possibly over-ride) the locale in your application interface, as well. Again, mechanics for this approach would be very similar to the code above - you'd probably let users choose a locale from a dropdown list and save it to their profile in the database. Then you'd set the locale to this value.
|
295
301
|
|
296
302
|
Internationalizing your Application
|
297
303
|
-----------------------------------
|
@@ -309,6 +315,17 @@ Yourapp::Application.routes.draw do
|
|
309
315
|
end
|
310
316
|
```
|
311
317
|
|
318
|
+
```ruby
|
319
|
+
# app/controllers/application_controller.rb
|
320
|
+
class ApplicationController < ActionController::Base
|
321
|
+
before_action :set_locale
|
322
|
+
|
323
|
+
def set_locale
|
324
|
+
I18n.locale = params[:locale] || I18n.default_locale
|
325
|
+
end
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
312
329
|
```ruby
|
313
330
|
# app/controllers/home_controller.rb
|
314
331
|
class HomeController < ApplicationController
|
@@ -394,7 +411,7 @@ en:
|
|
394
411
|
|
395
412
|
### Adding Date/Time Formats
|
396
413
|
|
397
|
-
OK! Now let's add a timestamp to the view, so we can demo the **date/time localization** feature as well. To localize the time format you pass the Time object to `I18n.l` or (preferably) use Rails' `#l` helper. You can pick a format by passing the `:format` option
|
414
|
+
OK! Now let's add a timestamp to the view, so we can demo the **date/time localization** feature as well. To localize the time format you pass the Time object to `I18n.l` or (preferably) use Rails' `#l` helper. You can pick a format by passing the `:format` option - by default the `:default` format is used.
|
398
415
|
|
399
416
|
```erb
|
400
417
|
# app/views/home/index.html.erb
|
@@ -475,12 +492,14 @@ Overview of the I18n API Features
|
|
475
492
|
|
476
493
|
You should have good understanding of using the i18n library now, knowing all necessary aspects of internationalizing a basic Rails application. In the following chapters, we'll cover it's features in more depth.
|
477
494
|
|
495
|
+
These chapters will show examples using both the `I18n.translate` method as well as the [`translate` view helper method](http://api.rubyonrails.org/classes/ActionView/Helpers/TranslationHelper.html#method-i-translate) (noting the additional feature provide by the view helper method).
|
496
|
+
|
478
497
|
Covered are features like these:
|
479
498
|
|
480
499
|
* looking up translations
|
481
500
|
* interpolating data into translations
|
482
501
|
* pluralizing translations
|
483
|
-
* using safe HTML translations
|
502
|
+
* using safe HTML translations (view helper method only)
|
484
503
|
* localizing dates, numbers, currency, etc.
|
485
504
|
|
486
505
|
### Looking up Translations
|
@@ -494,7 +513,7 @@ I18n.t :message
|
|
494
513
|
I18n.t 'message'
|
495
514
|
```
|
496
515
|
|
497
|
-
The `translate` method also takes a `:scope` option which can contain one or more additional keys that will be used to specify a
|
516
|
+
The `translate` method also takes a `:scope` option which can contain one or more additional keys that will be used to specify a "namespace" or scope for a translation key:
|
498
517
|
|
499
518
|
```ruby
|
500
519
|
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
|
@@ -568,6 +587,8 @@ you can look up the `books.index.title` value **inside** `app/views/books/index.
|
|
568
587
|
<%= t '.title' %>
|
569
588
|
```
|
570
589
|
|
590
|
+
NOTE: Automatic translation scoping by partial is only available from the `translate` view helper method.
|
591
|
+
|
571
592
|
### Interpolation
|
572
593
|
|
573
594
|
In many cases you want to abstract your translations so that **variables can be interpolated into the translation**. For this reason the I18n API provides an interpolation feature.
|
@@ -656,6 +677,8 @@ en:
|
|
656
677
|
<div><%= t('title.html') %></div>
|
657
678
|
```
|
658
679
|
|
680
|
+
NOTE: Automatic conversion to HTML safe translate text is only available from the `translate` view helper method.
|
681
|
+
|
659
682
|
![i18n demo html safe](images/i18n/demo_html_safe.png)
|
660
683
|
|
661
684
|
How to Store your Custom Translations
|
@@ -726,6 +749,19 @@ en:
|
|
726
749
|
|
727
750
|
Then `User.model_name.human` will return "Dude" and `User.human_attribute_name("login")` will return "Handle".
|
728
751
|
|
752
|
+
You can also set a plural form for model names, adding as following:
|
753
|
+
|
754
|
+
```ruby
|
755
|
+
en:
|
756
|
+
activerecord:
|
757
|
+
models:
|
758
|
+
user:
|
759
|
+
one: Dude
|
760
|
+
other: Dudes
|
761
|
+
```
|
762
|
+
|
763
|
+
Then `User.model_name.human(count: 2)` will return "Dudes". With `count: 1` or without params will return "Dude".
|
764
|
+
|
729
765
|
#### Error Message Scopes
|
730
766
|
|
731
767
|
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.
|
@@ -867,15 +903,15 @@ Rails uses fixed strings and other localizations, such as format strings and oth
|
|
867
903
|
|
868
904
|
#### Action View Helper Methods
|
869
905
|
|
870
|
-
* `distance_of_time_in_words` translates and pluralizes its result and interpolates the number of seconds, minutes, hours, and so on. See [datetime.distance_in_words](https://github.com/rails/rails/blob/master/
|
906
|
+
* `distance_of_time_in_words` translates and pluralizes its result and interpolates the number of seconds, minutes, hours, and so on. See [datetime.distance_in_words](https://github.com/rails/rails/blob/master/actionview/lib/action_view/locale/en.yml#L4) translations.
|
871
907
|
|
872
|
-
* `datetime_select` and `select_month` use translated month names for populating the resulting select tag. See [date.month_names](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L15) for translations. `datetime_select` also looks up the order option from [date.order](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L18) (unless you pass the option explicitly). All date selection helpers translate the prompt using the translations in the [datetime.prompts](https://github.com/rails/rails/blob/master/
|
908
|
+
* `datetime_select` and `select_month` use translated month names for populating the resulting select tag. See [date.month_names](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L15) for translations. `datetime_select` also looks up the order option from [date.order](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L18) (unless you pass the option explicitly). All date selection helpers translate the prompt using the translations in the [datetime.prompts](https://github.com/rails/rails/blob/master/actionview/lib/action_view/locale/en.yml#L39) scope if applicable.
|
873
909
|
|
874
|
-
* The `number_to_currency`, `number_with_precision`, `number_to_percentage`, `number_with_delimiter`, and `number_to_human_size` helpers use the number format settings located in the [number](https://github.com/rails/rails/blob/master/
|
910
|
+
* The `number_to_currency`, `number_with_precision`, `number_to_percentage`, `number_with_delimiter`, and `number_to_human_size` helpers use the number format settings located in the [number](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml#L37) scope.
|
875
911
|
|
876
912
|
#### Active Model Methods
|
877
913
|
|
878
|
-
* `model_name.human` and `human_attribute_name` use translations for model names and attribute names if available in the [activerecord.models](https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml#
|
914
|
+
* `model_name.human` and `human_attribute_name` use translations for model names and attribute names if available in the [activerecord.models](https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locale/en.yml#L36) scope. They also support translations for inherited class names (e.g. for use with STI) as explained above in "Error message scopes".
|
879
915
|
|
880
916
|
* `ActiveModel::Errors#generate_message` (which is used by Active Model validations but may also be used manually) uses `model_name.human` and `human_attribute_name` (see above). It also translates the error message and supports translations for inherited class names as explained above in "Error message scopes".
|
881
917
|
|
@@ -883,7 +919,7 @@ Rails uses fixed strings and other localizations, such as format strings and oth
|
|
883
919
|
|
884
920
|
#### Active Support Methods
|
885
921
|
|
886
|
-
* `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#
|
922
|
+
* `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.
|
887
923
|
|
888
924
|
Customize your I18n Setup
|
889
925
|
-------------------------
|
@@ -917,7 +953,7 @@ ReservedInterpolationKey # the translation contains a reserved interpolation
|
|
917
953
|
UnknownFileType # the backend does not know how to handle a file type that was added to I18n.load_path
|
918
954
|
```
|
919
955
|
|
920
|
-
The I18n API will catch all of these exceptions when they are thrown in the backend and pass them to the default_exception_handler method. This method will re-raise all exceptions except for `MissingTranslationData` exceptions. When a `MissingTranslationData` exception has been caught, it will return the exception
|
956
|
+
The I18n API will catch all of these exceptions when they are thrown in the backend and pass them to the default_exception_handler method. This method will re-raise all exceptions except for `MissingTranslationData` exceptions. When a `MissingTranslationData` exception has been caught, it will return the exception's error message string containing the missing key/scope.
|
921
957
|
|
922
958
|
The reason for this is that during development you'd usually want your views to still render even though a translation is missing.
|
923
959
|
|
@@ -1000,7 +1036,7 @@ If you found this guide useful, please consider recommending its authors on [wor
|
|
1000
1036
|
Footnotes
|
1001
1037
|
---------
|
1002
1038
|
|
1003
|
-
[^1]: Or, to quote [Wikipedia](http://en.wikipedia.org/wiki/Internationalization_and_localization:
|
1039
|
+
[^1]: Or, to quote [Wikipedia](http://en.wikipedia.org/wiki/Internationalization_and_localization): _"Internationalization is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes. Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text."_
|
1004
1040
|
|
1005
1041
|
[^2]: Other backends might allow or require to use other formats, e.g. a GetText backend might allow to read GetText files.
|
1006
1042
|
|
@@ -19,7 +19,7 @@ Ruby on Rails Guides
|
|
19
19
|
<h3><%= section['name'] %></h3>
|
20
20
|
<dl>
|
21
21
|
<% section['documents'].each do |document| %>
|
22
|
-
<%= guide(document['name'], document['url'], :
|
22
|
+
<%= guide(document['name'], document['url'], work_in_progress: document['work_in_progress']) do %>
|
23
23
|
<p><%= document['description'] %></p>
|
24
24
|
<% end %>
|
25
25
|
<% end %>
|
@@ -7,14 +7,17 @@ as of Rails 4. It is an extremely in-depth guide and recommended for advanced Ra
|
|
7
7
|
After reading this guide, you will know:
|
8
8
|
|
9
9
|
* How to use `rails server`.
|
10
|
+
* The timeline of Rails' initialization sequence.
|
11
|
+
* Where different files are required by the boot sequence.
|
12
|
+
* How the Rails::Server interface is defined and used.
|
10
13
|
|
11
14
|
--------------------------------------------------------------------------------
|
12
15
|
|
13
16
|
This guide goes through every method call that is
|
14
17
|
required to boot up the Ruby on Rails stack for a default Rails 4
|
15
18
|
application, explaining each part in detail along the way. For this
|
16
|
-
guide, we will be focusing on what happens when you execute
|
17
|
-
|
19
|
+
guide, we will be focusing on what happens when you execute `rails server`
|
20
|
+
to boot your app.
|
18
21
|
|
19
22
|
NOTE: Paths in this guide are relative to Rails or a Rails application unless otherwise specified.
|
20
23
|
|
@@ -26,9 +29,42 @@ quickly.
|
|
26
29
|
Launch!
|
27
30
|
-------
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
Let's start to boot and initialize the app. A Rails application is usually
|
33
|
+
started by running `rails console` or `rails server`.
|
34
|
+
|
35
|
+
### `railties/bin/rails`
|
36
|
+
|
37
|
+
The `rails` in the command `rails server` is a ruby executable in your load
|
38
|
+
path. This executable contains the following lines:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
version = ">= 0"
|
42
|
+
load Gem.bin_path('railties', 'rails', version)
|
43
|
+
```
|
44
|
+
|
45
|
+
If you try out this command in a Rails console, you would see that this loads
|
46
|
+
`railties/bin/rails`. A part of the file `railties/bin/rails.rb` has the
|
47
|
+
following code:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
require "rails/cli"
|
51
|
+
```
|
52
|
+
|
53
|
+
The file `railties/lib/rails/cli` in turn calls
|
54
|
+
`Rails::AppRailsLoader.exec_app_rails`.
|
55
|
+
|
56
|
+
### `railties/lib/rails/app_rails_loader.rb`
|
57
|
+
|
58
|
+
The primary goal of the function `exec_app_rails` is to execute your app's
|
59
|
+
`bin/rails`. If the current directory does not have a `bin/rails`, it will
|
60
|
+
navigate upwards until it finds a `bin/rails` executable. Thus one can invoke a
|
61
|
+
`rails` command from anywhere inside a rails application.
|
62
|
+
|
63
|
+
For `rails server` the equivalent of the following command is executed:
|
64
|
+
|
65
|
+
```bash
|
66
|
+
$ exec ruby bin/rails server
|
67
|
+
```
|
32
68
|
|
33
69
|
### `bin/rails`
|
34
70
|
|
@@ -36,8 +72,8 @@ This file is as follows:
|
|
36
72
|
|
37
73
|
```ruby
|
38
74
|
#!/usr/bin/env ruby
|
39
|
-
APP_PATH = File.expand_path('../../config/application',
|
40
|
-
|
75
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
76
|
+
require_relative '../config/boot'
|
41
77
|
require 'rails/commands'
|
42
78
|
```
|
43
79
|
|
@@ -57,7 +93,8 @@ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
|
57
93
|
In a standard Rails application, there's a `Gemfile` which declares all
|
58
94
|
dependencies of the application. `config/boot.rb` sets
|
59
95
|
`ENV['BUNDLE_GEMFILE']` to the location of this file. If the Gemfile
|
60
|
-
exists, `bundler/setup` is
|
96
|
+
exists, then `bundler/setup` is required. The require is used by Bundler to
|
97
|
+
configure the load path for your Gemfile's dependencies.
|
61
98
|
|
62
99
|
A standard Rails application depends on several gems, specifically:
|
63
100
|
|
@@ -89,7 +126,9 @@ A standard Rails application depends on several gems, specifically:
|
|
89
126
|
|
90
127
|
### `rails/commands.rb`
|
91
128
|
|
92
|
-
Once `config/boot.rb` has finished, the next file that is required is
|
129
|
+
Once `config/boot.rb` has finished, the next file that is required is
|
130
|
+
`rails/commands`, which helps in expanding aliases. In the current case, the
|
131
|
+
`ARGV` array simply contains `server` which will be passed over:
|
93
132
|
|
94
133
|
```ruby
|
95
134
|
ARGV << '--help' if ARGV.empty?
|
@@ -105,31 +144,64 @@ aliases = {
|
|
105
144
|
|
106
145
|
command = ARGV.shift
|
107
146
|
command = aliases[command] || command
|
147
|
+
|
148
|
+
require 'rails/commands/commands_tasks'
|
149
|
+
|
150
|
+
Rails::CommandsTasks.new(ARGV).run_command!(command)
|
108
151
|
```
|
109
152
|
|
110
153
|
TIP: As you can see, an empty ARGV list will make Rails show the help
|
111
154
|
snippet.
|
112
155
|
|
113
|
-
If we used `s` rather than `server`, Rails
|
156
|
+
If we had used `s` rather than `server`, Rails would have used the `aliases`
|
157
|
+
defined here to find the matching command.
|
158
|
+
|
159
|
+
### `rails/commands/command_tasks.rb`
|
160
|
+
|
161
|
+
When one types an incorrect rails command, the `run_command` is responsible for
|
162
|
+
throwing an error message. If the command is valid, a method of the same name
|
163
|
+
is called.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help)
|
167
|
+
|
168
|
+
def run_command!(command)
|
169
|
+
if COMMAND_WHITELIST.include?(command)
|
170
|
+
send(command)
|
171
|
+
else
|
172
|
+
write_error_message(command)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
With the `server` command, Rails will further run the following code:
|
114
178
|
|
115
179
|
```ruby
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
180
|
+
def set_application_directory!
|
181
|
+
Dir.chdir(File.expand_path('../../', APP_PATH)) unless
|
182
|
+
File.exist?(File.expand_path("config.ru"))
|
183
|
+
end
|
184
|
+
|
185
|
+
def server
|
186
|
+
set_application_directory!
|
187
|
+
require_command!("server")
|
121
188
|
|
122
|
-
require 'rails/commands/server'
|
123
189
|
Rails::Server.new.tap do |server|
|
124
|
-
# We need to require application after the server sets environment,
|
125
|
-
# otherwise the --environment option given to the server won't propagate.
|
126
190
|
require APP_PATH
|
127
191
|
Dir.chdir(Rails.application.root)
|
128
192
|
server.start
|
129
193
|
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def require_command!(command)
|
197
|
+
require "rails/commands/#{command}"
|
198
|
+
end
|
130
199
|
```
|
131
200
|
|
132
|
-
This file will change into the Rails root directory (a path two directories up
|
201
|
+
This file will change into the Rails root directory (a path two directories up
|
202
|
+
from `APP_PATH` which points at `config/application.rb`), but only if the
|
203
|
+
`config.ru` file isn't found. This then requires `rails/commands/server` which
|
204
|
+
sets up the `Rails::Server` class.
|
133
205
|
|
134
206
|
```ruby
|
135
207
|
require 'fileutils'
|
@@ -213,12 +285,12 @@ With the `default_options` set to this:
|
|
213
285
|
```ruby
|
214
286
|
def default_options
|
215
287
|
{
|
216
|
-
:
|
217
|
-
:
|
218
|
-
:
|
219
|
-
:
|
220
|
-
:
|
221
|
-
:
|
288
|
+
environment: ENV['RACK_ENV'] || "development",
|
289
|
+
pid: nil,
|
290
|
+
Port: 9292,
|
291
|
+
Host: "0.0.0.0",
|
292
|
+
AccessLog: [],
|
293
|
+
config: "config.ru"
|
222
294
|
}
|
223
295
|
end
|
224
296
|
```
|
@@ -251,43 +323,49 @@ set earlier) is required.
|
|
251
323
|
|
252
324
|
### `config/application`
|
253
325
|
|
254
|
-
When `require APP_PATH` is executed, `config/application.rb` is loaded
|
255
|
-
|
256
|
-
on your needs.
|
326
|
+
When `require APP_PATH` is executed, `config/application.rb` is loaded (recall
|
327
|
+
that `APP_PATH` is defined in `bin/rails`). This file exists in your application
|
328
|
+
and it's free for you to change based on your needs.
|
257
329
|
|
258
330
|
### `Rails::Server#start`
|
259
331
|
|
260
|
-
After `config/application` is loaded, `server.start` is called. This method is
|
332
|
+
After `config/application` is loaded, `server.start` is called. This method is
|
333
|
+
defined like this:
|
261
334
|
|
262
335
|
```ruby
|
263
336
|
def start
|
264
|
-
|
265
|
-
puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
|
266
|
-
puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"
|
267
|
-
puts "=> Run `rails server -h` for more startup options"
|
337
|
+
print_boot_information
|
268
338
|
trap(:INT) { exit }
|
269
|
-
|
339
|
+
create_tmp_directories
|
340
|
+
log_to_stdout if options[:log_stdout]
|
341
|
+
|
342
|
+
super
|
343
|
+
...
|
344
|
+
end
|
270
345
|
|
271
|
-
|
272
|
-
|
273
|
-
|
346
|
+
private
|
347
|
+
|
348
|
+
def print_boot_information
|
349
|
+
...
|
350
|
+
puts "=> Run `rails server -h` for more startup options"
|
351
|
+
puts "=> Ctrl-C to shutdown server" unless options[:daemonize]
|
352
|
+
end
|
353
|
+
|
354
|
+
def create_tmp_directories
|
355
|
+
%w(cache pids sessions sockets).each do |dir_to_make|
|
356
|
+
FileUtils.mkdir_p(File.join(Rails.root, 'tmp', dir_to_make))
|
357
|
+
end
|
274
358
|
end
|
275
359
|
|
276
|
-
|
360
|
+
def log_to_stdout
|
277
361
|
wrapped_app # touch the app so the logger is set up
|
278
362
|
|
279
363
|
console = ActiveSupport::Logger.new($stdout)
|
280
364
|
console.formatter = Rails.logger.formatter
|
365
|
+
console.level = Rails.logger.level
|
281
366
|
|
282
367
|
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
|
283
368
|
end
|
284
|
-
|
285
|
-
super
|
286
|
-
ensure
|
287
|
-
# The '-h' option calls exit before @options is set.
|
288
|
-
# If we call 'options' with it unset, we get double help banners.
|
289
|
-
puts 'Exiting' unless @options && options[:daemonize]
|
290
|
-
end
|
291
369
|
```
|
292
370
|
|
293
371
|
This is where the first output of the Rails initialization happens. This
|
@@ -346,7 +424,7 @@ end
|
|
346
424
|
|
347
425
|
The interesting part for a Rails app is the last line, `server.run`. Here we encounter the `wrapped_app` method again, which this time
|
348
426
|
we're going to explore more (even though it was executed before, and
|
349
|
-
thus
|
427
|
+
thus memoized by now).
|
350
428
|
|
351
429
|
```ruby
|
352
430
|
@wrapped_app ||= build_app app
|
@@ -373,7 +451,7 @@ The `options[:config]` value defaults to `config.ru` which contains this:
|
|
373
451
|
```ruby
|
374
452
|
# This file is used by Rack-based servers to start the application.
|
375
453
|
|
376
|
-
require ::File.expand_path('../config/environment',
|
454
|
+
require ::File.expand_path('../config/environment', __FILE__)
|
377
455
|
run <%= app_const %>
|
378
456
|
```
|
379
457
|
|
@@ -388,7 +466,7 @@ app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
|
|
388
466
|
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:
|
389
467
|
|
390
468
|
```ruby
|
391
|
-
require ::File.expand_path('../config/environment',
|
469
|
+
require ::File.expand_path('../config/environment', __FILE__)
|
392
470
|
```
|
393
471
|
|
394
472
|
### `config/environment.rb`
|
@@ -443,7 +521,9 @@ I18n and Rails configuration are all being defined here.
|
|
443
521
|
|
444
522
|
### Back to `config/environment.rb`
|
445
523
|
|
446
|
-
|
524
|
+
The rest of `config/application.rb` defines the configuration for the
|
525
|
+
`Rails::Application` which will be used once the application is fully
|
526
|
+
initialized. When `config/application.rb` has finished loading Rails and defined
|
447
527
|
the application namespace, we go back to `config/environment.rb`,
|
448
528
|
where the application is initialized. For example, if the application was called
|
449
529
|
`Blog`, here we would find `Blog::Application.initialize!`, which is
|
@@ -462,14 +542,31 @@ def initialize!(group=:default) #:nodoc:
|
|
462
542
|
end
|
463
543
|
```
|
464
544
|
|
465
|
-
As you can see, you can only initialize an app once.
|
545
|
+
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`
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
def run_initializers(group=:default, *args)
|
550
|
+
return if instance_variable_defined?(:@ran)
|
551
|
+
initializers.tsort_each do |initializer|
|
552
|
+
initializer.run(*args) if initializer.belongs_to?(group)
|
553
|
+
end
|
554
|
+
@ran = true
|
555
|
+
end
|
556
|
+
```
|
466
557
|
|
467
|
-
|
558
|
+
The run_initializers code itself is tricky. What Rails is doing here is
|
559
|
+
traversing all the class ancestors looking for those that respond to an
|
560
|
+
`initializers` method. It then sorts the ancestors by name, and runs them.
|
561
|
+
For example, the `Engine` class will make all the engines available by
|
562
|
+
providing an `initializers` method on them.
|
468
563
|
|
469
|
-
The
|
470
|
-
|
471
|
-
|
472
|
-
|
564
|
+
The `Rails::Application` class, as defined in `railties/lib/rails/application.rb`
|
565
|
+
defines `bootstrap`, `railtie`, and `finisher` initializers. The `bootstrap` initializers
|
566
|
+
prepare the application (like initializing the logger) while the `finisher`
|
567
|
+
initializers (like building the middleware stack) are run last. The `railtie`
|
568
|
+
initializers are the initializers which have been defined on the `Rails::Application`
|
569
|
+
itself and are run between the `bootstrap` and `finishers`.
|
473
570
|
|
474
571
|
After this is done we go back to `Rack::Server`
|
475
572
|
|
@@ -546,7 +643,7 @@ def self.run(app, options={})
|
|
546
643
|
else
|
547
644
|
server.register('/', Rack::Handler::Mongrel.new(app))
|
548
645
|
end
|
549
|
-
yield server
|
646
|
+
yield server if block_given?
|
550
647
|
server.run.join
|
551
648
|
end
|
552
649
|
```
|