Ruby好き非エンジニアのブログ

〜Ruby on Railsの学習記録〜

プロフィール編集ページの作成

プロフィール編集についてのメモ。

前回ログイン周りを一通りおこなったので、今回は登録した情報の編集・更新ができるようにします。


まずは、コントローラーにeditアクションを追加します。

app/controllers/users_controller.rb

def edit
    @user = User.find(params[:id])
    render :layout => 'users'
end

※render :layout => 'users' 
・・・通常画面と会員画面のレイアウトが異なるため、会員画面のレイアウトに飛ばしています。



そして、それに対応するViewも作成していきます。
登録時の、「ニックネーム」と「パスワード」カラムに加えて、
プロフィールの編集では、「肩書き」と「自己紹介」のカラムも追加してみます。
決まりは無いので何でも良いですが、
肩書き=position
自己紹介=s_introduction
としてみました。
(肩書きの英訳はtitleですが、titleは他で使用する予定のため、別の名前にしました)


ではまずはカラムの追加。ターミナルにて以下を実行。

rails generate  migration AddEditToUsers position:string s_introduction:text
rake db:migrate

これでマイグレーションファイルが出来ました。
db/migrate/2016*******_add_edit_to_users.rb

class AddEditToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :position, :string
    add_column :users, :s_introduction, :text
  end
end


次は編集フォームの作成。
先ほど追加したカラムと、画像の登録をできるようにします。
とりあえずはgravatorでサンプル画像を表示しておきます。
(画像の登録・表示はまた別の回で)

<% provide(:title, "Edit user") %>
<h1>プロフィール編集</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
   <%= form_for(@user) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    
    <div class="profile_left">
    
    <%= f.label :ニックネーム %>
    <%= f.text_field :nickname, class: 'form-control' %>

    <%= f.label :メールアドレス %>
    <%= f.email_field :email, class: 'form-control' %>

    <%= f.label :パスワード %>
    <%= f.password_field :password, class: 'form-control' %>

    <%= f.label :パスワード確認 %>
    <%= f.password_field :password_confirmation, class: 'form-control' %>
    </div>

    <div class="profile_right">
    <%= f.label :肩書き %>
    <%= f.text_field :position, class: 'form-control' %>

    <%= f.label :自己紹介 %>
    <%= f.text_area :s_introduction, class: 'form-control' %>
    
    <div class="gravatar_edit">
     <%= gravatar_for @user %>
     <a href="http://gravatar.com/emails" target="_blank">画像編集</a>
    </div>
    </div>


    <%= f.submit "更新する", class: "btn btn-primary" %>
   <% end %>
 </div>
</div>


出来上がった画面
f:id:Jyoko:20161008114826g:plain
登録時に入力した情報は引き継がれています。
細かいCSS部分は割愛させていただきますが、元々の登録情報と、新しく追加したカラムを左右分けてみました。


続いて更新部分。
editと同様、UsersControllerにupdateアクションを追加します。
app/controllers/users_controller.rb

def update
    if @user.update_attributes(user_params)
      redirect_to @user
      flash[:success] = "プロフィールを更新しました" 
    else
      render'edit'
    end
end

更新ができたらユーザー画面へ、それ以外はedit画面へ飛びます。

更新後、ユーザー画面に表示する内容がまだできていないので、showページに情報追加します。

<% provide(:title, @user.nickname)%>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user %>
        <h2><%= @user.nickname %></h2>
        <h3><%= @user.position %></h3>
      </h1>
    </section>
  </aside>
</div>

とりあえずは、
・画像
・ニックネーム
・肩書き
をここに表示します。
肩書き・自己紹介を入力して更新を押すと
f:id:Jyoko:20161008114711g:plain

画像・ニックネーム・肩書きのみが表示されます。
f:id:Jyoko:20161008114750g:plain

最後に、過入力による負荷を避けるため、バリデーションも追加しておきます。

validates :position, length: { maximum: 30 }
validates :s_introduction, length: { maximum: 300 }

