sorceryというRubyの認証便利gemを使う その1

# はじめに
Railsの認証系をなにか触ってみたく、sorceryというものがあるので何も考えずにサンプル通りに。
https://github.com/Sorcery/sorcery

sorceryは魔法という意味があるようです。魔法のような世界を体験してみます。

# 準備

 ```zsh:
% rails new sorcery-sample -d mysql
% tree -L 1
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── app
├── bin
├── config
├── config.ru
├── db
├── lib
├── log
├── public
├── test
├── tmp
└── vendor
``` 

# Simple Password Authenticationを試す
↓この通りにやる。
https://github.com/Sorcery/sorcery/wiki/Simple-Password-Authentication
Gemfileにsorceryを追加します。

 ```vim:Gemfile
gem 'sorcery'
``` 

sample通り、下記を実行するもエラー。

 ```zsh:
% rails g sorcery:install
/home/fukumura/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/bundler-1.13.6/lib/bundler/runtime.rb:94:in `rescue in block (2 levels) in require': There was an error while trying to load the gem 'uglifier'.
Gem Load Error is: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.
Backtrace for gem load error is:
・・・
``` 

See https://github.com/rails/execjs for a list of available runtimes.
と言っているので、見てみるとruntimeをサポートしているのは下記。（2017/1/12時点）

```
    therubyracer - Google V8 embedded within Ruby
    therubyrhino - Mozilla Rhino embedded within JRuby
    Duktape.rb - Duktape JavaScript interpreter
    Node.js
    Apple JavaScriptCore - Included with Mac OS X
    Microsoft Windows Script Host (JScript)
    Google V8
    mini_racer - Google V8 embedded within Ruby
```

せっかくなのでwithin Ruby の 'therubyracer'取り込む。
Gemfileの'therubyracer'行をコメントアウト。

 ```vim:Gemfile
 16 # See https://github.com/rails/execjs#readme for more supported runtimes
 17 gem 'therubyracer', platforms: :ruby
``` 

 ```zsh:
% bundle install
``` 

## generatorでアプリの雛形を作成。

 ```zsh:
% rails g sorcery:install
Running via Spring preloader in process 22607
Expected string default value for '--jbuilder'; got true (boolean)
      create  config/initializers/sorcery.rb
    generate  model User --skip-migration
Running via Spring preloader in process 22629
Expected string default value for '--jbuilder'; got true (boolean)
      invoke  active_record
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      insert  app/models/user.rb
      insert  app/models/user.rb
      create  db/migrate/20170111155833_sorcery_core.rb
``` 

## dbに反映。

続いて、初期化ファイル、Userモデル、単体テストスタブ、およびデフォルトのマイグレーションを作成。

```zsh:
% rake db:migrate
rake aborted!
Mysql2::Error: Access denied for user 'root'@'localhost'

Tasks: TOP => db:migrate
(See full trace by running task with --trace)
``` 

MySQLの設定してなかった・・・ので作成。

 ```zsh:
MariaDB [(none)]> create user fukumura@localhost identified by 'testtest';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> grant all on *.* to 'fukumura'@'localhost';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> quit
Bye
``` 

fukumuraで入れることを確認。

``` 
root@ubuntu-xenial:~# mysql -u fukumura -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 53
Server version: 10.0.28-MariaDB-0ubuntu0.16.04.1 Ubuntu 16.04

Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>
``` 

## 無事入れることを確認したので、configを修正。

 ```vim:config/database.yml
 12 default: &default
 13   adapter: mysql2
 14   encoding: utf8
 15   pool: 5
 16   username: fukumura
 17   password: testtest
 18   socket: /var/run/mysqld/mysqld.sock
``` 

## データベース作成。

データベース名にハイフンがあるとエラーになるのでバッククォートでくくって実行します。

```mysql:
MariaDB [(none)]> CREATE DATABASE sorcery-sample_development CHARACTER SET utf8;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '-sample_development CHARACTER SET utf8' at line 1
MariaDB [(none)]> CREATE DATABASE `sorcery-sample_development` CHARACTER SET utf8;
Query OK, 1 row affected (0.00 sec)
``` 
## dbに反映2。

 ```zsh:
% rake db:migrate
== 20170111155833 SorceryCore: migrating ======================================
-- create_table(:users)
   -> 0.0034s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0032s
== 20170111155833 SorceryCore: migrated (0.0067s) =============================
``` 

## generatorで雛形作成。

 ```zsh:
