rails 4.1.16 → 4.2.0.beta1
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 +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
@@ -1,5 +1,4 @@
|
|
1
|
-
<!DOCTYPE html
|
2
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
1
|
+
<!DOCTYPE html>
|
3
2
|
|
4
3
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
4
|
<head>
|
@@ -36,7 +35,6 @@
|
|
36
35
|
<li class="more-info"><a href="https://github.com/rails/rails">Code</a></li>
|
37
36
|
<li class="more-info"><a href="http://rubyonrails.org/screencasts">Screencasts</a></li>
|
38
37
|
<li class="more-info"><a href="http://rubyonrails.org/documentation">Documentation</a></li>
|
39
|
-
<li class="more-info"><a href="http://rubyonrails.org/ecosystem">Ecosystem</a></li>
|
40
38
|
<li class="more-info"><a href="http://rubyonrails.org/community">Community</a></li>
|
41
39
|
<li class="more-info"><a href="http://weblog.rubyonrails.org/">Blog</a></li>
|
42
40
|
</ul>
|
@@ -78,7 +76,6 @@
|
|
78
76
|
</select>
|
79
77
|
</li>
|
80
78
|
</ul>
|
81
|
-
</div>
|
82
79
|
</div>
|
83
80
|
</div>
|
84
81
|
<hr class="hide" />
|
@@ -175,22 +175,23 @@ render template: "products/show"
|
|
175
175
|
|
176
176
|
#### Rendering an Arbitrary File
|
177
177
|
|
178
|
-
The `render` method can also use a view that's entirely outside of your application:
|
178
|
+
The `render` method can also use a view that's entirely outside of your application (perhaps you're sharing views between two Rails applications):
|
179
179
|
|
180
180
|
```ruby
|
181
|
-
render
|
181
|
+
render "/u/apps/warehouse_app/current/app/views/products/show"
|
182
182
|
```
|
183
183
|
|
184
|
-
|
185
|
-
|
184
|
+
Rails determines that this is a file render because of the leading slash character. To be explicit, you can use the `:file` option (which was required on Rails 2.2 and earlier):
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
render file: "/u/apps/warehouse_app/current/app/views/products/show"
|
188
|
+
```
|
186
189
|
|
187
|
-
|
188
|
-
since an attacker could use this action to access security sensitive files in your file system.
|
190
|
+
The `:file` option takes an absolute file-system path. Of course, you need to have rights to the view that you're using to render the content.
|
189
191
|
|
190
192
|
NOTE: By default, the file is rendered without using the current layout. If you want Rails to put the file into the current layout, you need to add the `layout: true` option.
|
191
193
|
|
192
|
-
TIP: If you're running Rails on Microsoft Windows, you should use the `:file` option to
|
193
|
-
render a file, because Windows filenames do not have the same format as Unix filenames.
|
194
|
+
TIP: If you're running Rails on Microsoft Windows, you should use the `:file` option to render a file, because Windows filenames do not have the same format as Unix filenames.
|
194
195
|
|
195
196
|
#### Wrapping it up
|
196
197
|
|
@@ -262,7 +263,7 @@ TIP: This is useful when you're rendering a small snippet of HTML code.
|
|
262
263
|
However, you might want to consider moving it to a template file if the markup
|
263
264
|
is complex.
|
264
265
|
|
265
|
-
NOTE: This option will escape HTML entities if the string is not
|
266
|
+
NOTE: This option will escape HTML entities if the string is not HTML safe.
|
266
267
|
|
267
268
|
#### Rendering JSON
|
268
269
|
|
@@ -307,7 +308,7 @@ TIP: This option should be used only if you don't care about the content type of
|
|
307
308
|
the response. Using `:plain` or `:html` might be more appropriate in most of the
|
308
309
|
time.
|
309
310
|
|
310
|
-
NOTE: Unless
|
311
|
+
NOTE: Unless overridden, your response returned from this render option will be
|
311
312
|
`text/html`, as that is the default content type of Action Dispatch response.
|
312
313
|
|
313
314
|
#### Options for `render`
|
@@ -505,33 +506,33 @@ Layout declarations cascade downward in the hierarchy, and more specific layout
|
|
505
506
|
end
|
506
507
|
```
|
507
508
|
|
508
|
-
* `
|
509
|
+
* `articles_controller.rb`
|
509
510
|
|
510
511
|
```ruby
|
511
|
-
class
|
512
|
+
class ArticlesController < ApplicationController
|
512
513
|
end
|
513
514
|
```
|
514
515
|
|
515
|
-
* `
|
516
|
+
* `special_articles_controller.rb`
|
516
517
|
|
517
518
|
```ruby
|
518
|
-
class
|
519
|
+
class SpecialArticlesController < ArticlesController
|
519
520
|
layout "special"
|
520
521
|
end
|
521
522
|
```
|
522
523
|
|
523
|
-
* `
|
524
|
+
* `old_articles_controller.rb`
|
524
525
|
|
525
526
|
```ruby
|
526
|
-
class
|
527
|
+
class OldArticlesController < SpecialArticlesController
|
527
528
|
layout false
|
528
529
|
|
529
530
|
def show
|
530
|
-
@
|
531
|
+
@article = Article.find(params[:id])
|
531
532
|
end
|
532
533
|
|
533
534
|
def index
|
534
|
-
@
|
535
|
+
@old_articles = Article.older
|
535
536
|
render layout: "old"
|
536
537
|
end
|
537
538
|
# ...
|
@@ -541,10 +542,10 @@ Layout declarations cascade downward in the hierarchy, and more specific layout
|
|
541
542
|
In this application:
|
542
543
|
|
543
544
|
* In general, views will be rendered in the `main` layout
|
544
|
-
* `
|
545
|
-
* `
|
546
|
-
* `
|
547
|
-
* `
|
545
|
+
* `ArticlesController#index` will use the `main` layout
|
546
|
+
* `SpecialArticlesController#index` will use the `special` layout
|
547
|
+
* `OldArticlesController#show` will use no layout at all
|
548
|
+
* `OldArticlesController#index` will use the `old` layout
|
548
549
|
|
549
550
|
#### Avoiding Double Render Errors
|
550
551
|
|
@@ -902,7 +903,7 @@ You can also specify multiple videos to play by passing an array of videos to th
|
|
902
903
|
This will produce:
|
903
904
|
|
904
905
|
```erb
|
905
|
-
<video><source src="trailer.ogg" /><source src="
|
906
|
+
<video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
|
906
907
|
```
|
907
908
|
|
908
909
|
#### Linking to Audio Files with the `audio_tag`
|
@@ -17,9 +17,9 @@ Model setup
|
|
17
17
|
|
18
18
|
To be able to use the nested model functionality in your forms, the model will need to support some basic operations.
|
19
19
|
|
20
|
-
First of all, it needs to define a writer method for the attribute that corresponds to the association you are building a nested model form for. The `fields_for` form helper will look for this method to decide whether or not a nested model form should be
|
20
|
+
First of all, it needs to define a writer method for the attribute that corresponds to the association you are building a nested model form for. The `fields_for` form helper will look for this method to decide whether or not a nested model form should be built.
|
21
21
|
|
22
|
-
If the associated object is an array a form builder will be yielded for each object, else only a single form builder will be yielded.
|
22
|
+
If the associated object is an array, a form builder will be yielded for each object, else only a single form builder will be yielded.
|
23
23
|
|
24
24
|
Consider a Person model with an associated Address. When asked to yield a nested FormBuilder for the `:address` attribute, the `fields_for` form helper will look for a method on the Person instance named `address_attributes=`.
|
25
25
|
|
@@ -220,6 +220,6 @@ As you can see it has generated 2 `project name` inputs, one for each new `proje
|
|
220
220
|
|
221
221
|
You can basically see the `projects_attributes` hash as an array of attribute hashes, one for each model instance.
|
222
222
|
|
223
|
-
NOTE: The reason that `fields_for` constructed a
|
223
|
+
NOTE: The reason that `fields_for` constructed a hash instead of an array is that it won't work for any form nested deeper than one level deep.
|
224
224
|
|
225
225
|
TIP: You _can_ however pass an array to the writer method generated by `accepts_nested_attributes_for` if you're using plain Ruby or some other API access. See (TODO) for more info and example.
|
data/guides/source/plugins.md
CHANGED
@@ -45,7 +45,7 @@ $ bin/rails plugin new yaffle
|
|
45
45
|
See usage and options by asking for help:
|
46
46
|
|
47
47
|
```bash
|
48
|
-
$ bin/rails plugin --help
|
48
|
+
$ bin/rails plugin new --help
|
49
49
|
```
|
50
50
|
|
51
51
|
Testing Your Newly Generated Plugin
|
@@ -57,7 +57,7 @@ You can navigate to the directory that contains the plugin, run the `bundle inst
|
|
57
57
|
You should see:
|
58
58
|
|
59
59
|
```bash
|
60
|
-
|
60
|
+
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
|
61
61
|
```
|
62
62
|
|
63
63
|
This will tell you that everything got generated properly and you are ready to start adding functionality.
|
@@ -85,19 +85,19 @@ Run `rake` to run the test. This test should fail because we haven't implemented
|
|
85
85
|
|
86
86
|
```bash
|
87
87
|
1) Error:
|
88
|
-
test_to_squawk_prepends_the_word_squawk
|
89
|
-
NoMethodError: undefined method `to_squawk' for
|
90
|
-
|
88
|
+
CoreExtTest#test_to_squawk_prepends_the_word_squawk:
|
89
|
+
NoMethodError: undefined method `to_squawk' for "Hello World":String
|
90
|
+
/path/to/yaffle/test/core_ext_test.rb:5:in `test_to_squawk_prepends_the_word_squawk'
|
91
91
|
```
|
92
92
|
|
93
93
|
Great - now you are ready to start development.
|
94
94
|
|
95
|
-
In `lib/yaffle.rb`, add `require
|
95
|
+
In `lib/yaffle.rb`, add `require 'yaffle/core_ext'`:
|
96
96
|
|
97
97
|
```ruby
|
98
98
|
# yaffle/lib/yaffle.rb
|
99
99
|
|
100
|
-
require
|
100
|
+
require 'yaffle/core_ext'
|
101
101
|
|
102
102
|
module Yaffle
|
103
103
|
end
|
@@ -118,7 +118,7 @@ end
|
|
118
118
|
To test that your method does what it says it does, run the unit tests with `rake` from your plugin directory.
|
119
119
|
|
120
120
|
```bash
|
121
|
-
|
121
|
+
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
|
122
122
|
```
|
123
123
|
|
124
124
|
To see this in action, change to the test/dummy directory, fire up a console and start squawking:
|
@@ -149,7 +149,7 @@ end
|
|
149
149
|
```ruby
|
150
150
|
# yaffle/lib/yaffle.rb
|
151
151
|
|
152
|
-
require
|
152
|
+
require 'yaffle/core_ext'
|
153
153
|
require 'yaffle/acts_as_yaffle'
|
154
154
|
|
155
155
|
module Yaffle
|
@@ -196,16 +196,16 @@ When you run `rake`, you should see the following:
|
|
196
196
|
|
197
197
|
```
|
198
198
|
1) Error:
|
199
|
-
test_a_hickwalls_yaffle_text_field_should_be_last_squawk
|
199
|
+
ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk:
|
200
200
|
NameError: uninitialized constant ActsAsYaffleTest::Hickwall
|
201
|
-
|
201
|
+
/path/to/yaffle/test/acts_as_yaffle_test.rb:6:in `test_a_hickwalls_yaffle_text_field_should_be_last_squawk'
|
202
202
|
|
203
203
|
2) Error:
|
204
|
-
test_a_wickwalls_yaffle_text_field_should_be_last_tweet
|
204
|
+
ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet:
|
205
205
|
NameError: uninitialized constant ActsAsYaffleTest::Wickwall
|
206
|
-
|
206
|
+
/path/to/yaffle/test/acts_as_yaffle_test.rb:10:in `test_a_wickwalls_yaffle_text_field_should_be_last_tweet'
|
207
207
|
|
208
|
-
|
208
|
+
4 runs, 2 assertions, 0 failures, 2 errors, 0 skips
|
209
209
|
```
|
210
210
|
|
211
211
|
This tells us that we don't have the necessary models (Hickwall and Wickwall) that we are trying to test.
|
@@ -270,18 +270,18 @@ You can then return to the root directory (`cd ../..`) of your plugin and rerun
|
|
270
270
|
|
271
271
|
```
|
272
272
|
1) Error:
|
273
|
-
test_a_hickwalls_yaffle_text_field_should_be_last_squawk
|
274
|
-
NoMethodError: undefined method `yaffle_text_field' for #<Class:
|
275
|
-
|
276
|
-
|
273
|
+
ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk:
|
274
|
+
NoMethodError: undefined method `yaffle_text_field' for #<Class:0x007fd105e3b218>
|
275
|
+
activerecord (4.1.5) lib/active_record/dynamic_matchers.rb:26:in `method_missing'
|
276
|
+
/path/to/yaffle/test/acts_as_yaffle_test.rb:6:in `test_a_hickwalls_yaffle_text_field_should_be_last_squawk'
|
277
277
|
|
278
278
|
2) Error:
|
279
|
-
test_a_wickwalls_yaffle_text_field_should_be_last_tweet
|
280
|
-
NoMethodError: undefined method `yaffle_text_field' for #<Class:
|
281
|
-
|
282
|
-
|
279
|
+
ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet:
|
280
|
+
NoMethodError: undefined method `yaffle_text_field' for #<Class:0x007fd105e409c0>
|
281
|
+
activerecord (4.1.5) lib/active_record/dynamic_matchers.rb:26:in `method_missing'
|
282
|
+
/path/to/yaffle/test/acts_as_yaffle_test.rb:10:in `test_a_wickwalls_yaffle_text_field_should_be_last_tweet'
|
283
283
|
|
284
|
-
|
284
|
+
4 runs, 2 assertions, 0 failures, 2 errors, 0 skips
|
285
285
|
|
286
286
|
```
|
287
287
|
|
@@ -312,7 +312,7 @@ ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle
|
|
312
312
|
When you run `rake`, you should see the tests all pass:
|
313
313
|
|
314
314
|
```bash
|
315
|
-
|
315
|
+
4 runs, 4 assertions, 0 failures, 0 errors, 0 skips
|
316
316
|
```
|
317
317
|
|
318
318
|
### Add an Instance Method
|
@@ -386,7 +386,7 @@ ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle
|
|
386
386
|
Run `rake` one final time and you should see:
|
387
387
|
|
388
388
|
```
|
389
|
-
|
389
|
+
6 runs, 6 assertions, 0 failures, 0 errors, 0 skips
|
390
390
|
```
|
391
391
|
|
392
392
|
NOTE: The use of `write_attribute` to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use. For example, you could also use:
|
@@ -440,5 +440,5 @@ $ bin/rake rdoc
|
|
440
440
|
|
441
441
|
* [Developing a RubyGem using Bundler](https://github.com/radar/guides/blob/master/gem-development.md)
|
442
442
|
* [Using .gemspecs as Intended](http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/)
|
443
|
-
* [Gemspec Reference](http://
|
443
|
+
* [Gemspec Reference](http://guides.rubygems.org/specification-reference/)
|
444
444
|
* [GemPlugins: A Brief Introduction to the Future of Rails Plugins](http://www.intridea.com/blog/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins)
|
@@ -38,9 +38,11 @@ generate(:scaffold, "person name:string")
|
|
38
38
|
route "root to: 'people#index'"
|
39
39
|
rake("db:migrate")
|
40
40
|
|
41
|
-
|
42
|
-
git
|
43
|
-
git
|
41
|
+
after_bundle do
|
42
|
+
git :init
|
43
|
+
git add: "."
|
44
|
+
git commit: %Q{ -m 'Initial commit' }
|
45
|
+
end
|
44
46
|
```
|
45
47
|
|
46
48
|
The following sections outline the primary methods provided by the API:
|
@@ -228,6 +230,22 @@ git add: "."
|
|
228
230
|
git commit: "-a -m 'Initial commit'"
|
229
231
|
```
|
230
232
|
|
233
|
+
### after_bundle(&block)
|
234
|
+
|
235
|
+
Registers a callback to be executed after the gems are bundled and binstubs
|
236
|
+
are generated. Useful for all generated files to version control:
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
after_bundle do
|
240
|
+
git :init
|
241
|
+
git add: '.'
|
242
|
+
git commit: "-a -m 'Initial commit'"
|
243
|
+
end
|
244
|
+
```
|
245
|
+
|
246
|
+
The callbacks gets executed even if `--skip-bundle` and/or `--skip-spring` has
|
247
|
+
been passed.
|
248
|
+
|
231
249
|
Advanced Usage
|
232
250
|
--------------
|
233
251
|
|
@@ -18,7 +18,7 @@ Introduction to Rack
|
|
18
18
|
|
19
19
|
Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.
|
20
20
|
|
21
|
-
|
21
|
+
* [Rack API Documentation](http://rack.github.io/)
|
22
22
|
|
23
23
|
Explaining Rack is not really in the scope of this guide. In case you are not familiar with Rack's basics, you should check out the [Resources](#resources) section below.
|
24
24
|
|
data/guides/source/routing.md
CHANGED
@@ -183,61 +183,61 @@ You may wish to organize groups of controllers under a namespace. Most commonly,
|
|
183
183
|
|
184
184
|
```ruby
|
185
185
|
namespace :admin do
|
186
|
-
resources :
|
186
|
+
resources :articles, :comments
|
187
187
|
end
|
188
188
|
```
|
189
189
|
|
190
|
-
This will create a number of routes for each of the `
|
190
|
+
This will create a number of routes for each of the `articles` and `comments` controller. For `Admin::ArticlesController`, Rails will create:
|
191
191
|
|
192
|
-
| HTTP Verb | Path
|
193
|
-
| --------- |
|
194
|
-
| GET | /admin/
|
195
|
-
| GET | /admin/
|
196
|
-
| POST | /admin/
|
197
|
-
| GET | /admin/
|
198
|
-
| GET | /admin/
|
199
|
-
| PATCH/PUT | /admin/
|
200
|
-
| DELETE | /admin/
|
192
|
+
| HTTP Verb | Path | Controller#Action | Named Helper |
|
193
|
+
| --------- | ------------------------ | ---------------------- | ---------------------------- |
|
194
|
+
| GET | /admin/articles | admin/articles#index | admin_articles_path |
|
195
|
+
| GET | /admin/articles/new | admin/articles#new | new_admin_article_path |
|
196
|
+
| POST | /admin/articles | admin/articles#create | admin_articles_path |
|
197
|
+
| GET | /admin/articles/:id | admin/articles#show | admin_article_path(:id) |
|
198
|
+
| GET | /admin/articles/:id/edit | admin/articles#edit | edit_admin_article_path(:id) |
|
199
|
+
| PATCH/PUT | /admin/articles/:id | admin/articles#update | admin_article_path(:id) |
|
200
|
+
| DELETE | /admin/articles/:id | admin/articles#destroy | admin_article_path(:id) |
|
201
201
|
|
202
|
-
If you want to route `/
|
202
|
+
If you want to route `/articles` (without the prefix `/admin`) to `Admin::ArticlesController`, you could use:
|
203
203
|
|
204
204
|
```ruby
|
205
205
|
scope module: 'admin' do
|
206
|
-
resources :
|
206
|
+
resources :articles, :comments
|
207
207
|
end
|
208
208
|
```
|
209
209
|
|
210
210
|
or, for a single case:
|
211
211
|
|
212
212
|
```ruby
|
213
|
-
resources :
|
213
|
+
resources :articles, module: 'admin'
|
214
214
|
```
|
215
215
|
|
216
|
-
If you want to route `/admin/
|
216
|
+
If you want to route `/admin/articles` to `ArticlesController` (without the `Admin::` module prefix), you could use:
|
217
217
|
|
218
218
|
```ruby
|
219
219
|
scope '/admin' do
|
220
|
-
resources :
|
220
|
+
resources :articles, :comments
|
221
221
|
end
|
222
222
|
```
|
223
223
|
|
224
224
|
or, for a single case:
|
225
225
|
|
226
226
|
```ruby
|
227
|
-
resources :
|
227
|
+
resources :articles, path: '/admin/articles'
|
228
228
|
```
|
229
229
|
|
230
230
|
In each of these cases, the named routes remain the same as if you did not use `scope`. In the last case, the following paths map to `PostsController`:
|
231
231
|
|
232
|
-
| HTTP Verb | Path
|
233
|
-
| --------- |
|
234
|
-
| GET | /admin/
|
235
|
-
| GET | /admin/
|
236
|
-
| POST | /admin/
|
237
|
-
| GET | /admin/
|
238
|
-
| GET | /admin/
|
239
|
-
| PATCH/PUT | /admin/
|
240
|
-
| DELETE | /admin/
|
232
|
+
| HTTP Verb | Path | Controller#Action | Named Helper |
|
233
|
+
| --------- | ------------------------ | -------------------- | ---------------------- |
|
234
|
+
| GET | /admin/articles | articles#index | articles_path |
|
235
|
+
| GET | /admin/articles/new | articles#new | new_article_path |
|
236
|
+
| POST | /admin/articles | articles#create | articles_path |
|
237
|
+
| GET | /admin/articles/:id | articles#show | article_path(:id) |
|
238
|
+
| GET | /admin/articles/:id/edit | articles#edit | edit_article_path(:id) |
|
239
|
+
| PATCH/PUT | /admin/articles/:id | articles#update | article_path(:id) |
|
240
|
+
| DELETE | /admin/articles/:id | articles#destroy | article_path(:id) |
|
241
241
|
|
242
242
|
TIP: _If you need to use a different controller namespace inside a `namespace` block you can specify an absolute controller path, e.g: `get '/foo' => '/foo#index'`._
|
243
243
|
|
@@ -304,7 +304,7 @@ TIP: _Resources should never be nested more than 1 level deep._
|
|
304
304
|
One way to avoid deep nesting (as recommended above) is to generate the collection actions scoped under the parent, so as to get a sense of the hierarchy, but to not nest the member actions. In other words, to only build routes with the minimal amount of information to uniquely identify the resource, like this:
|
305
305
|
|
306
306
|
```ruby
|
307
|
-
resources :
|
307
|
+
resources :articles do
|
308
308
|
resources :comments, only: [:index, :new, :create]
|
309
309
|
end
|
310
310
|
resources :comments, only: [:show, :edit, :update, :destroy]
|
@@ -313,7 +313,7 @@ resources :comments, only: [:show, :edit, :update, :destroy]
|
|
313
313
|
This idea strikes a balance between descriptive routes and deep nesting. There exists shorthand syntax to achieve just that, via the `:shallow` option:
|
314
314
|
|
315
315
|
```ruby
|
316
|
-
resources :
|
316
|
+
resources :articles do
|
317
317
|
resources :comments, shallow: true
|
318
318
|
end
|
319
319
|
```
|
@@ -321,7 +321,7 @@ end
|
|
321
321
|
This will generate the exact same routes as the first example. You can also specify the `:shallow` option in the parent resource, in which case all of the nested resources will be shallow:
|
322
322
|
|
323
323
|
```ruby
|
324
|
-
resources :
|
324
|
+
resources :articles, shallow: true do
|
325
325
|
resources :comments
|
326
326
|
resources :quotes
|
327
327
|
resources :drafts
|
@@ -332,7 +332,7 @@ The `shallow` method of the DSL creates a scope inside of which every nesting is
|
|
332
332
|
|
333
333
|
```ruby
|
334
334
|
shallow do
|
335
|
-
resources :
|
335
|
+
resources :articles do
|
336
336
|
resources :comments
|
337
337
|
resources :quotes
|
338
338
|
resources :drafts
|
@@ -344,7 +344,7 @@ There exist two options for `scope` to customize shallow routes. `:shallow_path`
|
|
344
344
|
|
345
345
|
```ruby
|
346
346
|
scope shallow_path: "sekret" do
|
347
|
-
resources :
|
347
|
+
resources :articles do
|
348
348
|
resources :comments, shallow: true
|
349
349
|
end
|
350
350
|
end
|
@@ -352,21 +352,21 @@ end
|
|
352
352
|
|
353
353
|
The comments resource here will have the following routes generated for it:
|
354
354
|
|
355
|
-
| HTTP Verb | Path
|
356
|
-
| --------- |
|
357
|
-
| GET | /
|
358
|
-
| POST | /
|
359
|
-
| GET | /
|
360
|
-
| GET | /sekret/comments/:id/edit(.:format)
|
361
|
-
| GET | /sekret/comments/:id(.:format)
|
362
|
-
| PATCH/PUT | /sekret/comments/:id(.:format)
|
363
|
-
| DELETE | /sekret/comments/:id(.:format)
|
355
|
+
| HTTP Verb | Path | Controller#Action | Named Helper |
|
356
|
+
| --------- | -------------------------------------------- | ----------------- | ------------------------ |
|
357
|
+
| GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
|
358
|
+
| POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
|
359
|
+
| GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
|
360
|
+
| GET | /sekret/comments/:id/edit(.:format) | comments#edit | edit_comment_path |
|
361
|
+
| GET | /sekret/comments/:id(.:format) | comments#show | comment_path |
|
362
|
+
| PATCH/PUT | /sekret/comments/:id(.:format) | comments#update | comment_path |
|
363
|
+
| DELETE | /sekret/comments/:id(.:format) | comments#destroy | comment_path |
|
364
364
|
|
365
365
|
The `:shallow_prefix` option adds the specified parameter to the named helpers:
|
366
366
|
|
367
367
|
```ruby
|
368
368
|
scope shallow_prefix: "sekret" do
|
369
|
-
resources :
|
369
|
+
resources :articles do
|
370
370
|
resources :comments, shallow: true
|
371
371
|
end
|
372
372
|
end
|
@@ -374,15 +374,15 @@ end
|
|
374
374
|
|
375
375
|
The comments resource here will have the following routes generated for it:
|
376
376
|
|
377
|
-
| HTTP Verb | Path
|
378
|
-
| --------- |
|
379
|
-
| GET | /
|
380
|
-
| POST | /
|
381
|
-
| GET | /
|
382
|
-
| GET | /comments/:id/edit(.:format)
|
383
|
-
| GET | /comments/:id(.:format)
|
384
|
-
| PATCH/PUT | /comments/:id(.:format)
|
385
|
-
| DELETE | /comments/:id(.:format)
|
377
|
+
| HTTP Verb | Path | Controller#Action | Named Helper |
|
378
|
+
| --------- | -------------------------------------------- | ----------------- | --------------------------- |
|
379
|
+
| GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
|
380
|
+
| POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
|
381
|
+
| GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
|
382
|
+
| GET | /comments/:id/edit(.:format) | comments#edit | edit_sekret_comment_path |
|
383
|
+
| GET | /comments/:id(.:format) | comments#show | sekret_comment_path |
|
384
|
+
| PATCH/PUT | /comments/:id(.:format) | comments#update | sekret_comment_path |
|
385
|
+
| DELETE | /comments/:id(.:format) | comments#destroy | sekret_comment_path |
|
386
386
|
|
387
387
|
### Routing concerns
|
388
388
|
|
@@ -403,7 +403,7 @@ These concerns can be used in resources to avoid code duplication and share beha
|
|
403
403
|
```ruby
|
404
404
|
resources :messages, concerns: :commentable
|
405
405
|
|
406
|
-
resources :
|
406
|
+
resources :articles, concerns: [:commentable, :image_attachable]
|
407
407
|
```
|
408
408
|
|
409
409
|
The above is equivalent to:
|
@@ -413,7 +413,7 @@ resources :messages do
|
|
413
413
|
resources :comments
|
414
414
|
end
|
415
415
|
|
416
|
-
resources :
|
416
|
+
resources :articles do
|
417
417
|
resources :comments
|
418
418
|
resources :images, only: :index
|
419
419
|
end
|
@@ -422,7 +422,7 @@ end
|
|
422
422
|
Also you can use them in any place that you want inside the routes, for example in a scope or namespace call:
|
423
423
|
|
424
424
|
```ruby
|
425
|
-
namespace :
|
425
|
+
namespace :articles do
|
426
426
|
concerns :commentable
|
427
427
|
end
|
428
428
|
```
|
@@ -645,6 +645,8 @@ match 'photos', to: 'photos#show', via: :all
|
|
645
645
|
|
646
646
|
NOTE: Routing both `GET` and `POST` requests to a single action has security implications. In general, you should avoid routing all verbs to an action unless you have a good reason to.
|
647
647
|
|
648
|
+
NOTE: 'GET' in Rails won't check for CSRF token. You should never write to the database from 'GET' requests, for more information see the [security guide](security.html#csrf-countermeasures) on CSRF countermeasures.
|
649
|
+
|
648
650
|
### Segment Constraints
|
649
651
|
|
650
652
|
You can use the `:constraints` option to enforce a format for a dynamic segment:
|
@@ -662,26 +664,26 @@ get 'photos/:id', to: 'photos#show', id: /[A-Z]\d{5}/
|
|
662
664
|
`:constraints` takes regular expressions with the restriction that regexp anchors can't be used. For example, the following route will not work:
|
663
665
|
|
664
666
|
```ruby
|
665
|
-
get '/:id', to: '
|
667
|
+
get '/:id', to: 'articles#show', constraints: { id: /^\d/ }
|
666
668
|
```
|
667
669
|
|
668
670
|
However, note that you don't need to use anchors because all routes are anchored at the start.
|
669
671
|
|
670
|
-
For example, the following routes would allow for `
|
672
|
+
For example, the following routes would allow for `articles` with `to_param` values like `1-hello-world` that always begin with a number and `users` with `to_param` values like `david` that never begin with a number to share the root namespace:
|
671
673
|
|
672
674
|
```ruby
|
673
|
-
get '/:id', to: '
|
675
|
+
get '/:id', to: 'articles#show', constraints: { id: /\d.+/ }
|
674
676
|
get '/:username', to: 'users#show'
|
675
677
|
```
|
676
678
|
|
677
679
|
### Request-Based Constraints
|
678
680
|
|
679
|
-
You can also constrain a route based on any method on the
|
681
|
+
You can also constrain a route based on any method on the [Request object](action_controller_overview.html#the-request-object) that returns a `String`.
|
680
682
|
|
681
683
|
You specify a request-based constraint the same way that you specify a segment constraint:
|
682
684
|
|
683
685
|
```ruby
|
684
|
-
get 'photos', constraints: {subdomain: 'admin'}
|
686
|
+
get 'photos', to: 'photos#index', constraints: { subdomain: 'admin' }
|
685
687
|
```
|
686
688
|
|
687
689
|
You can also specify constraints in a block form:
|
@@ -694,6 +696,8 @@ namespace :admin do
|
|
694
696
|
end
|
695
697
|
```
|
696
698
|
|
699
|
+
NOTE: Request constraints work by calling a method on the [Request object](action_controller_overview.html#the-request-object) with the same name as the hash key and then compare the return value with the hash value. Therefore, constraint values should match the corresponding Request object method return type. For example: `constraints: { subdomain: 'api' }` will match an `api` subdomain as expected, however using a symbol `constraints: { subdomain: :api }` will not, because `request.subdomain` returns `'api'` as a String.
|
700
|
+
|
697
701
|
### Advanced Constraints
|
698
702
|
|
699
703
|
If you have a more advanced constraint, you can provide an object that responds to `matches?` that Rails should use. Let's say you wanted to route all users on a blacklist to the `BlacklistController`. You could do:
|
@@ -769,20 +773,20 @@ get '*pages', to: 'pages#show', format: true
|
|
769
773
|
You can redirect any path to another path using the `redirect` helper in your router:
|
770
774
|
|
771
775
|
```ruby
|
772
|
-
get '/stories', to: redirect('/
|
776
|
+
get '/stories', to: redirect('/articles')
|
773
777
|
```
|
774
778
|
|
775
779
|
You can also reuse dynamic segments from the match in the path to redirect to:
|
776
780
|
|
777
781
|
```ruby
|
778
|
-
get '/stories/:name', to: redirect('/
|
782
|
+
get '/stories/:name', to: redirect('/articles/%{name}')
|
779
783
|
```
|
780
784
|
|
781
785
|
You can also provide a block to redirect, which receives the symbolized path parameters and the request object:
|
782
786
|
|
783
787
|
```ruby
|
784
|
-
get '/stories/:name', to: redirect {|path_params, req| "/
|
785
|
-
get '/stories', to: redirect {|path_params, req| "/
|
788
|
+
get '/stories/:name', to: redirect { |path_params, req| "/articles/#{path_params[:name].pluralize}" }
|
789
|
+
get '/stories', to: redirect { |path_params, req| "/articles/#{req.subdomain}" }
|
786
790
|
```
|
787
791
|
|
788
792
|
Please note that this redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible.
|
@@ -791,7 +795,7 @@ In all of these cases, if you don't provide the leading host (`http://www.exampl
|
|
791
795
|
|
792
796
|
### Routing to Rack Applications
|
793
797
|
|
794
|
-
Instead of a String like `'
|
798
|
+
Instead of a String like `'articles#index'`, which corresponds to the `index` action in the `ArticlesController`, you can specify any [Rack application](rails_on_rack.html) as the endpoint for a matcher:
|
795
799
|
|
796
800
|
```ruby
|
797
801
|
match '/application.js', to: Sprockets, via: :all
|
@@ -799,7 +803,7 @@ match '/application.js', to: Sprockets, via: :all
|
|
799
803
|
|
800
804
|
As long as `Sprockets` responds to `call` and returns a `[status, headers, body]`, the router won't know the difference between the Rack application and an action. This is an appropriate use of `via: :all`, as you will want to allow your Rack application to handle all verbs as it considers appropriate.
|
801
805
|
|
802
|
-
NOTE: For the curious, `'
|
806
|
+
NOTE: For the curious, `'articles#index'` actually expands out to `ArticlesController.action(:index)`, which returns a valid Rack application.
|
803
807
|
|
804
808
|
### Using `root`
|
805
809
|
|
@@ -835,7 +839,7 @@ get 'こんにちは', to: 'welcome#index'
|
|
835
839
|
Customizing Resourceful Routes
|
836
840
|
------------------------------
|
837
841
|
|
838
|
-
While the default routes and helpers generated by `resources :
|
842
|
+
While the default routes and helpers generated by `resources :articles` will usually serve you well, you may want to customize them in some way. Rails allows you to customize virtually any generic part of the resourceful helpers.
|
839
843
|
|
840
844
|
### Specifying a Controller to Use
|
841
845
|
|
@@ -877,7 +881,7 @@ a warning.
|
|
877
881
|
You can use the `:constraints` option to specify a required format on the implicit `id`. For example:
|
878
882
|
|
879
883
|
```ruby
|
880
|
-
resources :photos, constraints: {id: /[A-Z][A-Z][0-9]+/}
|
884
|
+
resources :photos, constraints: { id: /[A-Z][A-Z][0-9]+/ }
|
881
885
|
```
|
882
886
|
|
883
887
|
This declaration constrains the `:id` parameter to match the supplied regular expression. So, in this case, the router would no longer match `/photos/1` to this route. Instead, `/photos/RR27` would match.
|
@@ -917,7 +921,7 @@ will recognize incoming paths beginning with `/photos` and route the requests to
|
|
917
921
|
|
918
922
|
### Overriding the `new` and `edit` Segments
|
919
923
|
|
920
|
-
The `:path_names` option lets you override the automatically-generated
|
924
|
+
The `:path_names` option lets you override the automatically-generated `new` and `edit` segments in paths:
|
921
925
|
|
922
926
|
```ruby
|
923
927
|
resources :photos, path_names: { new: 'make', edit: 'change' }
|
@@ -952,7 +956,7 @@ end
|
|
952
956
|
resources :photos
|
953
957
|
```
|
954
958
|
|
955
|
-
This will provide route helpers such as `admin_photos_path`, `new_admin_photo_path
|
959
|
+
This will provide route helpers such as `admin_photos_path`, `new_admin_photo_path`, etc.
|
956
960
|
|
957
961
|
To prefix a group of route helpers, use `:as` with `scope`:
|
958
962
|
|
@@ -972,15 +976,15 @@ You can prefix routes with a named parameter also:
|
|
972
976
|
|
973
977
|
```ruby
|
974
978
|
scope ':username' do
|
975
|
-
resources :
|
979
|
+
resources :articles
|
976
980
|
end
|
977
981
|
```
|
978
982
|
|
979
|
-
This will provide you with URLs such as `/bob/
|
983
|
+
This will provide you with URLs such as `/bob/articles/1` and will allow you to reference the `username` part of the path as `params[:username]` in controllers, helpers and views.
|
980
984
|
|
981
985
|
### Restricting the Routes Created
|
982
986
|
|
983
|
-
By default, Rails creates routes for the seven default actions (index
|
987
|
+
By default, Rails creates routes for the seven default actions (`index`, `show`, `new`, `create`, `edit`, `update`, and `destroy`) for every RESTful route in your application. You can use the `:only` and `:except` options to fine-tune this behavior. The `:only` option tells Rails to create only the specified routes:
|
984
988
|
|
985
989
|
```ruby
|
986
990
|
resources :photos, only: [:index, :show]
|
@@ -1042,6 +1046,28 @@ end
|
|
1042
1046
|
|
1043
1047
|
This will create routing helpers such as `magazine_periodical_ads_url` and `edit_magazine_periodical_ad_path`.
|
1044
1048
|
|
1049
|
+
### Overriding Named Route Parameters
|
1050
|
+
|
1051
|
+
The `:param` option overrides the default resource identifier `:id` (name of
|
1052
|
+
the [dynamic segment](routing.html#dynamic-segments) used to generate the
|
1053
|
+
routes). You can access that segment from your controller using
|
1054
|
+
`params[<:param>]`.
|
1055
|
+
|
1056
|
+
```ruby
|
1057
|
+
resources :videos, param: :identifier
|
1058
|
+
```
|
1059
|
+
|
1060
|
+
```
|
1061
|
+
videos GET /videos(.:format) videos#index
|
1062
|
+
POST /videos(.:format) videos#create
|
1063
|
+
new_videos GET /videos/new(.:format) videos#new
|
1064
|
+
edit_videos GET /videos/:identifier/edit(.:format) videos#edit
|
1065
|
+
```
|
1066
|
+
|
1067
|
+
```ruby
|
1068
|
+
Video.find_by(identifier: params[:identifier])
|
1069
|
+
```
|
1070
|
+
|
1045
1071
|
Inspecting and Testing Routes
|
1046
1072
|
-----------------------------
|
1047
1073
|
|