[8.1.1] Generate Sessions controller and tests

The elements of signing in and out correspond to particular REST actions
of the Sessions controller: the signin form is handled by the new
action, actually signing in is handled by sending a POST request to the
create action, and signing out is handled by sending a DELETE request to
the destroy action.  To get started, we'll generate a Sessions
controller and an integration test for the authentication machinery.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:sessions_controller
[8.1.1] Set up routing for Sessions controller

The signin page will live at the URI given by signin_path (defined
momentarily), and as usual we'll start with a minimalist test.

To get the tests in Listing 8.1 to pass, we first need to define routes
for the Sessions resource, together with a custom named route for the
signin page (which we'll map to the Session controller's new action).
As with the Users resource, we can use the resources method to define
the standard RESTful routes. Since we have no need to show or edit
sessions, we've restricted the actions to new, create, and destroy using
the :only option accepted by resources.  Note the use of via: :delete
for the signout route, which indicated that it should be invoked using
an HTTP DELETE request.

The next step to get the tests to pass is to add a new action to the
Sessions controller.

The final step is to define the initial version of the signin page. Note
that, since it is the page for a new session, the signin page lives in
the file app/views/sessions/new.html.erb, which we have to create.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:sessions_controller
[8.1.2] Spec out signin process

Comparing Figure 8.1 with Figure 7.11, we see that the signin form (or,
equivalently, the new session form) is similar in appearance to the
signup form, except with two fields (email and password) in place of
four. As with the signup form, we can test the signin form by using
Capybara to fill in the form values and then click the button.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:signin_tests
[8.1.3] Add signin form to signin page

With our tests in place, we're ready to start developing the signin
form. Recall that the signup form uses the form_for helper, taking as an
argument the user instance variable @user.

The main difference between this and the signin form is that we have no
Session model, and hence no analogue for the @user variable. This means
that, in constructing the new session form, we have to give form_for
slightly more information; in particular, whereas

    form_for(@user)

allows Rails to infer that the action of the form should be to POST to
the URI /users, in the case of sessions we need to indicate the name of
the resource and the corresponding URI:

    form_for(:session, url: sessions_path)

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:signin_form
[8.1.5] Add test for flash bug

The contents of the flash persist for one request, but -- unlike a
redirect -- re-rendering a template with render doesn't count as a
request.  The result is that the flash message persists one request
longer than we want. For example, if we submit invalid information, the
flash is set and gets displayed on the signin page; if we then click on
another page, such as the Home page, that's the first request since the
form submission, and the flash gets displayed again.

The tests should never pass when there is a known bug, so we should add
a failing test to catch it. Fortunately, dealing with a problem like
flash persistence is one of many areas where integration tests really
shine; they let us say exactly what we mean.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:rendering_with_a_flash_message
[8.1.5] Handle invalid signins

As in the case of creating users (signup), the first step in creating
sessions (signin) is to handle invalid input.

Recall that we displayed signup errors using the User
model error messages. These errors are associated with a particular
Active Record object, but this strategy won't work here because the
session isn't an Active Record model. Instead, we'll put a message in
the flash to be displayed upon failed signin.

Instead of flash we use flash.now, which is specifically designed for
displaying flash messages on rendered pages; unlike the contents of
flash, its contents disappear as soon as there is an additional request.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:rendering_with_a_flash_message
[8.2] Pave the way for persistent sessions

Having handled a failed signin, we now need to actually sign a user in.
The first step is easy -- completing the Sessions controller create
action is a snap.  Unfortunately, it's also a cheat.

We're now in a position to start implementing our signin model, namely,
remembering user signin status "forever" and clearing the session only
when the user explicitly signs out. The signin functions themselves will
end up crossing the traditional Model-View-Controller lines; in
particular, several signin functions will need to be available in both
controllers and views. We could make an entirely new module for
authentication, but the Sessions controller already comes equipped with
a module, namely, SessionsHelper. Moreover, such helpers are
automatically included in Rails views, so all we need to do to use the
Sessions helper functions in controllers is to include the module into
the Application controller.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:signin_success
[8.2.1] Generate remember_token migration + test