% rails g scaffold user email:string crypted_password:string salt:string --migration false
Running via Spring preloader in process 23067
Expected string default value for '--jbuilder'; got true (boolean)
      invoke  active_record
    conflict    app/models/user.rb
  Overwrite /home/fukumura/projects/sorcery-sample/app/models/user.rb? (enter "h" for help) [Ynaqdh] n
        skip    app/models/user.rb
      invoke    test_unit
   identical      test/models/user_test.rb
    conflict      test/fixtures/users.yml
    Overwrite /home/fukumura/projects/sorcery-sample/test/fixtures/users.yml? (enter "h" for help) [Ynaqdh] n
        skip      test/fixtures/users.yml
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    test_unit
      create      test/controllers/users_controller_test.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/users/index.json.jbuilder
      create      app/views/users/show.json.jbuilder
      create      app/views/users/_user.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.coffee
      invoke    scss
      create      app/assets/stylesheets/users.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss
``` 

user_params部分を修正。

 ```vim:app/controllers/users_controller.rb
    def user_params
      params.require(:user).permit(:email, :password, :password_confirmation)
    end
``` 

_form.html.erbを修正。

```vim:
<%= form_for(user) do |f| %>
  <% if user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :email %>
    <%= f.text_field :email %>
  </div>

  <div class="field">
     <%= f.label :password %><br />
     <%= f.password_field :password %>
  </div>
  <div class="field">
     <%= f.label :password_confirmation %><br />
     <%= f.password_field :password_confirmation %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
``` 

## ログイン後のセッション情報周りの機能も作らないといけないのでそれも作成。

 ```zsh:
% rails g controller UserSessions new create destroy
Running via Spring preloader in process 23111
Expected string default value for '--jbuilder'; got true (boolean)
Expected string default value for '--helper'; got true (boolean)
Expected string default value for '--assets'; got true (boolean)
      create  app/controllers/user_sessions_controller.rb
       route  get 'user_sessions/destroy'
       route  get 'user_sessions/create'
       route  get 'user_sessions/new'
      invoke  erb
      create    app/views/user_sessions
      create    app/views/user_sessions/new.html.erb
      create    app/views/user_sessions/create.html.erb
      create    app/views/user_sessions/destroy.html.erb
      invoke  test_unit
      create    test/controllers/user_sessions_controller_test.rb
      invoke  helper
      create    app/helpers/user_sessions_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/user_sessions.coffee
      invoke    scss
      create      app/assets/stylesheets/user_sessions.scss
``` 

viewファイル。

 ```vim:app/views/user_sessions/new.html.erb
<h1>Login</h1>

<%= render 'form' %>
<%= link_to 'Back', user_sessions_path %>
``` 

 ```app/views/user_sessions/_form.html.erb
<%= form_tag user_sessions_path, :method => :post do %>
  <div class="field">
    <%= label_tag :email %><br />
    <%= text_field_tag :email %>
  </div>
  <div class="field">
    <%= label_tag :password %><br />
    <%= password_field_tag :password %>
  </div>
  <div class="actions">
    <%= submit_tag "Login" %>
  </div>
<% end %>
``` 

セッションコントローラー

 ```vim:app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
  def new
    @user = User.new
  end

  def create
    if @user = login(params[:email], params[:password])
      redirect_back_or_to(:users, notice: 'Login successful')
    else
      flash.now[:alert] = 'Login failed'
      render action: 'new'
    end
  end

  def destroy
    logout
    redirect_to(:users, notice: 'Logged out!')
  end
end
``` 

## ルーティングの設定。

 ```vim:config/routes.rb
root :to => 'users#index'
resources :user_sessions
resources :users

get 'login' => 'user_sessions#new', :as => :login
post 'logout' => 'user_sessions#destroy', :as => :logout
``` 

## サンプルにはナビゲーションまでご丁寧にあったのでそれも実装。
 ```vim:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Tutorial</title>
      <%= stylesheet_link_tag    "application" %>
      <%= javascript_include_tag "application" %>
      <%= csrf_meta_tags %>
  </head>
  <body>

  <div id="nav">
    <% if current_user %>
      <%= link_to "Edit Profile", edit_user_path(current_user.id) %>
      <%= link_to "Logout", :logout, method: :post %>
    <% else %>
      <%= link_to "Register", new_user_path %> |
      <%= link_to "Login", :login %>
    <% end %>
  </div>
  <div>
    <p id="notice"><%= flash[:notice] %></p>
    <p id="alert"><%= flash[:alert] %></p>
  </div>
  <%= yield %>

  </body>
</html>
``` 

sample通りにやってみた。ちゃんと動いた。

ソースはこちら。https://github.com/fukumura/sorcery-sample