・肩書きは30文字以下
・自己紹介は300文字以下
※とりあえずこれだけ設定し、状況に合わせて修正します。

仮に肩書きを30文字以上入れてみると…
f:id:Jyoko:20161008120558g:plain
エラーのアラートが流れ、編集画面に戻されます。

ざっくりですが、プロフィールの編集・更新部分が出来上がりました。
また日本語化等等、細かいところは修正しながら進めていきます。

ユーザー登録時にログイン

前回解決できなかったユーザー登録時にheaderが切り替わらないという話。

・ログイン時はheaderが切り替わる
・ユーザー登録時はheaderが切り替わらない
というものでした。

前回の解決策として、ユーザーページを新たに作成し、そこにredirectするという方法にしましたが、
Ruby on Railsチュートリアルを見たら簡単に解決したのでメモ(汗
第8章 ログイン、ログアウト | Rails チュートリアル


まずは
app/helpers/sessions_helper.rbに下記を追加。

def log_in(user)
  session[:user_id] = user.id
end

そして、
チュートリアル通り、SessionsControllerとUsersControllerのcreateメソッドに

SessionsController

log_in user

UsersController

log_in @user

をそれぞれを追加

こんな感じになります。
SessionsController

def create
  user = login(params[:login], params[:password], params[:remember_me])
  if user
    log_in user #ここを追加
    flash[:info] = "ログインしました"
    redirect_back_or_to user
  else
    flash[:danger] = 'ニックネーム・メールアドレス もしくはパスワードが間違っています'
    render :new
  end
end

UsersController

def create 
  @user = User.new(user_params)
  if @user.save
    log_in @user #ここを追加
    flash[:success] = "Welcome to the Jammies!"
    redirect_to @user
  else
    render 'new'
  end
end

以上です。笑
とりあえず、今のところエラー無く無事動作しています。

細かいところですが、モヤモヤしていたので解決できて良かった。
ということで今回はここまで!

会員ページのviewの切り替えについて

前回未ログインユーザーと・ログインユーザーのheader切り替え部分の修正をおこなっていましたが、
できたと思った切り替えがうまくいっておらず、、
とりあえずその後あれこれ試してみたのでメモ。

とその前に、前回から変更したところは、

・ニックネーム 10文字以下
・パスワード3文字以上
・メールアドレス30文字以下
上記バリデーションだけ追加しておきました。



で、切り替えの部分ですが、確かに

・ログイン→会員ページ
への遷移の場合はちゃんとheader切り替わってました。

ですが、

・新規登録→会員ページ
へ遷移した際にはheaderが未ログイン状態のviewのままになっていました。

今まで何度かログイン部分は作成しましたが、ここの部分はスムーズだったのでなぜだろう??

とりあえずhelperから情報を呼び出す記述をしていなかったので下記を追加。

app/helpers/sessions_helper.rb

module SessionsHelper
	def current_user
		@current_user ||= User.find_by(id: session[:user_id])
	end

	def logged_in?
		!!current_user
	end

	def store_location
		session[:forwarding_url] = request.url if request.get?
	end
end

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include SessionsHelper #ここを追加

  private
  def logged_in_user
  	unless logged_in?
  		store_location
  		flash[:danger] = "ログインしてください"
  		redirect_to login_url
  	end
  end
end

これでどうだろう?と思いましたが、何も変わらず。
ちなみに、ヘッダの記述はパーシャルで下記の通り記述
app/view/layouts/_header.html.erb

<nav>
  <ul class="nav navbar-nav navbar-right">
     <li><%= link_to "Jammiesホーム", root_path %></li>
     <li><%= link_to "使い方", '#' %></li>
     <li><%= link_to "お知らせ", '#' %></li>
     <% if logged_in? %>
       <li><%= link_to "マイページ", current_user %></li>
       <li class="dropdown">
         <a href="#" class="dropdown-toggle" data-toggle="dropdown">
           アカウント <b class="caret"></b>
         </a>
           <ul class="dropdown-menu">
             <li><%= link_to "アカウント情報", current_user %></li>
             <li><%= link_to "プロフィールの編集", '#' %></li>
             <li class="divider"></li>
             <li>
             <%= link_to "ログアウト", logout_path, method: "delete" %>
             </li>
           </ul>
       </li>
     <% else %>
             <li><%= link_to "会員登録", signup_path %></li>
             <li><%= link_to "ログイン", login_path %></li>
     <% end %>
  </ul>
</nav>
<% if logged in? %>

の部分で、ログイン時しか呼び出せていないんだろうなーと思いつつ。

<% if current_user %>

にしてみたりしましたが、状況変わらず。
いまいち解決方法がわかりませんでした。

ということで少し切り替えて、会員ページごと替えてしまえー!ということで
ユーザーページごとレイアウトを切り替えることにしました。
ついでにヘッダの色も変えてわかりやすく。

app/views/layouts/に下記2つのファイルを作成
・users.html.erb
・_header_users.html.erb


users.html.erbに

<%= render 'layouts/header_users' %>

を追加し、_header_users.html.erbに飛ぶようにする。

続いて
UsersControllerとSessionsControllerに、

layout 'application'

を追加。

そして、UsersControllerに、

def show
    @user = User.find(params[:id])
    render :layout => 'users'  #ここを追加
  end

と記述。

たったこれだけですが、
とりあえず切り替えは上手くいったようです。

◆登録前
f:id:Jyoko:20160911133211g:plain

◆登録後
f:id:Jyoko:20160911133253g:plain

ヘッダーの色は白→黒へ。
登録前に表示されていた「会員登録」「ログイン」
の表記は、「マイページ」「アカウント」へ。

またいろいろと問題発生しそうですが、とりあえず今回はこの辺で終了!

ではまた!

ログイン周りの修正②

ログインする前とログインした後のヘッダメニューの切り替えをおこないます。

app/views/layouts/_header.html.erb

<header class="navbar navbar-fixed-top navbar-inverse" >
  <div class="container">
    <%= link_to "Jammies", '#', id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li><%= link_to "Jammiesホーム", '#' %></li>
        <li><%= link_to "使い方", '#' %></li>
        <li><%= link_to "お知らせ", '#' %></li>
      <% if current_user %>
        <li><%= link_to "マイページ", current_user %></li>
        <li class="dropdown">
          <a href="#" class="dropdown-toggle" data-toggle="dropdown">
            アカウント <b class="caret"></b>
          </a>
          <ul class="dropdown-menu">
            <li><%= link_to "アカウント情報", current_user %></li>
            <li><%= link_to "プロフィールの編集", '#' %></li>
            <li class="divider"></li>
            <li>
              <%= link_to "ログアウト", logout_path, method: "delete" %>
            </li>
          </ul>
        </li>
      <% else %>
        <li><%= link_to "会員登録", signup_path %></li>
        <li><%= link_to "ログイン", login_path %></li>
      <% end %>
      </ul>
     </nav>
   </div>
 </header>

・ログイン前
f:id:Jyoko:20160827183545g:plain

・ログイン後
f:id:Jyoko:20160827183221g:plain

ログイン前に表示されていた「会員登録」や「ログイン」がなくなり、「マイページ」「アカウント」が表示されました。
※「アカウント」のところはプルダウンにしてあります。

あとは、ログアウトする時、/logoutを「URL直接入力」していたのを「ログアウトボタン押下」に変更
config/routes.rb

get 'logout', to: 'sessions#destroy'
↓
delete 'logout', to: 'sessions#destroy'

f:id:Jyoko:20160827183126g:plain

プルダウンもログアウトボタンでのログアウトもできました。

ということで今回はこの辺で。

ログイン周りの修正①

今日はログイン周りの修正をメモ

現在はメールアドレスでのログインしかできないので、
ニックネームとメールアドレスでログインできるようにする。

db/migrate/***sorcery.core.rb

class SorceryCore < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :email,            :null => false
      t.string :nickname,         :null => false #ここを追加
      t.string :crypted_password
      t.string :salt

      t.timestamps
    end

    add_index :users, :email, unique: true
  end
end

tableにnicknameを追加して、rake db:migrate実行。


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 = [:nickname, :email]

最後の行に、:nicknameを追加。

ついでにmodelにvalidation追加。
とりあえず、nicknameのpresence/uniquenessを追加しました。

次はフォームの編集。

<div class="field">
  <%= label_tag :login %>
  <%= text_field_tag :login, nil, placeholder: 'ニックネームorメールアドレス', class: 'form-control' %>
</div>

params[:login]をコントローラーに渡すよう設定。


次はコントローラーの編集。
app/controllers/sessions_controller.rb

 def create
     user = login(params[:login], params[:password], params[:remember_me])
  	if user
          flash[:success] = "ログインしました!"
  	  redirect_back_or_to root_url
  	else
          flash[:danger] = 'Email or password was invalid.'
          render :new
  	end
  end

loginメソッドの第一引数は、デフォルトではparams[:email]でしたが、params[:login]を渡すようにしました。

ログイン後、トップページへ飛ぶようになっていたので、

redirect_back_or_to root_url
↓
redirect_back_or_to user

としてユーザーページに飛ぶように変更。

ログイン画面はこんな感じで。
f:id:Jyoko:20160827084321g:plain

ニックネームでログインすると…(既に「も」というニックネームを登録済み)
f:id:Jyoko:20160827084351g:plain

無事ログインできました!
f:id:Jyoko:20160827084353g:plain


とりあえず今回はここで終了。
まだ細かいところの修正をしていくので、続きは次回。

スターバックスでプログラミングはできるか?

f:id:Jyoko:20160815131847j:plain

ワイワイと賑やかな環境で作業するのが苦手で、
普段はもっぱら家でプログラミングしてます。

ただ、最近いろんな作業環境でテストしたいなーと思っていたので
たまには外で開発の続きをしてみることにしました。

ベタですが、とりあえずはコーヒー飲みながら作業できるところが良いので、カフェという選択。

カフェと言えば個人的にはドトールがお気に入り。
静かだし仕事するには最適。
仕事の日はほぼ毎日通ってます。

ただ、家の近所にはドトールは無く、wi-fi使えるカフェもほとんどないので、まずはじめにということでスタバで作業してみます。

今回の時間帯は
11:00〜13:00
時間は予め2hと決めて来店。

年齢的にあま〜い飲み物は受け付けないので、
スターバックスラテを注文。(この辺どうでもいい)


「スタバでは仕事がしにくい」なんてことを良く耳にします。
(そもそも仕事しに来るな…という意見も…)
理由は、
・複数名での来店が多く賑やか
・音楽が賑やか
なところでしょうか。

何かを集中して作業する人にとっては不向きなのかもしれませんね。


実際スタバで作業してみて:

確かに音楽は気になるかも。
当然、友人などと来ている人もいるので、店内は賑やか。

ただ、たまたまなのか読書やPC作業などをしている人も多く、
作業はとてもしやすかった。
時間も決めていったので、集中もできた。


結論:
人が少ない時間帯を狙って、時間を決めて作業すれば全く問題なし!


家族と居る時などは良く行くスタバでも作業できそう。というのは個人的には嬉しい。
気分転換にもなりいいかも。

ということで今回は場所についての投稿。
今後もいろんな所で試していきます。

sorceryによるログイン画面実装

今日はログイン画面。
 

ログイン機能の実装でまず名前があがるのがdeviseでしょうか。

単純な登録やログイン画面の作成ならdeviseはとても便利!

ただ、カスタマイズをしようとすると途端にハードルがあがる。。

 

もっと柔軟に作れるgemがいい!ということで、

今回はsorceryを使うことにしました。

GitHub - NoamB/sorcery: Magical authentication for Rails 3 & 4

 

またまた流れをメモしていきます。

 

まずはgemfileにsorceryを追加。

gem 'sorcery'

を記述した後、

$ bundle install

 

とりあえず必要な機能を実装するため下記コマンドを実行

$ rails generate sorcery:install core remember_me reset_password

・remember_me:ログイン状態の維持

・reset_password:パスワードを忘れた際の再設定

 

出来上がったdb/migrate/2016******_sorcery_core.rbはこんな感じ

class SorceryCore < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :email, :null => false
      t.string :crypted_password
      t.string :salt

      t.timestamps
    end

    add_index :users, :email, unique: true
  end
end

次はconfig/initializers/sorcery.rbのファイル設定の変更

Rails.application.config.sorcery.submodules = [:core, :remember_me, :reset_password]

 

Rails.application.config.sorcery.configure do |config|

  config.user_config do |user|

    user.username_attribute_names = [:email]

    user.reset_password_mailer = UserMailer

  end

 

  config.user_class = "User"

end

 

一応、reset_password用にメーラーを設定

$ rails g mailer UserMailer reset_password_email

 

UsersControllerは作成済みなので、ログイン処理のためのSessionsControllerを作成

$ rails g controller sessions new


/app/views/sessions/new.html.erbにログイン用のフォームも作成

<h1>Log in</h1>

    <%= form_tag sessions_path do %>
      <div class="field">
        <%= label_tag :email %>
        <%= text_field_tag :email, params[:email] %>
      </div>
      <div class="field">
        <%= label_tag :password %>
        <%= password_field_tag :password %>
      </div>
      <div class="field">
        <%= check_box_tag :remember_me, 1, params[:remember_me] %>
        <%= label_tag :remember_me %>
      </div>
      <div class="actions"><%= submit_tag "Log in" %></div>
    <% end %>

app/controllers/sessions_controller.rbに
ログイン・ログアウトの処理を記述。

class SessionsController < ApplicationController
  def new
  end

  def create
    user = login(params[:email], params[:password], params[:remember_me])
    if user
      redirect_back_or_to root_url, :notice => "Logged in!"
    else
      flash.now.alert = "Email or password was invalid."
    end
  end

  def destroy
    logout
    redirect_to root_url, :notice => "Logged out!"
  end
end

routesは、下記のように変更・追加

get 'login', to: 'sessions#new'
get 'logout', to: 'sessions#destroy'

 

resources :sessions


と、ここで久々にサーバーを起動させるとエラー発生。

Migrations are pending. To resolve this issue, run: bin/rails db:migrate RAILS_ENV=development

おお!しまった。

migration未実行によるエラー。

すかさず

$rake db:migrate

を実行。

しかしまだエラーは残ったまま。

エラーの中身をよく見てみると、

table "users" already exists

とのこと。

 

新規登録画面作成時に作ったusersテーブルとsorceryで作成したテーブルがかぶってました。

ということで、

$rails dbconsole

でdbコンソール開いて、該当するテーブルを削除

sqlite> drop table users:

これで無事解決!

あとは、 

$ rails g model user --skip-migration

でモデル・テーブルの作り直しをして、

app/models/user.rbに

authenticates_with_sorcery!

とその他validationを追加して、

 

$rails generate migration AddNicknameToUsers nickname:string

で再度nicknameカラム追加。

これでまずは新規登録が元通り実装でき、
f:id:Jyoko:20160813234210g:plain

 

ログインフォームからの
f:id:Jyoko:20160813234330g:plain


ログインも無事完了!
f:id:Jyoko:20160813234411g:plain



 

途中エラーがたくさんでましたが何とか無事実装完了。(もっと良い方法がある気がしますが…)

sorceryの認証機能は、必要に応じて随時追加していきます。

 

あとは見た目が悪いので、レイアウトやルートを直してから次に進みます。

 

参考:

#283 Authentication with Sorcery - RailsCasts

Railsでテーブルを削除したい時の対処法 - Qiita