We'll start with a small addition to the User model specs. We can get
this test to pass by generating a remember token at the command line:

    $ rails generate migration add_remember_token_to_users

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:remember_me
[8.2.1] Implement and rub the migration

Next we fill in the resulting migration.  Note that, because we expect
to retrieve users by remember token, we've added an index to the
remember_token column.

Next we update the development and test databases as usual:

    $ bundle exec rake db:migrate
    $ bundle exec rake db:test:prepare

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:remember_me
[8.2.1] Add nonblank member token to User model

We'll create a remember token using a callback.  We'll use a before_save
callback, this time to create remember_token just before the user is
saved. To test for this, we first save the test user and then check that
the user's remember_token attribute isn't blank. This gives us
sufficient flexibility to change the random string if we ever need to.

The application code introduces several new elements. First, we add a
callback method to create the remember token.  This arranges for Rails
to look for a method called create_remember_token and run it before
saving the user. Second, the method itself is only used internally by
the User model, so there's no need to expose it to outside users. The
Ruby way to accomplish this is to use the private keyword.

Finally, the create_remember_token method needs to assign to one of the
user attributes, and in this context it is necessary to use the self
keyword in front of remember_token. Because of the way Active Record
synthesizes attributes based on database columns, without self the
assignment would create a local variable called remember_token, which
isn't what we want at all. Using self ensures that assignment sets the
user's remember_token so that it will be written to the database along
with the other attributes when the user is saved.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:remember_me
[8.2.2] Add session sign-in cookie

Now we're ready to write the first signin element, the sign_in function
itself. Our desired authentication method is to place a remember token
as a cookie on the user's browser, and then use the token to find the
user record in the database as the user moves from page to page.

We can use cookies as if it were a hash; each element in the cookie is
itself a hash of two elements, a value and an optional expires date. For
example, we could implement user signin by placing a cookie with value
equal to the user’s remember token that expires 20 years from now:

    cookies[:remember_token] = { value:   user.remember_token,
                                 expires: 20.years.from_now.utc }

This pattern of setting a cookie that expires 20 years in the future
became so common that Rails added a special permanent method to
implement it, so that we can simply write

    cookies.permanent[:remember_token] = user.remember_token

Under the hood, using permanent causes Rails to set the expiration to
20.years.from_now automatically.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:a_working_sign_in_method
[8.2.3] Add current_user accessors

This uses the common but initially obscure ||= ("or equals") assignment
operator. Its effect is to set the @current_user instance variable to
the user corresponding to the remember token, but only if @current_user
is undefined. In other words, the construction

    @current_user ||= User.find_by_remember_token(cookies[:remember_token])

calls the find_by_remember_token method the first time current_user is
called, but on subsequent invocations returns @current_user without
hitting the database. This is only useful if current_user is used more
than once for a single user request; in any case, find_by_remember_token
will be called at least once every time a user visits a page on the
site.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:current_user
[8.2.4] Change layout based on session

We come finally to a practical application of all our signin/out work:
we'll change the layout links based on signin status. In particular
we'll arrange for the links to change when users sign in or sign out,
and we'll also add links for listing all users and user settings and one
for the current user's profile page. In doing so, we'll get the tests to
pass, which means our test suite will be green for the first time since
the beginning of the chapter.

In the process of putting the new links into the layout, we'll take
advantage of Bootstrap's ability to make dropdown menus, which you can
read more about on the Bootstrap components page.  The dropdown menu
requires the use of Bootstrap's JavaScript library, which we can include
using the Rails asset pipeline by editing the application JavaScript
file.

Unfortunately, if you actually examine the application in the browser,
you'll see that it doesn't yet work. This is because the "remember me"
functionality requires the user to have a remember token, but the
current user doesn't have one: we created the first user back in Section
7.4.3, long before implementing the callback that sets the remember
token. To fix this, we need to save each user to invoke the before_save
callback, which creates a remember token as a side-effect:

    $ rails console
    >> User.first.remember_token
    => nil
    >> User.all.each { |user| user.save(validate: false) }
    >> User.first.remember_token
    => "Im9P0kWtZvD0RdyiK9UHtg"

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:changing_the_layout_links
[8.2.5] Automatically sign in on signup

