Deviseでの方法はいくらでもあるのですけど、sorceryについてはなかなか見つからなかったもので。
Rails version 4.2.5
Ruby version 2.3.0-p0 (x86_64-linux)
sorcery (0.9.1)
前提:公式のこちらのチュートリアルに沿って、emailでのログインは可能になっていること。つまり途中から導入する場合ということになります。
マイグレーションファイルの編集
db/migrate/****_sorcery_core.rbにて
class SorceryCore < ActiveRecord::Migration def change create_table :users do |t| t.string :email, :null => false t.string :username, :null => false # これを追加 t.string :crypted_password t.string :salt t.timestamps end add_index :users, :email, unique: true add_index :users, :username, unique: true # これを追加 end end
emailの他に、今回はusernameというカラムを作ります。
そして、rake db:migrate:resetを行いました。もうすでにDBにユーザーを作っていたのですが、それにはusernameを設定していなかったので、リセットを行うことにしました。
設定ファイルの編集
config/initializers/sorcery.rbにて
# --- user config --- config.user_config do |user| # -- core -- # specify username attributes, for example: [:username, :email]. # Default: `[:email]` # user.username_attribute_names = [:username, :email]
user.username_attribute_namesに:emailだけでなく、:usernameも追加します。
Strong Parametersへの追加
app/controllers/users_controller.rb
def user_params params.require(:user).permit(:username, :email, :password, :password_confirmation) end
User modelにバリデーション追加
app/models/user.rb
validates :email, :username, presence: true . . validates :username, uniqueness: true
:usernameにpresence: trueとuniqueness: trueのバリデーションをかけます。
ログインフォームの編集
ユーザーネーム用・email用のフォームを一つにまとめたい。
どうすればいいのかなあ、と思って試行錯誤したのですが、以下の方法にしました。
app/views/user_sessions/_form.html.erb
ログイン画面にてemail用のフィールドの代わりに、下記のコードに置き換えます。
<div class="field"> <%= label_tag :login %><br /> <%= text_field_tag :login, nil, placeholder: 'username or email' %> </div>
params[:login]というのをコントローラーに渡すようにしておきます。ここにユーザーネーム、emailのどちらかが入ることになります。

セッション用コントローラーの編集
app/controllers/user_sessions_contoller.rb
def create # params[:login] is username or email if @user = login(params[:login], params[:password], params[:remember_me]) redirect_back_or_to(:users, notice: 'Login successfully') else flash.now[:alert] = 'Login failed' render :new end end
デフォルトではloginメソッドの第1引数にparams[:email]を渡していましたが、ここではparams[:login]を渡します。
Processing by UserSessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"****", "login"=>"user1", "password"=>"[FILTERED]", "commit"=>"Login", "remember_me"=>"on"}
User Load (1.9ms) SELECT "users".* FROM "users" WHERE ("users"."username" = 'user1' OR "users"."email" = 'user1') ORDER BY "users"."id" ASC LIMIT 1
フィールドにuser1というユーザーネームでログインしたところをconsoleで見てみます。
usersテーブルのusernameかemailのどちらかにuser1があるかどうか、検索してくれていますね。
Processing by UserSessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"****", "login"=>"test@example.com", "password"=>"[FILTERED]", "commit"=>"Login", "remember_me"=>"on"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE ("users"."username" = 'test@example.com' OR "users"."email" = 'test@example.com') ORDER BY "users"."id" ASC LIMIT 1
今度はtest@example.comでログインしたところ。こちらでもOR検索してくれています。
その他
- ユーザーネームも必須にしたので、ユーザー登録画面(app/views/users/_form.html.erb)に、
username用のフォームを追加する。 - ユーザーの一覧・詳細画面にもユーザーネームを表示する
ということをしました。
参考
こちらの記事では、emailからユーザーネームでのログインに置き換える、ということをしています。
(追記)
うーん、記事を投稿してすぐに気がついたのですけど、もしユーザーネームのほうにふざけてuser@example.comなどと、emailと同じ形式で登録してしまうと、他のユーザーのemailとかぶってしまうことが無きにしもあらず、のような気がしました。
ユーザーネームには@, . (+も?)などは使えないようにバリデーションをかける必要があるかもです。
(さらに追記)
こう・・・かな?
app/models/user.rb
validates :username, format: { with: /\A\w+\z/ }
単語、数字、アンダースコアだけに制限すると。