はじめに
Railsの認証系をなにか触ってみたく、sorceryというものがあるので何も考えずにサンプル通りに。
https://github.com/Sorcery/sorcery
sorceryは魔法という意味があるようです。魔法のような世界を体験してみます。
準備
% 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を追加します。
gem 'sorcery'
sample通り、下記を実行するもエラー。
% 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'行をコメントアウト。
16 # See https://github.com/rails/execjs#readme for more supported runtimes
17 gem 'therubyracer', platforms: :ruby
% bundle install
generatorでアプリの雛形を作成。
% 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モデル、単体テストスタブ、およびデフォルトのマイグレーションを作成。
% 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の設定してなかった・・・ので作成。
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を修正。
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
データベース作成。
データベース名にハイフンがあるとエラーになるのでバッククォートでくくって実行します。
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。
% 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で雛形作成。
% 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部分を修正。
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
_form.html.erbを修正。
<%= 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 %>
ログイン後のセッション情報周りの機能も作らないといけないのでそれも作成。
% 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ファイル。
<h1>Login</h1>
<%= render 'form' %>
<%= link_to 'Back', user_sessions_path %>
<%= 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 %>
セッションコントローラー
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
ルーティングの設定。
root :to => 'users#index'
resources :user_sessions
resources :users
get 'login' => 'user_sessions#new', :as => :login
post 'logout' => 'user_sessions#destroy', :as => :logout
サンプルにはナビゲーションまでご丁寧にあったのでそれも実装。
<!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通りにやってみた。ちゃんと動いた。