In principle, although we are now done with authentication, newly
registered users might be confused, as they are not signed in by
default. Implementing this is the last bit of polish before letting
users sign out. We'll start by adding a line to the authentication
tests.

With the sign_in method from Section 8.2, getting this test to pass by
actually signing in the user is easy: just add sign_in @user right after
saving the user to the database.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:signin_upon_signup
[8.2.6] Add sign out functionality

Our authentication model is to keep users signed in until they sign out
explicitly. In this section, we'll add this necessary signout
capability.

So far, the Sessions controller actions have followed the RESTful
convention of using new for a signin page and create to complete the
signin. We'll continue this theme by using a destroy action to delete
sessions, i.e., to sign out. To test this, we'll click on the "Sign out"
link and then look for the reappearance of the signin link.

As with the other authentication elements, we'll put sign_out in the
Sessions helper module. The implementation is simple: we set the current
user to nil and use the delete method on cookies to remove the remember
token from the session. Setting the current user to nil isn't currently
necessary because of the immediate redirect in the destroy action, but
it's a good idea in case we ever want to use sign_out without a
redirect.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:signing_out
[8.3.1] Add cucumber-related gems

Having finished the foundation of the sample application's
authentication system, we're going to take this opportunity to show how
to write signin tests using Cucumber, a popular tool for behavior-driven
development that enjoys significant popularity in the Ruby community.

To install Cucumber, first add the cucumber-rails gem and a utility gem
called database_cleaner to the :test group in the Gemfile. Then install
as usual:

    $ bundle install

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:installation_and_setup
[8.3.1] Generate cucumber files

To set up the application to use Cucumber, we next generate some
necessary support files and directories:

    $ rails generate cucumber:install

This creates a features/ directory where the files associated with
Cucumber will live.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:installation_and_setup
[8.3.2] Add cucumber features and steps

Cucumber features are descriptions of expected behavior using a
plain-text language called Gherkin. Gherkin tests read much like
well-written RSpec examples, but because they are plain-text they are
more accessible to those more comfortable reading English than Ruby
code.

All we've done so far is write some plain text, so it shouldn't be
surprising that the Cucumber scenarios aren't yet passing. To get the
test suite to green, we need to add a step file that maps the plain-text
lines to Ruby code. The step file goes in the features/step_definitions
directory; we'll call it authentication_steps.rb.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:features_and_steps
[8.3.3] Add RSpec custom matcher for errors

Having written some simple Cucumber scenarios, it's worth comparing the
result to the equivalent RSpec examples.  You can see how a case could
be made for either Cucumber or integration tests. Cucumber features are
easily readable, but they are entirely separate from the code that
implements them -- a property that cuts both ways. I find that Cucumber
is easy to read and awkward to write, while integration tests are (for a
programmer) a little harder to read and much easier to write.

In this vein, it might make you unhappy to write

    should have_selector('div.alert.alert-error', text: 'Invalid')

in a bunch of places, when what you really want is to indicate that the
page should have an error message. This practice couples the test
tightly to the implementation, and we would have to change it everywhere
if the implementation changed. In the context of pure RSpec, there is a
solution, which is to use a custom matcher, allowing us to write the
following instead:

    should have_error_message('Invalid')

We can define such a matcher in the same utilities file where we put the
full_title test helper.

[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:rspec_custom_matchers
[8.5.1] Refactor signin: form_tag -> form_for

Refactor the signin form to use form_tag in place of form_for. Make sure
the test suite still passes. Hint: See the RailsCast on authentication
in Rails 3.1[1], and note in particular the change in the structure of
the params hash.

[1] http://railscasts.com/episodes/270-authentication-in-rails-3-1?view=asciicast
[Source] http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:sign_in_out_exercises