rails 4.0.13 → 4.1.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -17
- data/guides/CHANGELOG.md +68 -34
- data/guides/assets/images/edge_badge.png +0 -0
- data/guides/assets/images/feature_tile.gif +0 -0
- data/guides/assets/images/footer_tile.gif +0 -0
- data/guides/assets/images/fxn.png +0 -0
- data/guides/assets/images/getting_started/article_with_comments.png +0 -0
- data/guides/assets/images/getting_started/challenge.png +0 -0
- data/guides/assets/images/getting_started/confirm_dialog.png +0 -0
- data/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png +0 -0
- data/guides/assets/images/getting_started/form_with_errors.png +0 -0
- data/guides/assets/images/getting_started/index_action_with_edit_link.png +0 -0
- data/guides/assets/images/getting_started/new_article.png +0 -0
- data/guides/assets/images/getting_started/rails_welcome.png +0 -0
- data/guides/assets/images/getting_started/routing_error_no_controller.png +0 -0
- data/guides/assets/images/getting_started/routing_error_no_route_matches.png +0 -0
- data/guides/assets/images/getting_started/show_action_for_articles.png +0 -0
- data/guides/assets/images/getting_started/template_is_missing_articles_new.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_create_for_articles.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_new_for_articles.png +0 -0
- data/guides/assets/images/header_tile.gif +0 -0
- data/guides/assets/images/icons/README +1 -1
- data/guides/assets/images/icons/callouts/11.png +0 -0
- data/guides/assets/images/icons/callouts/12.png +0 -0
- data/guides/assets/images/icons/callouts/13.png +0 -0
- data/guides/assets/images/icons/callouts/15.png +0 -0
- data/guides/assets/images/icons/caution.png +0 -0
- data/guides/assets/images/icons/example.png +0 -0
- data/guides/assets/images/radar.png +0 -0
- data/guides/assets/images/rails4_features.png +0 -0
- data/guides/assets/images/rails_guides_kindle_cover.jpg +0 -0
- data/guides/assets/images/vijaydev.jpg +0 -0
- data/guides/assets/javascripts/guides.js +30 -34
- data/guides/assets/stylesheets/main.css +2 -1
- data/guides/assets/stylesheets/print.css +1 -1
- data/guides/bug_report_templates/action_controller_gem.rb +9 -4
- data/guides/bug_report_templates/action_controller_master.rb +4 -2
- data/guides/bug_report_templates/active_record_gem.rb +5 -2
- data/guides/bug_report_templates/active_record_master.rb +2 -1
- data/guides/bug_report_templates/generic_gem.rb +15 -0
- data/guides/bug_report_templates/generic_master.rb +26 -0
- data/guides/code/getting_started/Gemfile +21 -24
- data/guides/code/getting_started/Gemfile.lock +78 -73
- data/guides/code/getting_started/Rakefile +1 -1
- data/guides/code/getting_started/app/assets/javascripts/application.js +1 -2
- data/guides/code/getting_started/app/views/layouts/application.html.erb +2 -2
- data/guides/code/getting_started/config/environment.rb +1 -1
- data/guides/code/getting_started/config/environments/development.rb +2 -2
- data/guides/code/getting_started/config/environments/production.rb +3 -3
- data/guides/code/getting_started/config/environments/test.rb +2 -2
- data/guides/code/getting_started/config/initializers/secret_token.rb +1 -1
- data/guides/code/getting_started/config/initializers/session_store.rb +1 -1
- data/guides/code/getting_started/config/routes.rb +1 -1
- data/guides/code/getting_started/config.ru +1 -1
- data/guides/code/getting_started/public/404.html +2 -0
- data/guides/code/getting_started/public/422.html +2 -0
- data/guides/code/getting_started/public/500.html +2 -0
- data/guides/code/getting_started/test/test_helper.rb +0 -3
- data/guides/rails_guides/helpers.rb +3 -1
- data/guides/source/2_2_release_notes.md +2 -2
- data/guides/source/2_3_release_notes.md +8 -8
- data/guides/source/3_0_release_notes.md +2 -3
- data/guides/source/3_1_release_notes.md +2 -2
- data/guides/source/3_2_release_notes.md +12 -12
- data/guides/source/4_0_release_notes.md +79 -46
- data/guides/source/4_1_release_notes.md +731 -0
- data/guides/source/_welcome.html.erb +5 -2
- data/guides/source/action_controller_overview.md +189 -40
- data/guides/source/action_mailer_basics.md +27 -27
- data/guides/source/action_view_overview.md +131 -20
- data/guides/source/active_model_basics.md +6 -6
- data/guides/source/active_record_basics.md +15 -15
- data/guides/source/active_record_callbacks.md +18 -16
- data/guides/source/active_record_querying.md +93 -51
- data/guides/source/active_record_validations.md +26 -24
- data/guides/source/active_support_core_extensions.md +72 -118
- data/guides/source/active_support_instrumentation.md +13 -4
- data/guides/source/api_documentation_guidelines.md +104 -6
- data/guides/source/asset_pipeline.md +573 -244
- data/guides/source/association_basics.md +94 -22
- data/guides/source/caching_with_rails.md +15 -6
- data/guides/source/command_line.md +55 -46
- data/guides/source/configuring.md +248 -52
- data/guides/source/contributing_to_ruby_on_rails.md +18 -17
- data/guides/source/credits.html.erb +2 -2
- data/guides/source/debugging_rails_applications.md +39 -8
- data/guides/source/development_dependencies_install.md +91 -8
- data/guides/source/documents.yaml +4 -0
- data/guides/source/engines.md +678 -232
- data/guides/source/form_helpers.md +53 -35
- data/guides/source/generators.md +19 -15
- data/guides/source/getting_started.md +758 -497
- data/guides/source/i18n.md +64 -28
- data/guides/source/index.html.erb +1 -1
- data/guides/source/initialization.md +155 -58
- data/guides/source/kindle/toc.html.erb +1 -1
- data/guides/source/layout.html.erb +2 -2
- data/guides/source/layouts_and_rendering.md +59 -26
- data/guides/source/maintenance_policy.md +3 -3
- data/guides/source/migrations.md +101 -62
- data/guides/source/nested_model_forms.md +3 -3
- data/guides/source/plugins.md +34 -31
- data/guides/source/rails_application_templates.md +27 -8
- data/guides/source/rails_on_rack.md +41 -58
- data/guides/source/routing.md +115 -104
- data/guides/source/ruby_on_rails_guides_guidelines.md +2 -2
- data/guides/source/security.md +81 -36
- data/guides/source/testing.md +56 -79
- data/guides/source/upgrading_ruby_on_rails.md +531 -21
- data/guides/source/working_with_javascript_in_rails.md +19 -11
- metadata +51 -23
- data/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png +0 -0
- data/guides/assets/images/getting_started/new_post.png +0 -0
- data/guides/assets/images/getting_started/post_with_comments.png +0 -0
- data/guides/assets/images/getting_started/show_action_for_posts.png +0 -0
- data/guides/assets/images/getting_started/template_is_missing_posts_new.png +0 -0
- data/guides/assets/images/getting_started/undefined_method_post_path.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_create_for_posts.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_new_for_posts.png +0 -0
- data/guides/assets/images/jaimeiniesta.jpg +0 -0
- data/guides/source/kindle/KINDLE.md +0 -26
data/guides/source/testing.md
CHANGED
@@ -64,7 +64,7 @@ YAML-formatted fixtures are a very human-friendly way to describe your sample da
|
|
64
64
|
Here's a sample YAML fixture file:
|
65
65
|
|
66
66
|
```yaml
|
67
|
-
# lo & behold!
|
67
|
+
# lo & behold! I am a YAML comment!
|
68
68
|
david:
|
69
69
|
name: David Heinemeier Hansson
|
70
70
|
birthday: 1979-10-15
|
@@ -141,7 +141,7 @@ NOTE: For more information on Rails <i>scaffolding</i>, refer to [Getting Starte
|
|
141
141
|
When you use `rails generate scaffold`, for a resource among other things it creates a test stub in the `test/models` folder:
|
142
142
|
|
143
143
|
```bash
|
144
|
-
$ rails generate scaffold post title:string body:text
|
144
|
+
$ bin/rails generate scaffold post title:string body:text
|
145
145
|
...
|
146
146
|
create app/models/post.rb
|
147
147
|
create test/models/post_test.rb
|
@@ -211,38 +211,16 @@ This line of code is called an _assertion_. An assertion is a line of code that
|
|
211
211
|
|
212
212
|
Every test contains one or more assertions. Only when all the assertions are successful will the test pass.
|
213
213
|
|
214
|
-
###
|
214
|
+
### Maintaining the test database schema
|
215
215
|
|
216
|
-
|
217
|
-
|
218
|
-
```bash
|
219
|
-
$ rake db:migrate
|
220
|
-
...
|
221
|
-
$ rake db:test:load
|
222
|
-
```
|
223
|
-
|
224
|
-
The `rake db:migrate` above runs any pending migrations on the _development_ environment and updates `db/schema.rb`. The `rake db:test:load` recreates the test database from the current `db/schema.rb`. On subsequent attempts, it is a good idea to first run `db:test:prepare`, as it first checks for pending migrations and warns you appropriately.
|
225
|
-
|
226
|
-
NOTE: `db:test:prepare` will fail with an error if `db/schema.rb` doesn't exist.
|
227
|
-
|
228
|
-
#### Rake Tasks for Preparing your Application for Testing
|
229
|
-
|
230
|
-
| Tasks | Description |
|
231
|
-
| ------------------------------ | ------------------------------------------------------------------------- |
|
232
|
-
| `rake db:test:clone` | Recreate the test database from the current environment's database schema |
|
233
|
-
| `rake db:test:clone_structure` | Recreate the test database from the development structure |
|
234
|
-
| `rake db:test:load` | Recreate the test database from the current `schema.rb` |
|
235
|
-
| `rake db:test:prepare` | Check for pending migrations and load the test schema |
|
236
|
-
| `rake db:test:purge` | Empty the test database. |
|
237
|
-
|
238
|
-
TIP: You can see all these rake tasks and their descriptions by running `rake --tasks --describe`
|
216
|
+
In order to run your tests, your test database will need to have the current structure. The test helper checks whether your test database has any pending migrations. If so, it will try to load your `db/schema.rb` or `db/structure.sql` into the test database. If migrations are still pending, an error will be raised.
|
239
217
|
|
240
218
|
### Running Tests
|
241
219
|
|
242
220
|
Running a test is as simple as invoking the file containing the test cases through `rake test` command.
|
243
221
|
|
244
222
|
```bash
|
245
|
-
$ rake test test/models/post_test.rb
|
223
|
+
$ bin/rake test test/models/post_test.rb
|
246
224
|
.
|
247
225
|
|
248
226
|
Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
|
@@ -253,7 +231,7 @@ Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
|
|
253
231
|
You can also run a particular test method from the test case by running the test and providing the `test method name`.
|
254
232
|
|
255
233
|
```bash
|
256
|
-
$ rake test test/models/post_test.rb test_the_truth
|
234
|
+
$ bin/rake test test/models/post_test.rb test_the_truth
|
257
235
|
.
|
258
236
|
|
259
237
|
Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
|
@@ -270,14 +248,14 @@ To see how a test failure is reported, you can add a failing test to the `post_t
|
|
270
248
|
```ruby
|
271
249
|
test "should not save post without title" do
|
272
250
|
post = Post.new
|
273
|
-
|
251
|
+
assert_not post.save
|
274
252
|
end
|
275
253
|
```
|
276
254
|
|
277
255
|
Let us run this newly added test.
|
278
256
|
|
279
257
|
```bash
|
280
|
-
$ rake test test/models/post_test.rb test_should_not_save_post_without_title
|
258
|
+
$ bin/rake test test/models/post_test.rb test_should_not_save_post_without_title
|
281
259
|
F
|
282
260
|
|
283
261
|
Finished tests in 0.044632s, 22.4054 tests/s, 22.4054 assertions/s.
|
@@ -294,7 +272,7 @@ In the output, `F` denotes a failure. You can see the corresponding trace shown
|
|
294
272
|
```ruby
|
295
273
|
test "should not save post without title" do
|
296
274
|
post = Post.new
|
297
|
-
|
275
|
+
assert_not post.save, "Saved the post without a title"
|
298
276
|
end
|
299
277
|
```
|
300
278
|
|
@@ -317,7 +295,7 @@ end
|
|
317
295
|
Now the test should pass. Let us verify by running the test again:
|
318
296
|
|
319
297
|
```bash
|
320
|
-
$ rake test test/models/post_test.rb test_should_not_save_post_without_title
|
298
|
+
$ bin/rake test test/models/post_test.rb test_should_not_save_post_without_title
|
321
299
|
.
|
322
300
|
|
323
301
|
Finished tests in 0.047721s, 20.9551 tests/s, 20.9551 assertions/s.
|
@@ -342,7 +320,7 @@ end
|
|
342
320
|
Now you can see even more output in the console from running the tests:
|
343
321
|
|
344
322
|
```bash
|
345
|
-
$ rake test test/models/post_test.rb test_should_report_error
|
323
|
+
$ bin/rake test test/models/post_test.rb test_should_report_error
|
346
324
|
E
|
347
325
|
|
348
326
|
Finished tests in 0.030974s, 32.2851 tests/s, 0.0000 assertions/s.
|
@@ -359,6 +337,17 @@ Notice the 'E' in the output. It denotes a test with error.
|
|
359
337
|
|
360
338
|
NOTE: The execution of each test method stops as soon as any error or an assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.
|
361
339
|
|
340
|
+
When a test fails you are presented with the corresponding backtrace. By default
|
341
|
+
Rails filters that backtrace and will only print lines relevant to your
|
342
|
+
application. This eliminates the framework noise and helps to focus on your
|
343
|
+
code. However there are situations when you want to see the full
|
344
|
+
backtrace. simply set the `BACKTRACE` environment variable to enable this
|
345
|
+
behavior:
|
346
|
+
|
347
|
+
```bash
|
348
|
+
$ BACKTRACE=1 bin/rake test test/models/post_test.rb
|
349
|
+
```
|
350
|
+
|
362
351
|
### What to Include in Your Unit Tests
|
363
352
|
|
364
353
|
Ideally, you would like to include a test for everything which could possibly break. It's a good practice to have at least one test for each of your validations and at least one test for every method in your model.
|
@@ -412,8 +401,8 @@ Rails adds some custom assertions of its own to the `test/unit` framework:
|
|
412
401
|
| `assert_no_difference(expressions, message = nil, &block)` | Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.|
|
413
402
|
| `assert_recognizes(expected_options, path, extras={}, message=nil)` | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.|
|
414
403
|
| `assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)` | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.|
|
415
|
-
| `assert_response(type, message = nil)` | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299,
|
416
|
-
| `assert_redirected_to(options = {}, message=nil)` | Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on
|
404
|
+
| `assert_response(type, message = nil)` | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](http://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.|
|
405
|
+
| `assert_redirected_to(options = {}, message=nil)` | Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.|
|
417
406
|
| `assert_template(expected = nil, message=nil)` | Asserts that the request was rendered with the appropriate template file.|
|
418
407
|
|
419
408
|
You'll see the usage of some of these assertions in the next chapter.
|
@@ -529,8 +518,10 @@ You also have access to three instance variables in your functional tests:
|
|
529
518
|
|
530
519
|
### Setting Headers and CGI variables
|
531
520
|
|
532
|
-
|
533
|
-
|
521
|
+
[HTTP headers](http://tools.ietf.org/search/rfc2616#section-5.3)
|
522
|
+
and
|
523
|
+
[CGI variables](http://tools.ietf.org/search/rfc3875#section-4.1)
|
524
|
+
can be set directly on the `@request` instance variable:
|
534
525
|
|
535
526
|
```ruby
|
536
527
|
# setting a HTTP Header
|
@@ -640,11 +631,11 @@ The `assert_select` assertion is quite powerful. For more advanced usage, refer
|
|
640
631
|
|
641
632
|
There are more assertions that are primarily used in testing views:
|
642
633
|
|
643
|
-
| Assertion
|
644
|
-
|
|
645
|
-
| `assert_select_email`
|
646
|
-
| `assert_select_encoded`
|
647
|
-
| `css_select(selector)`
|
634
|
+
| Assertion | Purpose |
|
635
|
+
| --------------------------------------------------------- | ------- |
|
636
|
+
| `assert_select_email` | Allows you to make assertions on the body of an e-mail. |
|
637
|
+
| `assert_select_encoded` | Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.|
|
638
|
+
| `css_select(selector)` or `css_select(element, selector)` | Returns an array of all the elements selected by the _selector_. In the second variant it first matches the base _element_ and tries to match the _selector_ expression on any of its children. If there are no matches both variants return an empty array.|
|
648
639
|
|
649
640
|
Here's an example of using `assert_select_email`:
|
650
641
|
|
@@ -662,7 +653,7 @@ Integration tests are used to test the interaction among any number of controlle
|
|
662
653
|
Unlike Unit and Functional tests, integration tests have to be explicitly created under the 'test/integration' folder within your application. Rails provides a generator to create an integration test skeleton for you.
|
663
654
|
|
664
655
|
```bash
|
665
|
-
$ rails generate integration_test user_flows
|
656
|
+
$ bin/rails generate integration_test user_flows
|
666
657
|
exists test/integration/
|
667
658
|
create test/integration/user_flows_test.rb
|
668
659
|
```
|
@@ -708,8 +699,6 @@ A simple integration test that exercises multiple controllers:
|
|
708
699
|
require 'test_helper'
|
709
700
|
|
710
701
|
class UserFlowsTest < ActionDispatch::IntegrationTest
|
711
|
-
fixtures :users
|
712
|
-
|
713
702
|
test "login and browse site" do
|
714
703
|
# login via https
|
715
704
|
https!
|
@@ -723,7 +712,7 @@ class UserFlowsTest < ActionDispatch::IntegrationTest
|
|
723
712
|
https!(false)
|
724
713
|
get "/posts/all"
|
725
714
|
assert_response :success
|
726
|
-
assert assigns(:
|
715
|
+
assert assigns(:posts)
|
727
716
|
end
|
728
717
|
end
|
729
718
|
```
|
@@ -736,10 +725,7 @@ Here's an example of multiple sessions and custom DSL in an integration test
|
|
736
725
|
require 'test_helper'
|
737
726
|
|
738
727
|
class UserFlowsTest < ActionDispatch::IntegrationTest
|
739
|
-
fixtures :users
|
740
|
-
|
741
728
|
test "login and browse site" do
|
742
|
-
|
743
729
|
# User david logs in
|
744
730
|
david = login(:david)
|
745
731
|
# User guest logs in
|
@@ -759,24 +745,24 @@ class UserFlowsTest < ActionDispatch::IntegrationTest
|
|
759
745
|
|
760
746
|
private
|
761
747
|
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
748
|
+
module CustomDsl
|
749
|
+
def browses_site
|
750
|
+
get "/products/all"
|
751
|
+
assert_response :success
|
752
|
+
assert assigns(:products)
|
753
|
+
end
|
767
754
|
end
|
768
|
-
end
|
769
755
|
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
756
|
+
def login(user)
|
757
|
+
open_session do |sess|
|
758
|
+
sess.extend(CustomDsl)
|
759
|
+
u = users(user)
|
760
|
+
sess.https!
|
761
|
+
sess.post "/login", username: u.username, password: u.password
|
762
|
+
assert_equal '/welcome', sess.path
|
763
|
+
sess.https!(false)
|
764
|
+
end
|
778
765
|
end
|
779
|
-
end
|
780
766
|
end
|
781
767
|
```
|
782
768
|
|
@@ -801,13 +787,6 @@ when you initiate a Rails project.
|
|
801
787
|
| `rake test:all` | Runs all tests quickly by merging all types and not resetting db |
|
802
788
|
| `rake test:all:db` | Runs all tests quickly by merging all types and resetting db |
|
803
789
|
|
804
|
-
There're also some test commands which you can initiate by running rake tasks:
|
805
|
-
|
806
|
-
| Tasks | Description |
|
807
|
-
| ------------------------ | ----------- |
|
808
|
-
| `rake test` | Runs all unit, functional and integration tests. You can also simply run `rake` as the _test_ target is the default.|
|
809
|
-
| `rake test:recent` | Tests recent changes|
|
810
|
-
| `rake test:uncommitted` | Runs all the tests which are uncommitted. Supports Subversion and Git|
|
811
790
|
|
812
791
|
Brief Note About `MiniTest`
|
813
792
|
-----------------------------
|
@@ -901,10 +880,9 @@ class PostsControllerTest < ActionController::TestCase
|
|
901
880
|
|
902
881
|
private
|
903
882
|
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
883
|
+
def initialize_post
|
884
|
+
@post = posts(:one)
|
885
|
+
end
|
908
886
|
end
|
909
887
|
```
|
910
888
|
|
@@ -926,7 +904,7 @@ Testing mailer classes requires some specific tools to do a thorough job.
|
|
926
904
|
|
927
905
|
### Keeping the Postman in Check
|
928
906
|
|
929
|
-
Your mailer classes
|
907
|
+
Your mailer classes - like every other part of your Rails application - should be tested to ensure that it is working as expected.
|
930
908
|
|
931
909
|
The goals of testing your mailer classes are to ensure that:
|
932
910
|
|
@@ -956,12 +934,11 @@ Here's a unit test to test a mailer named `UserMailer` whose action `invite` is
|
|
956
934
|
require 'test_helper'
|
957
935
|
|
958
936
|
class UserMailerTest < ActionMailer::TestCase
|
959
|
-
tests UserMailer
|
960
937
|
test "invite" do
|
961
938
|
# Send the email, then test that it got queued
|
962
939
|
email = UserMailer.create_invite('me@example.com',
|
963
940
|
'friend@example.com', Time.now).deliver
|
964
|
-
|
941
|
+
assert_not ActionMailer::Base.deliveries.empty?
|
965
942
|
|
966
943
|
# Test the body of the sent email contains what we expect it to
|
967
944
|
assert_equal ['me@example.com'], email.from
|
@@ -1029,7 +1006,7 @@ located under the `test/helpers` directory. Rails provides a generator which
|
|
1029
1006
|
generates both the helper and the test file:
|
1030
1007
|
|
1031
1008
|
```bash
|
1032
|
-
$ rails generate helper User
|
1009
|
+
$ bin/rails generate helper User
|
1033
1010
|
create app/helpers/user_helper.rb
|
1034
1011
|
invoke test_unit
|
1035
1012
|
create test/helpers/user_helper_test.rb
|