20200404のRubyに関する記事は21件です。

[*Rails*] deviseの使い方(rails6版)

はじめに

Rails6での再再検証版です。
以下のRails5のときのものをRails6で実行し、一部変更になっている部分を反映したバージョンです。
[Rails] deviseの使い方(rails5版)

【前回】
rails : 5.0.0.1
ruby  : 2.3.1
devise: 4.2.0

↓

【今回】
rails : 6.0.2.2
ruby  : 2.7.1
devise: 4.7.1

deviseとは

ユーザー登録して、送られてきたメールのリンクをクリックして本登録して、ログインして、パスワード忘れたら再設定して、何回もログインミスったらアカウントロックして…などといった認証系アプリに必要な機能を簡単に追加できる便利なgemです。

1. gemのインストール

1. プロジェクトの作成

新しいプロジェクトを作ります。

$ rails new devise_rails6
$ cd devise_rails6/

2. Gemfileの編集とインストール

以下ファイルにdeviseomniauth-twitterを追加します。

source 'https://rubygems.org'

# (省略)...

# Devise
gem 'devise'
gem 'omniauth-twitter'

gemをインストール。

$ bundle install

2. deviseの設定

devise関連ファイルを追加。
すると以下のような英文が表示されます。1から4まで順番に見ていきます。

$ rails generate devise:install

Running via Spring preloader in process 30440
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================

1. デフォルトURLの指定

英文の例に書いてあった config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } を以下のファイルに追加しました。
config.action_mailer.default_url_options = { host: 'localhost:3000' } でもOKです。

config/environments/development.rb
Rails.application.configure do
  # Settings specified here will take precedence over those in config/application.rb.

  # (省略)...

  # mailer setting
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
  # config.action_mailer.default_url_options = { host: 'localhost:3000' }
end

2. root_urlの指定

1番で指定した http://localhost:3000/ にアクセスした際に表示されるページを指定します。
現状ページは1つも作っていないため、先に追加します。

Pagesコントローラーと、indexページとshowページを追加してみます。

$ rails g controller Pages index show

routes.rb に以下を指定します。

config/routes.rb
Rails.application.routes.draw do
  root 'pages#index'
  get 'pages/show'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

3. flashメッセージの設定

ログインした時などに上の方に「ログインしました」みたいなメッセージが出るようにします。
以下のファイルの <body> タグのすぐ下に指定されたタグを挿入します。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>DeviseRails6</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <!-- ここに追加 -->
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>

    <%= yield %>
  </body>
</html>

4. deviseのViewを生成

deviseの導入で追加されるViewは、以下のコマンドを実行しなければデザインを変更できないので、デザインをカスタマイズするためにも実行します。

$ rails g devise:views

すると以下の様なファイルが生成されます。

app/views/devise/shared/_error_messages.html.erb (エラーメッセージ用パーシャル)
app/views/devise/shared/_links.html.erb (リンク用パーシャル)
app/views/devise/confirmations/new.html.erb (認証メールの再送信画面)
app/views/devise/passwords/edit.html.erb (パスワード変更画面)
app/views/devise/passwords/new.html.erb (パスワードを忘れた際、メールを送る画面)
app/views/devise/registrations/edit.html.erb (ユーザー情報変更画面)
app/views/devise/registrations/new.html.erb (ユーザー登録画面)
app/views/devise/sessions/new.html.erb (ログイン画面)
app/views/devise/unlocks/new.html.erb (ロック解除メール再送信画面)
app/views/devise/mailer/confirmation_instructions.html.erb (メール用アカウント認証文)
app/views/devise/mailer/email_changed.html.erb (メール用メールアドレス変更完了文)
app/views/devise/mailer/password_change.html.erb (メール用パスワード変更完了文)
app/views/devise/mailer/reset_password_instructions.html.erb (メール用パスワードリセット文)
app/views/devise/mailer/unlock_instructions.html.erb (メール用ロック解除文)

3. Userモデルの設定

1. Userモデルの作成

以下を実行。

$ rails g devise User

マイグレーションファイルができます。
デフォルトではこんな感じになってます。

db/migrate/20200404040003_devise_create_users.rb
# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      # t.integer  :sign_in_count, default: 0, null: false
      # t.datetime :current_sign_in_at
      # t.datetime :last_sign_in_at
      # t.string   :current_sign_in_ip
      # t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end

Userモデルはこんな感じになっています。
デフォルトでは database_authenticatableregisterablerecoverablerememberablevalidatable が使えるようになっています。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
end

2. deviseモジュール概要

各モジュールについて以下に紹介します。

機能 概要
database_authenticatable サインイン時にユーザーの正当性を検証するためにパスワードをハッシュ化してDBに登録します。認証方法としてはPOSTリクエストかHTTP Basic認証が使えます。
registerable 登録処理を通してユーザーをサインアップします。また、ユーザーに自身のアカウントを編集したり削除できるようにします。
recoverable パスワードをリセットし、それを通知します。
rememberable 保存されたcookieから、ユーザーを記憶するためのトークンを生成・削除します。
trackable サインイン回数や、サインイン時間、IPアドレスを記録します。
validatable Emailやパスワードのバリデーションを提供します。独自に定義したバリデーションを追加することもできます。
confirmable メールに記載されているURLをクリックして本登録を完了する、といったよくある登録方式を提供します。また、サインイン中にアカウントが認証済みかどうかを検証します。
lockable 一定回数サインインを失敗するとアカウントをロックします。ロック解除にはメールによる解除か、一定時間経つと解除するといった方法があります。
timeoutable 一定時間活動していないアカウントのセッションを破棄します。
omniauthable intridea/omniauthをサポートします。TwitterやFacebookなどの認証を追加したい場合はこれを使用します。

3. Userモデルの編集

今回はデフォルトではないものも触ってみたいと思うので全部入れてみます。
Twitter認証を使うのでTwitterを指定しています。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable, :lockable, :timeoutable, :trackable, :omniauthable, omniauth_providers:[:twitter]
end

4. マイグレーションファイルの編集

上に合わせて使用モジュールに対応する部分のコメントアウトを外します。今回は全部使うので全部外します。
全部を使用しない場合は 3. で使うことにしたものに関連する項目だけコメントアウトを外してください。

db/migrate/20200404040003_devise_create_users.rb
# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      t.string   :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at
      t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      t.string   :unlock_token # Only if unlock strategy is :email or :both
      t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    add_index :users, :confirmation_token,   unique: true
    add_index :users, :unlock_token,         unique: true
  end
end

5. omniauth-twitter用カラムの追加

ついでに omniauth-twitter で使う provideruid 、それとTwitter認証の場合はアカウント名を保存しておきたいので username もUserテーブルに追加します。
その他、保存したい項目があったら arunagw / omniauth-twitter の認証時のハッシュ情報を参考に保存用カラムを追加してください。

$ rails g migration add_columns_to_users provider uid username

以下のようなマイグレーションファイルができます。

db/migrate/20200404042756_add_columns_to_users.rb
class AddColumnsToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :username, :string
  end
end

ここまで出来たら以下を実行します。

$ rake db:migrate

4. viewの編集

以下のファイルを編集して、ページ上部にメニューが出るようにします。
user_signed_in? はdeviseのHelperメソッドです。
ログインしているかしてないかで上部のメニューの表示が変わるようになります。
current_user で現在サインインしているユーザーの情報を取得できます。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>DeviseRails6</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <header>
      <nav>
          <% if user_signed_in? %>
            <strong><%= link_to current_user.username, pages_show_path %></strong>
            <%= link_to 'プロフィール変更', edit_user_registration_path %>
            <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
          <% else %>
            <%= link_to 'サインアップ', new_user_registration_path %>
            <%= link_to 'ログイン', new_user_session_path %>
          <% end %>
      </nav>
    </header>
    <!-- ここに追加 -->
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>

    <%= yield %>
  </body>
</html>

以下の2ページも修正。
indexの方がトップページ、showの方がログインしているユーザー用のページになる予定です。

app/views/pages/index.html.erb
<h1>ようこそ</h1>
<p>トップページです。</p>
app/views/pages/show.html.erb
<h1>こんにちは、<%= current_user.username %>さん</h1>
<p>ユーザー用ページです。</p>

サーバーを立ち上げて、「サインアップ」を押下すると、 app/models/user.rbomniauthable を設定しているのに何の設定もしていないので現状ではエラーになります。

$ brew install yarn
$ rails webpacker:install
$ rails s

スクリーンショット 2020-04-04 13.39.59.png

スクリーンショット 2020-04-04 13.40.38.png

omniauthable を設定していない場合はサインアップページが表示されます。

Twitterで認証する

1. 設定

1. Twitter Developerの登録

Twitter Developerにアクセスし、 Create an app をクリックし(ツイッターにログインしてないとボタンが出ません)、情報を入力にします。
審査がいるようになってしまったので、私がやったときは何週間かかかった気がします。
私は今回、rails5版のこの記事を書いたときのものを使用しています。

スクリーンショット 2020-04-04 13.49.43.png

新規申請する場合は、申請時に Enable Sign in with Twitter にチェックを入れておいてください。

スクリーンショット 2020-04-04 13.52.47.png

2. 設定ファイルの編集

Keys and tokens タブを開き、 API keyAPI secret key を以下の該当箇所にコピーして貼り付けます。

スクリーンショット 2020-04-04 13.55.46.png

callback_urlhttp://127.0.0.1:3000/users/auth/twitter/callback にし、Twitter Developer側のコールバックの設定も変更する必要があるようです。
なので、以下の用に修正しました。

スクリーンショット 2020-04-04 14.16.43.png

config/initializers/devise.rb
Devise.setup do |config|
  # (省略)...

  config.omniauth :twitter, 'API Key', 'API Secret', callback_url: 'http://127.0.0.1:3000/users/auth/twitter/callback'
end

2. 動作確認

サーバーを立ち上げてサインアップページの下の方にあるSign in with Twitterというリンクをクリックします。

すると以下の様な画面が開くのでログインして 連携アプリを認証 をクリックします。

スクリーンショット 2020-04-04 14.16.18.png

こんな画面が出るはずです。

スクリーンショット 2020-04-04 14.20.44.png

3. コールバック用コントローラーの作成

Twitter認証後適切に画面が遷移するように以下を実行してコントローラーを作ります。

$ rails g controller omniauth_callbacks

作成したコントローラーの中身を以下のように修正します。
継承するのが Devise::OmniauthCallbacksController になっていることに注意です。
omniauth.auth という環境変数に認証に関する情報が入っています。
その情報を使ってユーザーが登録されているかを検証し、登録してる場合はログイン、登録されてない場合は登録用ページに遷移します。

app/controllers/omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def twitter
    @user = User.from_omniauth(request.env["omniauth.auth"].except("extra"))

    if @user.persisted?
        sign_in_and_redirect @user
    else
        session["devise.user_attributes"] = @user.attributes
        redirect_to new_user_registration_url
    end
  end
end

ちなみに omniauth.auth の中にはこんなものが入ってます。(一部の値は適当に変えてあります。)

{
  "provider"=>"twitter",
  "uid"=>"0123456789",
  "info"=>{
    "nickname"=>"manycicadas",
    "name"=>"芭蕉",
    "location"=>"関東",
    "image"=>"http://pbs.twimg.com/profile_images/483964583371997185/2ZqzhzKV_normal.png",
    "description"=>"JavaEE/Ruby(Rails)/HTML/CSS/JavaScript/Raspberry Pi などなどが好き。",
    "urls"=>{
      "Website"=>nil,
      "Twitter"=>"https://twitter.com/manycicadas"
    }
  },
  "credentials"=>{
    "token"=>"xxx",
    "secret"=>"xxxxxx"
  }
  (省略)...
}

Userモデルに self.from_omniauthself.new_with_session を作ります。
self.from_omniauth では uidprovider で検索してあったらそれを、無かったらレコードを作ります。
self.new_with_session については、もしこのメソッドを追加しておかなければ、Twitter認証後サインアップページで登録を行っても、認証情報として取ってきたuidやproviderなどが登録されません。それらが登録されないのでTwitterで認証しても登録されてないユーザーとして毎回サインアップページに飛ばされます。

app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable, :lockable, :timeoutable, :trackable, :omniauthable, omniauth_providers:[:twitter]

  def self.from_omniauth(auth)
    find_or_create_by(provider: auth["provider"], uid: auth["uid"]) do |user|
      user.provider = auth["provider"]
      user.uid = auth["uid"]
      user.username = auth["info"]["nickname"]
    end
  end

  def self.new_with_session(params, session)
    if session["devise.user_attributes"]
      new(session["devise.user_attributes"]) do |user|
        user.attributes = params
      end
    else
      super
    end
  end
end

以下ファイルを編集して、コールバック用のコントローラーとしてさっき作ったコントローラーが呼ばれるようにします。これを書かないとdevise側のコントローラーが呼ばれます。

config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: { :omniauth_callbacks => "omniauth_callbacks" }

  root 'pages#index'
  get 'pages/show'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

これでTwitter認証ができるようになりました。
初回、Twitter認証を行うと、サインアップページに飛ばされ、そこでメールアドレスやパスワードを入力して登録するとユーザー情報が登録されます。
今回は comfirmable 機能を入れているので、登録したら確認メッセージを送ったとのメッセージが出て、そのままログインすることはできません。
この機能を入れてなかった場合、登録すると即ログインします。

アカウント登録確認メールを送る

1. comfirmable概要

多くの登録系サイトで採用されている、登録すると仮登録状態になり、届いたメールのリンクをクリックするとログイン可能になるという仕組みを追加できるのがcomfirmableです。
今回はGmailのアカウントを使って実際にメールが届くように設定します。

2. ログを見る

log/deployments.log
Devise::Mailer#confirmation_instructions: processed outbound mail in 8.8ms
Delivered mail 5e881d3772753_89a53b9c70510@SampleMacBook-Pro.local.mail (22.3ms)
Date: Sat, 04 Apr 2020 14:37:59 +0900
From: please-change-me-at-config-initializers-devise@example.com
Reply-To: please-change-me-at-config-initializers-devise@example.com
To: sample@example.com
Message-ID: <5e881d3772753_89a53b9c70510@SampleMacBook-Pro.local.mail>
Subject: Confirmation instructions
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

<p>Welcome sample@example.com!</p>

<p>You can confirm your account email through the link below:</p>

<p><a href="http://localhost:3000/users/confirmation?confirmation_token=1vus7S8UesPoay5yz2je">Confirm my account</a></p>

3. メールが実際に届くようにする

1. 設定ファイルの編集

今回はgmailを使うのでメールアドレスにはgmailを設定します。
デフォルトの設定があると思うので、それを変更してください。

config/initializers/devise.rb
Devise.setup do |config|
  # (省略)...
  config.mailer_sender = "メールアドレス"
end

gmailの場合はGメールアドレスとGメールパスワードの部分を自分のアカウントのものに変更します。

config/environments/development.rb
Rails.application.configure do
  # (省略)...

  # mailer setting
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
  # config.action_mailer.default_url_options = { host: 'localhost:3000' }
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    :address => 'smtp.gmail.com',
    :port => 587,
    :user_name => 'Gメールアドレス',
    :password => 'Gメールパスワード',
    :authentication => :plain,
    :enable_starttls_auto => true
  }
end

2. Gmailの設定

1. とりあえず試したい場合

デフォルトでは安全性の低いアプリケーションからGmailへのアクセスが制限されています。
よってその制限を解除することで動くようにできます。
ただ、安全性の低いアプリからのアクセスを許可するということはセキュリティ的にはいまいちなのであくまで動作確認ように利用することをおすすめします。
安全性の低いアプリがアカウントにアクセスするのを許可する の安全性の低いアプリを許可というリンクをクリックして、以下のように設定します。

スクリーンショット 2020-04-04 14.48.41.png

2. 2段階認証がオンの場合

2段階認証をオンにしている場合、1の設定はできないはずなのでこちらをやってください。
また、1のセキュリティ的にイマイチな方をやりたくない方もこの際2段階認証をオンにしてこの設定をやってください。

アカウントへのアクセスとセキュリティ設定の管理を開きます。
2段階認証がオフの場合は2段階認証プロセスから2段階認証の設定をしてください。(2 段階認証プロセスを有効にする)
2段階認証設定を行っている場合はアプリパスワードを開きます。

スクリーンショット 2020-04-04 15.00.16.png

アプリを選択その他 を選択します。

スクリーンショット 2020-04-04 15.00.51.png

適当に名前をつけます。

スクリーンショット 2020-04-04 15.01.50.png

パスワードが生成されます。

スクリーンショット 2020-04-04 15.04.21.png

生成されたパスワードを、1の設定の :password => "Gメールパスワード", のGメールのパスワードの部分に設定します。

3. 届いたメールを確認

これでアプリ側からサインアップすると、以下の様なメールが届くようになります。 Confirm my account をクリックするログイン画面からログインが可能になります。

スクリーンショット 2020-04-04 15.08.30.png

メールで送られる文章は以下のファイルを編集することで可能です。
app/views/devise/confirmation_instructions.html.erb

アカウントをロックする

1. lockable概要

アカウントの認証を一定回数間違うと、アカウントをロックするようにする機能です。

2. 設定

1. 設定ファイルの編集

以下ファイルを修正して、アカウントの認証を5回失敗します。

config/initializers/devise.rb
Devise.setup do |config|
    # (省略)...

    # lock sessings
    config.unlock_strategy = :email
    config.maximum_attempts = 4
end

するとアカウントがロックされてこのようなメールが届きます。

スクリーンショット 2020-04-04 15.14.29.png

メールの中身は app/views/devise/mailer/unlock_instructions.rb ファイルを修正すれば変わります。

2. 設定値について

  • lock_strategy(ロック方法)
属性 説明
:failed_attempts 失敗回数によってロック。
:none ロックしない。
  • unlock_strategy(ロック解除方法)
属性 説明
:time 指定時間でロックを解除する。
:email メールでロックを解除する。
:both :timeと:emailの両方。
:none 解除させない。

:none を指定した場合、ユーザーのレコードのlocked_atカラムをnilにアップデートしたらロックが解除できます。

  • unlock_in(ロック解除時間)

2時間で解除するならconfig.unlock_in = 2.hoursといった具合に指定。

  • unlock_keys

アカウントをロックまたは解除するときに使用するキーを定義するらしいです。
config.unlock_keys = [:username]という感じで指定できます。

  • maximum_attempts(失敗可能回数)

アカウントの認証を失敗して良い回数を指定します。
config.maximum_attempts = 4と指定した場合、4回目までは失敗しても大丈夫ですが、5回目を失敗した時点でアカウントがロックされます。

セッションをタイムアウトする

1. timeoutable概要

一定時間活動がない場合にセッションをタイムアウトさせるのがtimeoutableです。

2. 設定

以下ファイルを修正するとタイムアウトまでの時間を指定できます。
以下の場合だと3分後にセッションがタイムアウトします。
デフォルトは30分だそうです。

config/initializers/devise.rb
Devise.setup do |config|
    (省略)...
  # timeout setting
  config.timeout_in = 3.minutes
end

その他の設定

1. ログイン後のページを変更する

ログインすると、デフォルトでは root_url に飛ばされます。
これを app/views/pages/show.html.erb になるように修正します。
after_sign_in_path_for メソッドを追加します。ここにログイン後に遷移したいページを指定します。
あと sign_in_required も追加します。showページはログインしているユーザーだけにアクセスさせ、ログインしてない場合はログインページに遷移させます。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  def after_sign_in_path_for(resource)
    pages_show_path
  end

  private
      def sign_in_required
          redirect_to new_user_session_url unless user_signed_in?
      end
end

Pagesコントローラーに before_action を追加します。

app/controllers/pages_controller.rb
class PagesController < ApplicationController
  before_action :sign_in_required, only: [:show]

  def index
  end

  def show
  end
end

これでログイン後showページに遷移するようになります。

2. サインアップする際に登録するパラメーターを増やす

ユーザーを登録するときにデフォルトではEmailとパスワードだけですが、
ユーザー名も登録させたい、などの場合があると思います。

まずはサインアップページにユーザー名を入力するエリアを追加します。
ユーザーのプロフィール変更ページ(views/devise/registrations/edit.html.erb)にも同様に追加しときます。

app/views/devise/registrations/new.html.erb
<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  # (省略)...

  <!-- ユーザー名を追加 -->
  <div class="field">
    <%= f.label :username %><br />
    <%= f.text_field :username %>
  </div>

  # (省略)...

<%= render "devise/shared/links" %>

次に ApplicationController に以下を追加します。
テンプレートを変えて、ユーザー名を入力するようにしただけでは実際に登録されないからです。
詳しくは strong_parameters について調べてください。
簡単に言えばよく分からんパラメーターは渡せないようになってるので渡せるようにします。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?

  # (省略)...

  protected
    def configure_permitted_parameters
      devise_parameer_sanitizer.permit(:sign_up, keys: [:username])
      devise_parameter_sanitizer.permit(:account_update, keys: [:username])
    end
end

最後に

作ったプロジェクト全体は以下です。
cigalecigales/devise_rails6

スクリーンショット 2020-04-04 15.42.43.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bundle install --deploymentでAWS Lambda向けデプロイパッケージを作っていて嵌った罠

背景

AWS lambdaにRubyスクリプトを配置するデプロイパッケージを作るとき、Rubyスクリプトが依存するgemファイルをzipファイルに含めるため、bundle install --deploymentのように、--deploymentオプションを指定していました。

--deploymentオプションを指定するとgemはvendor/bundleディレクトリにインストールされます。
次のコマンドで、vendorディレクトリをデプロイパッケージに含めます。

zip deploy_package lambda_function.rb -FSr vendor

--deploymentオプションを使っているのは、AWSのデプロイスクリプトのサンプルにbundle install --deploymentが使われていた時があり、その名残です。現在ではbundle install --path vendor/bundleが使われています。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/ruby-package.html

bundle install --path vendor/bundle を使っていれば、この先を読む必要はありません。

物語

PCにインストールしているbundlerのバージョンを2.1.4に上げました。

bundler 2.1.4では--deploymentオプションは非推奨になりました。
代わりにbundle config deployment trueコマンドで設定を変更します。
https://github.com/rubygems/bundler/pull/7519

CI環境で使うデプロイスクリプトもbundle config deployment trueに修正しました。デプロイが失敗するようになりました。
gemがvender/bundleディレクトリにインストールされなくなったようです。

原因

CI環境ではRuby 2.7を使っています。
Ruby 2.7に同梱されているbundlerのバージョンは2.1.2でした。

bundler 2.1.2 では bundle config deployment true を設定しても、gemのインストール先が変わりません。

対策

bundle config set path 'vendor/bundle' を設定して、gemのインストール先をvender/bundleにしました。

これでbundler 2.1.2でも2.1.4でもgemはvendor/bundleディレクトリにインストールされます。

その後

CI環境でbundler 2.1.4を使うようになり、1ヶ月半くらいでbundle config deployment trueに戻しました。

--pathオプションも非推奨なので、bundle install --path vendor/bundleにはしませんでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MVCとは

MVC

Model(モデル)
View(ビュー)
Controller(コントローラー)
の頭文字です。
役割としては
Model(モデル)
ビジネスロジック(データ処理)を行う
View(ビュー)
表示処理を行う
Controller(コントローラー)
リクエストに応じて適切な処理を呼び出す

MVCの流れ

ルーティング=>コントローラー
リクエストに対応したルーティングが読まれ、それに対応したコントローラーのアクションが動きます。
コントローラー=>モデル
コントローラーで呼び出したアクションをモデルを通してデータベースから必要なデータを呼び出します。
コントローラー=>ビュー
呼び出したデータをビューに受け渡し、受け取ったデータをWebページに表示させます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]本番環境でSeed-fuを使う

本記事投稿のいきさつ

railsでアプリを作成している中で、Seed_fu Gem を使用しました。
その際、開発環境では上手くテーブルにデータを格納することができたのですが、本番環境で苦戦したため備忘録として書きます。
ここでは、自分が苦戦したEC2内でのコマンドのみを書きます。

エラー(異なるディレクトリでコマンドを入力していたため)

自分が作成したアプリのディレクトリへ移動しコマンドを入力したところ

Mysql2::Error::ConnectionError: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

と、mysplに接続ができないというエラーが発生。
これはコマンドを入力するディレクトリが誤っていたため発生したエラーでした。
実は開発環境とは異なり、本番環境ではcurrentディレクトリ内のデータが本番環境で動いているフォルダ群になります。
そのためcurrentディレクトリへ移動した後、再度コマンドを入力します。

 cd current
 rails db:seed_fu

これで大丈夫かと思われましたが、またしてもエラーが。

エラー(コマンド誤り)

Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile

listen gem がないとのこと。
まさかと思いgemfileを確認したところちゃんと書かれていました。
調べたところ、seedでデータを投入する際は環境の指定をする必要があるようです。ということで以下を再度実行。

rails db:seed_fu RAILS_ENV=production

今度は成功しました。初めての経験で苦戦しましたが、とても勉強になりました。
ちなみに、test環境でも同じように以下を実行すれば大丈夫です。

rails db:seed_fu RAILS_ENV=test

おわり

最後まで見ていただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

for文の基本

環境,前提

Ruby 2.5.1
MacOS Mojave Ver.10.14.6

本記事はRubyがインストールされた前提の記事です。
Rubyをインストールしたあと、とにかくRubyをいろいろ触ってみて慣れていくための記事です。お役に立てば幸いです。

for文の基本

オブジェクトの中身から1つずつ取り出して、変数に代入して、オブジェクトの中身を全て処理するまでループが回り続けます。

sample.rb
for 変数 in オブジェクト do
    //繰り返したい処理
end

では1から10までの合計をfor文を用いて算出してみます。

sample.rb
sum = 0
for i in 1..10 do
    sum += i
end
puts sum

実行結果は以下のように55となります。

Tarminal
55

範囲オブジェクト

ここで1..10ってなんだろう?と思われた方も多いはずです。これは範囲オブジェクトといい、
ここで登場する1..10というのは範囲オブジェクトといいRuby独特の物です。開始値と終了値を..で結べば範囲オブジェクトとなります。Rubyでは範囲ですらオブジェクトです。

解説

先ほどのfor文において

sample.rb
for i in 1..10 do

の記述の部分は
1から10までの整数を順番に取り出してiに代入して
sum += i を実行するということです。

補足

範囲オブジェクトの開始と終了は具体的な数値である必要はなく、変数でも構いません。よって以下のように書くこともできます。

sample.rb
form = 1
to = 10
sum = 0
for i in form..to do
    sum += i
end
puts sum

実行結果は以下のように55となります。

Tarminal
55
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

selfを使って、undefined methodエラーを解消

はじめに

Modelで定義したメソッドがControllerで呼び出せなくなったとき、
selfを使って2パターンの解決法を見つけたため、記録として残しておく。

今回は、helloという名前のメソッドをモデルで定義するという設定とする。

解決法1(特異メソッド方式)

post.rb
class Post
  def self.hello
    puts 'hello'
  end
end

メソッド名の前にself.をつけるやり方

ruby初学者なら何度も見たことのある形だ。

解決法2(特異クラス方式)

post.rb
class Post
  class << self
    def hello
      puts 'hello'
    end
  end
end

こちらでも、きちんとcontrollerで呼び出すことができた。
この場合だと、メソッド名の前にself.を書く必要もないし、class << self と end内であれば、
複数のメソッドを同じように定義できるのがメリットだ。

post.rb
class Post
  class << self
    def hello
      puts 'hello'
    end

    def goodmorning
      # 二つ定義しても self つけなくて良い
      puts 'Good morning'
    end
  end
end

そもそも

ここからは、特異メソッド方式や特異クラス方式、selfについてもう一歩踏み込んだところまで理解したい方々に読んで欲しい。

class << selfの仕組み

 特異メソッド

hello = 'hello'
def hello.say
  puts hello + 'world'
end
hello.say #=> hello world

another_hello = 'hello'
another_hello.say #=> NoMethodError: undefined method `say' for "hello":String

このように、特異メソッド(hello.say)とは、オブジェクト(hello)に直接固有のメソッドを定義したものだ。
なので、上記のように別のオブジェクト(another_hello)には使うことはできない。

これに加えて、もう一つ特異メソッドを定義する方法がある。

hello = 'hello'

class << hello
  def say_world
    puts "#{self}, world"
  end
end
hello.say_world #=> hello, world

これも << hello というところで helloオブジェクトの特異クラスを引き出しており、あくまでオブジェクトに対しての特異メソッドの定義となっている。

つまり、一番最初のこの部分、

class Post
  class << self

クラス定義のコンテキストでの self とは、Classクラスのインスタンス Post class だ。class Post とはクラスを定義するときの記法だが、Ruby の中での理解としては、Class クラスのオブジェクトを生成し Post というグローバルな定数へ代入している。

なので、以下の2つの記述は同じ意味だ。

class Post
  def hello
    puts 'hello'
  end
end
post = Post.new
post.hello #=> hello
Post = Class.new do
  def hello
    puts 'hello'
  end
end
post = Post.new
post.hello #=> hello

定数 Post に入っている Class クラスのオブジェクトへ特異メソッドを定義してみる。

Post = Class.new do
  def hello
    puts 'hello'
  end
end
def Post.bye
  puts 'good bye'
end
Post.new.hello #=> hello
Post.bye       #=> good bye

Post.bye という呼出しができた。これはクラスメソッドの呼出しと同じ。
つまり、クラスメソッド Post.bye というのは、Post に入っている Class クラスのオブジェクトへの特異メソッドの定義として読むことができる。

これにオブジェクトの特異クラスの引き出しの記法 << をあわせて考えると、一番冒頭のコードでやろうとしていることがわかってくる。

特異クラス

クラスメソッドの定義は特異メソッド形式と特異クラス形式があった。

特異メソッド形式で定義した def Post.bye を特異クラス形式へ書きかえてみる。

Post = Class.new do
  def hello
    puts 'hello'
  end
end
class << Post
  def bye
    puts 'good bye'
  end
end
Post.new.hello #=> hello
Post.bye       #=> good bye

さらに書き換えると下記にようになる

class Post
  def hello
    puts 'hello'
  end

  class << self
    def bye
      puts 'good bye'
    end
  end
end
Post.new.hello #=> hello
Post.bye       #=> good bye

クラスメソッドのための記法があるわけではなく、特異メソッドという仕組みを使って巧みにクラスメソッドが実現されていることがわかった。

おわりに

今まで何気なく使っていたself。
というかなんとなく理解できたようでできていなかった部分だった。

私が参考にした、というかこの記事はほぼ下記の記事のコピペなので、私の記事がわかりにくかったという方は、ぜひ下のURLの記事を参考にして欲しい。

参考記事

https://magazine.rubyist.net/articles/0046/0046-SingletonClassForBeginners.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CatalinaにアップグレードしたmacでrbenvからRuby 2.7.1をインストールしようとしたらgmpが読み込めずに失敗した

CatalinaにアップグレードしたmacでrbenvからRuby 2.7.1をインストールしようとした。

$ rbenv install 2.7.1
...
dyld: Library not loaded: /usr/local/opt/gmp/lib/libgmp.10.dylib
  Referenced from: /var/folders/p9/38jx3_ks5zb3ml70pccx4g440000gn/T/ruby-build.20200404201643.64758.NBfaXF/ruby-2.7.1/ruby
  Reason: image not found
make: *** [rdoc] Abort trap: 6

で失敗する。

$ ls /usr/local/opt/gmp/lib/
ls: /usr/local/opt/gmp/lib/: No such file or directory

見てみると当該ディレクトリにはなにもない。
しかしインストールしようとするとインストール済みと言われる。

$ brew install gmp
Updating Homebrew...

Error: gmp 6.1.2_2 is already installed
To upgrade to 6.2.0, run `brew upgrade gmp`.

アップグレードしてみる

$ brew upgrade gmp
Updating Homebrew...
==> Upgrading 1 outdated package:
gmp 6.2.0
==> Upgrading gmp -> 6.2.0 
==> Downloading https://homebrew.bintray.com/bottles/gmp-6.2.0.catalina.bottle.tar.gz
==> Downloading from https://akamai.bintray.com/2e/2e6acd6e62d1b8ef0800061e113aea30a63f56b32b99c010234c0420fd6d3ecf?__gda__=exp=1586003712~hmac=1a3ede8872bae9e8bd6b238be3e7c56d1cc64a5b770646c6ffe342d6da269809&response-content-disposition=attachment%3Bfilename%3D%22gmp-6.2.0.catalina.bottle.tar.gz%22&response-content-type=application%2Fgzip&requ
######################################################################## 100.0%
==> Pouring gmp-6.2.0.catalina.bottle.tar.gz
?  /usr/local/Cellar/gmp/6.2.0: 20 files, 3.2MB
Removing: /usr/local/Cellar/gmp/6.1.2_2... (18 files, 3.1MB)

見てみたら先程のディレクトリにもちゃんとファイルができた。

$ ls /usr/local/opt/gmp/lib/
libgmp.10.dylib     libgmp.a        libgmp.dylib        libgmpxx.4.dylib    libgmpxx.a      libgmpxx.dylib      pkgconfig

再度Rubyをインストール

$ rbenv install 2.7.1
Downloading ruby-2.7.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.1.tar.bz2
Installing ruby-2.7.1...
ruby-build: using readline from homebrew
Installed ruby-2.7.1 to /Users/niwatako/.anyenv/envs/rbenv/versions/2.7.1

成功。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails 通知機能

はじめに

今回は通知機能を作成しました。
こちらの記事を参考にさせていただいております。
https://qiita.com/nekojoker/items/80448944ec9aaae48d0a
https://qiita.com/yuto_1014/items/2db1dd4fcd7945b980f7

作るもの

コメント・いいね・フォローされた時に通知を作成して確認・削除できる機能を作成します。すでに上記の機能はあるものとして作成してます。どれか一つでも機能があればOK。参考に過去に投稿したコメント・フォロー機能についてリンクを載せておきます。
コメント機能:https://qiita.com/E6YOteYPzmFGfOD/items/ef776d34908872ea19f7
フォロー機能:https://qiita.com/E6YOteYPzmFGfOD/items/ec0492b509962c3b7ae4

ER図

参考に今回作成する通知機能のER図です。(今回関係ないタグ機能がありますが無視してください。)
スクリーンショット 2020-04-04 16.22.02.png

作成の流れ

1.Notificationモデルの作成
2.各モデルの関連づけ
3.通知メゾットの作成
4.通知機能をコントローラーへ埋め込む
5.通知一覧画面と通知削除作成

モデルの作成

rails g model Notification visitor:references visited:references micropost:references comment:references action:string checked:boolean
db/migrate/
class CreateNotifications < ActiveRecord::Migration[5.1]
  def change
    create_table :notifications do |t|
      t.references :visitor, foreign_key:{ to_table: :users }, null: false
      t.references :visited, foreign_key:{ to_table: :users }, null: false
      t.references :micropost, foregin_key: true
      t.references :comment, foregin_key: true
      t.string :action, null: false
      t.boolean :checked, null: false, default: false

      t.timestamps
    end
  end
end

vistor_id
→通知するユーザー(いいね、コメント、フォローする側)
visted_id
→通知をうけるユーザー
rails g model カラム名:references とすると自動で
・カラム名にidをつけた形でカラムを作成。
・インデックス付与。
foreign_key:true
・外部キー制約を付与
to_table: :user
・visitorとvisitedカラムの参照テーブルを指定してます。
nul: false
・空の状態で保存できなくする。

モデルの関連付け

app/models/user.rb
has_many :active_notifications, foreign_key:"visitor_id", class_name: "Notification", dependent: :destroy
has_many :passive_notifications, foreign_key:"visited_id", class_name: "Notification", dependent: :destroy
app/models/micropost.rb
has_many :notifications, dependent: :destroy
app/models/comment.rb
has_many :notifications, dependent: :destroy
app/models/notification.rb
class Notification < ApplicationRecord
  default_scope -> { order(created_at: :desc) }
  belongs_to :visitor, class_name: "User", optional: true
  belongs_to :visited, class_name: "User", optional: true
  belongs_to :micropost, optional: true
  belongs_to :comment, optional: true
  validates :visitor_id, presence: true
  validates :visited_id, presence: true
  ACTION_VALUES = ["like", "follow", "comment"]
  validates :action,  presence: true, inclusion: {in:ACTION_VALUES}
  validates :checked, inclusion: {in: [true,false]}
end

各オプション
foreign_key:"vistor_id", class_name: "Notification"
→関連づけるカラム名とテーブル名を指定します。
dependent: :destroy
→投稿が削除された際に関連付いている通知を削除。
default_scope -> { order(created_at: :desc) }
→通知が新しい順に並ぶ。
optional: true: nullの値を許可する。
inclusion
→保存できる値を制限しています。

通知メゾットの作成

いいね

app/models/micropost.rb
def create_notification_like!(current_user)
    temp = Notification.where(["visitor_id = ? and visited_id = ? and micropost_id = ? and action = ? ",
                                  current_user.id, user_id, id, 'like'])
    if temp.blank?
      notification = current_user.active_notifications.new(
        micropost_id: id,
        visited_id: user_id,
        action: 'like'
      )

      if notification.visitor_id == notification.visited_id
         notification.checked = true
      end
      notification.save if notification.valid?
    end
  end

始めに通知が作成済みでないか確認してます。(何度も作成しないように)登録済みの場合と自分の投稿に対するいいねの場合は通知を確認済みとして作成します。(checkedをtrueにする。)active_notificationsはuserモデルでhas_many関連付けを行った際に名付けた関連名です。

コメント

app/models/micropost.rb
def create_notification_comment!(current_user, comment_id)
    #同じ投稿にコメントしているユーザーに通知を送る。(current_userと投稿ユーザーのぞく)
    temp_ids = Comment.where(micropost_id: id).where.not("user_id=? or user_id=?", current_user.id,user_id).select(:user_id).distinct
    #取得したユーザー達へ通知を作成。(user_idのみ繰り返し取得)
    temp_ids.each do |temp_id|
      save_notification_comment!(current_user, comment_id, temp_id['user_id'])
    end
    #投稿者へ通知を作成
    save_notification_comment!(current_user, comment_id, user_id)
end

def save_notification_comment!(current_user, comment_id, visited_id)
    notification = current_user.active_notifications.new(
      micropost_id: id,
      comment_id: comment_id,
      visited_id: visited_id,
      action: 'comment'
    )
    if notification.visitor_id == notification.visited_id
      notification.checked = true
    end
    notification.save if notification.valid?
end

コメントの場合は同じ投稿に対してコメントしているユーザーにもコメントを送るようにします。マイクロポストの投稿者には必ず通知が1件作成されるようにします。
「where.(micropost_id: id)」
→同じ投稿にコメント
「where.not("user_id=? or user_id=?", current_user.id,user_id)」
→コメントしたユーザーと投稿者は除く。
distinct
→複数回コメントしている人も通知は一件のみにする
select(:user_id)
→user_idのみ取得しています。

取得したuser_idを下で定義している通知作成メゾットに渡して繰り返し通知を作成します。

フォロー

app/models/user.rb
def create_notification_follow!(current_user)
    #すでに通知が作成されているか確認
    temp = Notification.where(["visitor_id = ? and visited_id = ? and action = ? ",current_user.id, id, 'follow'])
    if temp.blank?
      notification = current_user.active_notifications.new(
        visited_id: id,
        action: 'follow'
      )
      notification.save if notification.valid?
    end
end

通知機能を各コントローラーへ埋め込み

各通知メゾットをコメント等のアクションを起こした際に起動するようにコントローラー内へ埋め込んでいきます。

コメント

app/controlloers/comments_controller.rb
def create
    @comment = current_user.comments.build(comment_params)
    @comment.micropost_id = params[:micropost_id]
    if @comment.save
      flash[:success] = 'コメントしました'
      #通知機能用
      @micropost=@comment.micropost
      @micropost.create_notification_comment!(current_user, @comment.id)
      #ここまで通知機能
      redirect_to @comment.micropost
    else
      comments_get
      render template: 'microposts/show'
end

フォロー

app/controllers/follow_controlloer.rb
def create
    @user =User.find(params[:follow_relationship][:following_id])
    current_user.follow(@user)
    #通知機能追加
    @user.create_notification_follow!(current_user)
    respond_to do |format|
      format.html {redirect_back(fallback_location: root_url)}
      format.js 
    end
end

いいね

app/controllers/like_controlloer.rb
def create
    @user = current_user
    @micropost = Micropost.find(params[:micropost_id])
    current_user.like(@micropost)
    #通知機能追加
    @micropost.create_notification_like!(current_user)
    respond_to do |format|
      format.html { redirect_back(fallback_location: root_url) }
      format.js
    end
end

通知一覧画面と通知削除作成

まずは通知ページ表示ようのコントローラ、ルーディングの設定です。

rails g controller notifications
config/routes.rb
resources :notifications, only: [:index, :destroy]
app/controllers/notifications_controlloer.rb
def index
    @notifications = current_user.passive_notifications
    #通知画面を開くとcheckedをtrueにして通知確認済にする
    @notifications.where(checked: false).each do |notification|
      notification.update_attributes(checked: true)
    end
end

def destroy
    @notifications =current_user.passive_notifications.destroy_all
    redirect_to notifications_path
end

続いて通知一覧画面の作成です。

app/views/notifications/index.html.erb
<h3 class="text-center">通知</h3>
    <%= link_to "通知削除", notification_path(@notifications), method: :delete ,class: "fas fa-trash" %>
    <% if @notifications.exists? %>
    <div class="notification-index">
        <%= render @notifications %>
    </div>
<% else %>
    <p>通知はありません</p>
<% end %>
app/views/notifications/_notification.html.erb
<div class="notification-view">
  <%= notification_form(notification) %><span class="moderate-font"><%= " (#{time_ago_in_words(notification.created_at)}前)" %></span>
  <br>
  <% if !@comment.nil? %>
    <p class="notification-comment"><%= @comment %></p>
  <% end %>
</div>

いいね、フォロー、コメントの通知によって表示する内容を変更するために、
ヘルパーメゾットを作成してviewで呼び出します。

ruby/app/helprs/notifications_helper.rb
module NotificationsHelper
  def notification_form(notification)
    #通知を送ってきたユーザーを取得
    @visitor = notification.visitor
    #コメントの内容を通知に表示する
    @comment = nil
    @visitor_comment = notification.comment_id
    # notification.actionがfollowかlikeかcommentかで処理を変える
    case notification.action
    when 'follow'
      #aタグで通知を作成したユーザーshowのリンクを作成
      tag.a(notification.visitor.name, href: user_path(@visitor)) + 'があなたをフォローしました'
    when 'like'
      tag.a(notification.visitor.name, href: user_path(@visitor)) + 'が' + tag.a('あなたの投稿', href: micropost_path(notification.micropost_id)) + 'にいいねしました'
    when 'comment' then
      #コメントの内容と投稿のタイトルを取得       
      @comment = Comment.find_by(id: @visitor_comment)
      @comment_content =@comment.content
      @micropost_title =@comment.micropost.title
      tag.a(@visitor.name, href: user_path(@visitor)) + 'が' + tag.a("#{@micropost_title}", href: micropost_path(notification.micropost_id)) + 'にコメントしました'
    end
end

以上となります。

だいぶ長くなりましたがここまでお読みいただきありがとうございました!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 基礎知識

Ruby基礎知識

to_sメソッド

オブジェクトの内容を文字列化するメソッド

ruby
puts 1.to_s + 7.to_s #=> "17"

puts 'nil'.to_s #=> ""


to_iメソッド

文字列表記から整数表記へ変換するメソッド

ruby
str = "23"
puts str.to_i + 12  #=> 35


to_fメソッド

整数表記から小数表記へ変換するメソッド

ruby
n = 5
puts n.to_f       #=>5.0
puts n.to_f / 2   #=>2.5


メソッドの呼び出し

オブジェクト.メソッド(引数)
*()は省略可

ruby
#数値を16進数の文字列に変換

puts 14.to_s(16) #=> "e"

puts 14.to_s 16 #=> "e"


コメントの入れ方

ruby
# 1行の場合(文の途中からでも可)

=begin
複数行
コメント
可
=end


文字列に変数を組み込む

ruby
age = 24
puts "私の年齢は" + age + "歳です"    #=>エラー
puts "私の年齢は #{age} 歳です"       #=>私の年齢は24歳です
puts "私の年齢は" + age.to_s + "歳です" #=>私の年齢は24歳です


sprintfメソッド

ruby
sprintf('%0.3.f', 1.2)  #=> "1.200"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【VScode】なぜか自動保存できねえ時の解決法、そしてパーミッション関連のことも【備忘録】

【VScode】なぜか自動保存できねえ時の解決法

設定でAfterdelayなどにしてファイルの自動保存機能をつけているVScodeで開発しているときに、
以下のようなエラーが出て、自動が保存ができないような事態の解決法を紹介します
エラー1.jpg

[アクセス権限が不十分です。[Sudo 権限で再試行]を選択してスーパーユーザーとして再試行してください。]
と出ております。
つまり、現在、Sudoユーザーとして、ファイルにアクセスできていない状態なので、
・スーパーユーザーにモードを変える
・ファイル、あるいはフォルダのアクセス権限(パーミッション)を変更する
方法があります。

今回は後者の手段を用いて、このエラーを解決します。

terminal
sudo chmod -R 777 ファイルのディレクトリ位置(Ex. /Users/kazuhitoNakayama/Desktop/index.html.erb)

この一行で、全てのユーザーに対してのファイルの実行権限を与えられるので、
上記エラーが解決されます。

解説

chmodとは

?ファイルやディレクトリのアクセス権限を変更する際に使うコマンドです
 (UNIX系のOSで使用できます。Ex.Macなど)

使い方は、chmod [オプション] [与える権限] [対象のファイル、ディレクトリのディレクトリ位置]

オプションは私は普段 「-R」を使ってますが、他にもたくさんあるので
「chmod オプション」とかでお調べください!

777とは

?先ほどchmod [オプション] [与える権限] [対象のファイル、ディレクトリのディレクトリ位置]
とお伝えしました。
ここでの[与える権限]について説明すると、アクセス権の記法には2種類あり、
・アルファベット
・数字
があります。

アルファベットでの記法

あるフォルダの中に入って「ls -l」をすることで、各ファイルの権限を参照できるので、みてみると、
アルファベットの羅列が見えます。

terminal
$ ls -l
drwxr-xr-x  2 user_name  group  68  8 30 15:53 css
-rw-r--r--  1 user_name  group   0  8 30 15:53 index.html

それぞれの文字は
r:(read)読み込み権限
w:(write)書き込み権限
x:(execute)実行権限
こんな風に対応しております。

例えば上の、drwxr-xr-xをいかに読むかというと
d/rwx/r-x/r-x 
↑こんな風に区切られます。

また、それぞれの区切りの中で、
ディレクトリなのかファイルなのか/所有者に対する権限/グループに対する権限/その他の者に対する権限/
という風に区切られており、それぞれの対象に対して、権限をr・w・xで与えます。
(1文字目に関してはdならば当該ファイルはディレクトリとわかります。-ならばファイルです。)

よって、
d/rwx/r-x/r-xは
ーディレクトリであり
自分に対して、読み込み、書き込み、実行の全ての権限が与えられており、
グループに対して、読み込み、実行権限が与えられており、
その他の者に対して、読み込み、実行権限が与えられています。

数字の記法

シンプルで、上記、r・w・xがそれぞれ数字に変わるだけですね!

r(読み込み権限):4
w(書き込み権限):2
x(実行権限):1

そして今回は3つの数字で権限対象、与える権限を表現します
例えば、731

1つ目の数字:所有者
2つ目の数字:グループ
3つ目の数字:その他の者
という風になっているので、

731は、
所有者は、7=4+2+1なので読み込み、書き込み、実行権限が与えられ、
グループは、3=2+1なので書き込み、実行権限が与えられ、
その他の者は、1なので実行権限が与えられます。

総括

冒頭の振り返りで行くと

terminal
sudo chmod -R 777 ファイルのディレクトリ位置(Ex. /Users/kazuhitoNakayama/Desktop/index.html.erb)

これによりindex.html.erbのファイルのパーミッションを、誰でも実行できるようにしたので、自分がファイルを変更してもパーミッションで怒られることがなくなったのです!

パーミッションは自分的に忘れやすい!
必覚!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

素人がWebサービスを自分で作る備忘録(前準備編)1

この記事の目的

この記事は自分が初めてWebサービスを作るにあたっての備忘録的な意味合いが強いです。
その為、エンジニアリング経験者の方にとっては特に為になる内容ではないと思われますが、ズブの素人が初めてサービスを開発しようと思ってからの成長日記だと思って見てもらえると嬉しいです。

また、同じような未経験者の方がWebサービスを開発しようとしたとき、何かの手助けになれれば嬉しいと思います。

現在のレベル

・Progate学習済み(HTML&CSS、Javascript、React、Ruby、RubyonRails、Git、CommandLine、SQL)
・Railsチュートリアル 二周

個人でWebサービスを作る際にはまず、何のために作るのか決めそれに適した基礎知識を習得する必要が出てきます。
今回は、第一の目標として今まで学習してきた内容のアウトプット、第二の目標はモダンなIT企業へのジョブチェンジするためのポートフォリオ作成ということで行っていきます。
その為、Web業界で今現在多く使われているRailsを選択しました。
Rails学習のバイブルとして多くに人に利用されているRailsチュートリアルですが、これを内容を理解するためには学習のための学習が必要となってくる(Reactを除く)。その学習にはProgateがおすすめ!

Webサービスの企画

上記の学習を終えたところで実際に、Webサービスの開発に入っていきます
しかし、いきなりコードを書けと言われて書くことはできないはずです!なぜなら何を作ろうとか、どのような機能を持たせようとか、そういったところの話を先にやらないと作れるわけがないんです!

 1. 何のために作成するのか
 2. どのような物を作るのか
 3. どういった機能を盛り込むのか

このようなところから考えていきたいと思います。

1に関しては上でも書いた通り、知識のアウトプット、ポートフォリオ作成です。

2に関しては

 ・今まで学んだ知識 +αで作れる物(大きなことを言うと後で後悔しそうなので)
 ・未経験からの転職で有利になりそうな機能を実装すること
 ・自分が作りたい物(コロナとかで外に出れないので他の人と繋がれるサービスとか)

を条件とします。
 
3に関しては

 ・ユーザー管理機能
 ・投稿機能
 ・投稿一覧、投稿詳細機能
 ・画像ファイルアップロード機能
 ・ページネーション機能or無限スクロール機能
 ・DBテーブルのリレーション管理
 ・単体、統合テスト
+αの内容
 ・ReactによるJavascriptライブラリ
 
を取り入れていこうと思います。

次では具体的なサービスの形を考えていくことについて書いていこうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

素人がWebサービスを自分で作る備忘録(前準備編)

この記事の目的

この記事は自分が初めてWebサービスを作るにあたっての備忘録的な意味合いが強いです。
その為、エンジニアリング経験者の方にとっては特に為になる内容ではないと思われますが、ズブの素人が初めてサービスを開発しようと思ってからの成長日記だと思って見てもらえると嬉しいです。

また、同じような未経験者の方がWebサービスを開発しようとしたとき、何かの手助けになれれば嬉しいと思います。

現在のレベル

・Progate学習済み(HTML&CSS、Javascript、React、Ruby、RubyonRails、Git、CommandLine、SQL)
・Railsチュートリアル 二周

個人でWebサービスを作る際にはまず、何のために作るのか決めそれに適した基礎知識を習得する必要が出てきます。
今回は、第一の目標として今まで学習してきた内容のアウトプット、第二の目標はモダンなIT企業へのジョブチェンジするためのポートフォリオ作成ということで行っていきます。
その為、Web業界で今現在多く使われているRailsを選択しました。
Rails学習のバイブルとして多くに人に利用されているRailsチュートリアルですが、これを内容を理解するためには学習のための学習が必要となってくる(Reactを除く)。その学習にはProgateがおすすめ!

Webサービスの企画

上記の学習を終えたところで実際に、Webサービスの開発に入っていきます
しかし、いきなりコードを書けと言われて書くことはできないはずです!なぜなら何を作ろうとか、どのような機能を持たせようとか、そういったところの話を先にやらないと作れるわけがないんです!

 1. 何のために作成するのか
 2. どのような物を作るのか
 3. どういった機能を盛り込むのか

このようなところから考えていきたいと思います。

1に関しては上でも書いた通り、知識のアウトプット、ポートフォリオ作成です。

2に関しては

 ・今まで学んだ知識 +αで作れる物(大きなことを言うと後で後悔しそうなので)
 ・未経験からの転職で有利になりそうな機能を実装すること
 ・自分が作りたい物(コロナとかで外に出れないので他の人と繋がれるサービスとか)

を条件とします。
 
3に関しては

 ・ユーザー管理機能
 ・投稿機能
 ・投稿一覧、投稿詳細機能
 ・画像ファイルアップロード機能
 ・ページネーション機能or無限スクロール機能
 ・DBテーブルのリレーション管理
 ・単体、統合テスト
+αの内容
 ・ReactによるJavascriptライブラリ
 
を取り入れていこうと思います。

Webサービスの見た目を考える

Webサービスの見た目を考える場合、誰をターゲットにするのか、どのような場面で使われるのかなどを考えて機能やページデザインを考える物です。
しかし、今回は完全に自分のためのものですのでその辺は深く考えずやっていきたいと思います。(はやく実装に進みたいので)

とりあえず
ターゲット:20代 外出自粛させられているせいで友達と遊べなくて暇を持て余している。

ページデザインは適当にサックリ手書きで紙に書きました。(早く実装にすすみt)
画面遷移図も手書きでさっくり(早くz)

データベーススキーマ設計も手書きで作成。
※データベーススキーマ設計は私にとって初めて聞くものだったので次の記事にてもう少しまとめたいと思います。

ここまで行ったこと

・基礎学習
・作るWebサービスの企画
  なぜ作るのか
  どのような物を作るのか
  どういった機能が必要なのか
・Webサービスの見た目を簡単に設計
  モックアップの作成
  画面遷移図の作成
  データベーススキーマ設計
  

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】enum_helpを用いてセレクトボックスを作成

はじめに

image.png
こんな感じのフォームを作ります。
観測した限りでは一番シンプルな書き方です。問題などございましたらご指摘をお願いします。

環境

ruby 2.5.3
Rails 5.2.4.2
enum_help (0.0.17)
rails-i18n (5.1.3)

作成法

enumの設定

models/user.rb
class User < ApplicationRecord
  validates :name, presence: true, length: { maximum: 30 }
  validates :description, length: { maximum: 300 }
  validates :privacy, presence: true

  enum privacy: { published: 0, closed: 1 }
end

booleanにしていないのは、今後限定公開などの機能を追加することを想定しているためです。
このようにenumを設定することで、データベースでは数値の値を文字列として擬似的に扱うことができます。
また、この値は文字列でもシンボルでも指定することができます。

user.privacy
=> "closed"
user.privacy = 0
=> 0
user.privacy
=> "published"
user.privacy = :closed
=> :closed
user.privacy
=> "closed"

i18nの設定

Gemfile
gem 'rails-i18n'
gem 'enum_help'

必要なgemを記述して、$ bundle installでインストールします。
そして、application.rbに以下の設定を追加します。

config/application.rb
module TestApp
  class Application < Rails::Application
    # 言語・タイムゾーンを日本に設定
    config.i18n.default_locale = :ja
    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local
    # config/locales/配下の全てのrb, ymlファイルを読み込み対象とする
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
  end
end

config/locales配下に日本語化用のファイルを用意します。(ファイルの場所や名前は自由)

config/locales/models/ja.yml
ja:
  activerecord:
    models:
      user: ユーザー
    attributes:
      id: ID
      created_at: 作成日時
      updated_at: 更新日時
      user:
        name: 名前
        description: 自己紹介
        privacy: 公開設定
  enums:
    user:
      privacy:
        published: 公開
        closed: 非公開

formの作成

form.html.slim
= form_with model: @user, local: true do |f|
  = f.label :name
  = f.text_field :name
  br
  = f.label :description
  = f.text_area :description
  br
  = f.label :privacy
  = f.select :privacy, User.privacies_i18n.invert
  br
  = f.submit

こんな感じです。ポイントはセレクトボックスの値です。
= f.select プロパティ名, 選択肢の配列とすればいいので、

  = f.select :privacy, [['公開', 0], ['非公開', 1]]

これでも可能ですが、なんか嫌です。モデルと関連付けたい。
そこで、gemの出番です。'enum_help'によってそれぞれ以下のようなインスタンスメソッドとクラスメソッドが使えるようになります。

user.privacy_i18n
=> "公開"
User.privacies_i18n
=> {"published"=>"公開", "closed"=>"非公開"}

これを利用して[[表示される選択肢, 渡される値], [表示される選択肢, 渡される値]]の形の配列に置き換えます。

User.privacies_i18n.map { |k, v| [v, k] }
=> [["公開", "published"], ["非公開", "closed"]]
User.privacies_i18n.invert.to_a
=> [["公開", "published"], ["非公開", "closed"]]

どちらも同じですが、後者の方が文字数が少なくシンプルかなと思います。

(追記)
これだけ書いておいて何ですが、配列に変更する必要はありませんでした。ハッシュのままでも行けました。
ということで、User.privacies_i18n.invertが最短です。
そして五年前の記事で既に言及していたので、ここに恥を晒しておきます。

リンク

Enums | Active Record クエリインターフェイス - Railsガイド
Rails 国際化 (i18n) API - Railsガイド

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails6.0でYoutubeDataAPIv3を使う

プログラミングの練習がてら、愛しのホロライブのチャンネル、動画情報をyoutubeのapiを利用して表示しようと思ったときに、以前YoutubDataAPIを利用した時と勝手が変わっていたので、改めて実装方法を備忘として残しておこうと思います。

開発環境

  • CentOS 7.6
  • Ruby 2.5.3
  • Ruby on Rails 6.0

準備

APIキーを取得する

APIを利用するためにはAPIキーを準備する必要があります。
これはこれで説明がいるほどめんどくさいのですが、ググればいくらでも出てきますので調べてみてください。説明するのがめんどくさいわけではないです。説明するのがめんどくさいわけではないです。

一応良さそうな記事をお借りさせていただきます。
YouTube APIキーの取得 (2020/03/25時点)

Gemをインストールする

Railsプロジェクトの中のGemfileの中に以下を追記してbundle installする。

Gemfile
gem 'google-api-client'

(必要があれば) 環境変数の設定をする

APIキーを使うとyoutubeのパーソナルな情報が利用できるのですが、逆に言うと悪用すると他人のパーソナルな情報を引き出せるということになるので、GitHubなどにソースをコミットした時に意図せずAPIキーを公開してしまわないように別で管理する必要があります。
というわけで、環境変数を利用してコミットされるソースの外側でAPIキーを管理します。
ただしAPIを触るというだけなら必要ない手続きになるので、必要のない方はスルーしてください。

まずは以下のをGemfileに以下のコードを記述し、bundle installする。

Gemfile
gem 'dotenv-rails'

こちらのGemは環境変数を利用しやすくしてくれる優れものです。

次にAPIキーを管理するファイルを用意します。
.gitignoreというファイルと同じところ(おそらくプロジェクトの直下)に.envファイルを作成し、中にAPIキーの情報を記入します。

.env
YOUTUBE_DATA_API_KEY=APIキー

管理ファイルを作成したらそのファイルをコミットの対象外にします。
.gitignoreファイルに設定するとそのファイルはコミット対象外になるので、.envを追記します。

.gitignore
# 追記
.env

これで環境変数の準備完了です。

いざ

YoutubeDataAPIに接続するためのインスタンスを準備

APIを使うたびにインスタンスを生成する必要はないので、イニシャライザーで生成します。というわけで/config/initializersディレクトリにそれ用のファイルを作成します。ファイル名は仮にyoutube.rbとします。

/config/initializers/youtube.rb
require 'google/apis/youtube_v3'

API_KEY = ENV['YOUTUBE_DATA_API_KEY']

def get_youtube_service

  service = Google::Apis::YoutubeV3::YouTubeService.new
  service.key = API_KEY

  return service
end

これでAPIにアクセスできるインスタンスを取得できるメソッドが用意できました。
ちなみに環境変数設定をしていない方は、ENV['YOUTUBE_DATA_API_KEY']の個所をAPIキーに置き換えてください。

ようやっと使用。

お使いのコントローラー等で、先ほど用意したメソッドでインスタンスを取得し、用意されたメソッドで情報を持ってきます。下記ではチャンネル情報を取得しています。

使用例
# インスタンスを取得
youtube = get_youtube_service
#APIに接続して、目的の情報を取得する
channel_data = youtube.list_channels("snippet,brandingSettings", id: "UCDqI2jOz0weumE8s7paEk6g")

#インスタンスに情報を詰める
@member = Member.new
channel = channel_data.items.first
snippet = channel.snippet
branding_settings = channel.branding_settings
@member.channel_id = channel.id
@member.channel_name = snippet.title
@member.icon = snippet.thumbnails.high.url
@member.banner = branding_settings.image.banner_image_url

return @member

@memberのモデル構造は察してください。

今回は我が愛しのロボ子さんのチャンネル情報を取得しました。
取ってきた情報は結構な入れ子構造になっていて、そこんとこをわかっておかないと利用するのは結構きついです。具体的な話は公式のリファレンスを参照するのがいいと思うのでそちらをご確認ください。
ただし若干情報が古いらしく、たまに使えない箇所があります。
公式リファレンス

チャンネル情報以外の情報を取るときは、今回イニシャライザーでrequiredしている'google/apis/youtube_v3'のソースを直接参照するのが速いと思います。
(他にいいやり方があれば是非教えてください。。。)
OSごとに保存先は違うとは思いますが、私自身の備忘も兼ねて参考程度に載せておくと以下のようになります。
/home/(ユーザー)/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/google-api-client-0.37.2/generated/google/apis/youtube_v3/service.rb

以上です。
何か不備があればコメントやご連絡をいただけますと幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】カラムとプロパティの違いについて

はじめに

カラムプロパティって言葉でてきて混乱したのでめっちゃ簡単にまとめてみました~?

カラムとプロパティの違い?

カラム:データーベース側の呼び方

プロパティ:サーバーサイド側の呼び方

結論:呼び方が違うだけで大体一緒のこと

カラムちょっと詳しく?

カラム.png

画像(データーベース)の id や user_id 、 name などのこと。

プロパティちょっと詳しく?

例:form_for
<%= form_for @user do |form| %>
<%= form.label :name %> <%= form.text_field :name %>
<%= form.label :address %> <%= form.text_area :address %>
<%= form.submit %>
<% end %>

このようなフォームを作るときに出てくる

<%= form.text_field :name %>
<%= form.text_area :address %>

この部分の :name 、 :address のこと

さいごに

form.text_fieldの引数に、『カラム名を指定する』っていうのはあまり正しくなくて、『プロパティを指定する』っていうのが正しいみたいです~?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】現場Railsメモ

執筆中

  • 復習
  • アウトプット前提のインプット

環境

Ruby 2.6.5
Rails 5.2.4.2
MySQL 5.7.28

Chapter1 RailsのためのRuby入門

オブジェクト

  • 言葉によって区別できるあらゆる「モノ」
  • 全てのオブジェクトはobject_idという固有番号をもち、固有の存在として認識される
  • 仕事は何らかの振る舞い(メソッド)をすること

クラス

  • オブジェクトの原型になるもの(設計図)

人間

太郎くん
花子さん
hogeくん
fugaくん

人間がクラスに該当。

インスタンス

  • クラスを元に作成された実態

人間

太郎くん
花子さん
hogeくん
fugaくん

人間未満(言葉不適切?)がインスタンスに該当。

変数

  • 何かのオブジェクトをさし示すことができる、ラベルのような存在

ローカル変数

  • プログラム中の一定の処理の範囲で使われ、その範囲が終わったら捨てられる

インスタンス変数

  • オブジェクトの内部に保持されてオブジェクトが存在する限り一緒に存在する

メソッド

  • オブジェクトの何らかの振る舞い
  • クラスメソッドとインスタンスメソッド

人間
- 名前
- 体
- 喜怒哀楽

太郎くん
- 1秒間にタイピングを10回打てる
花子さん
- 3桁の数字の15個の和を2秒で求められる
hogeくん
- 100mを9.58で走れる
fugaくん
- 1時間で料理を10品作れる

人間クラスのインスタンスである4人は歩く、食べる、笑うことができる。
名前、体、喜怒哀楽クラスメソッドに該当。

人間クラスのインスタンスである4人のそれぞれにしかできない
特殊能力がインスタンスメソッドに該当。

レシーバー

  • メソッドを呼ぶ元のオブジェクト

hoge = 'ほげ'
hoge.length
hoge.class

hoge.lengthhogeがレシーバーに該当。
.lengthStringクラス(前述の人間クラス)がもつメソッドに該当。

返り値(戻り値)

  • メソッドが呼ばれた時に帰ってくる値のこと

属性(Attribute)

  • オブジェクトの抱えるデータ

1-4~スタート

Chapter3 タスク管理アプリケーションを作ろう

テンプレートエンジン

  • HTMLの構造が直感的にわかりやすいテンプレート

ERBやHamlやSlim

Strong Parameters

  • 想定しているリクエストパラメーターのみ受け取る仕組みのこと

flashとflash.nowの違い

  • flash → 次のリクエストに対してデータを伝える → redirect
  • flash.now → 同じリクエスト内で伝えることができる → render

simple_format

  • 入力時の改行を、表示させる時に改行としてviewに表示させることを可能にする

リファレンス

Chapter4 現実の複雑さに対応する

環境別rails db:migrate

ターミナル
# 開発用
$ rails db:migrate
# テスト用
$ rails db:migrate RAILS_ENV=test
# 本番用
$ rails db:migrate RAILS_ENV=production

change_cloumn_null

  • 既存テーブルの既存カラムのNOT NULL制約をつけたり外したりできる

共同開発を意識し、今後はrails db:rollbackは極力使用しないようにする

ターミナル
$ rails g migration Changeテーブル名NameNotNull
db/上記コマンドによって生成されるマイグレーションファイル
def change
  change_column_null :テーブル名, :カラム名, false or true
end

オリジナルの検証コード(重要)

  • P141~142

本書ではカンマを許可しないメソッドを実装

コールバック

  • 登録や削除などの重要なイベントの前後に任意の処理を挟むことができる仕組み

然るべきタイミングが来たら処理を呼ぶことができる

トランザクション

  • 一連の複数の処理によるデータベースの生合成を保つための機能

セッション

  • 1つのブラウザから
  • 連続して送られている複数のリクエストの間で
  • ブラウザとサーバーで共有したい「情報」を
  • サーバー側に保存する仕組み

Cookie

  • 1つのブラウザから
  • 連続して送られている複数のリクエストの間で
  • ブラウザとサーバーで共有したい「情報」を
  • ブラウザ側に保存する仕組み

セッションとCookie

digest

  • ハッシュ化を行った文字列

パスワードの保存時に使用

ハッシュ化

  • 元の値に戻すことができない一方的な変換

Userモデルにadminフラグを追加

マイグレーションファイル
def change
  add_column :users, :admin, :boolean, default: false, null: false
end

:boolean, default: falseadminカラムに0が入る?
データ取得時にはtrueorfalseが入っている

true or falseの言語化?

ターミナル
$ rails c
user1 = User.first
user1.admin
→ false

user2 = User.find(2)
user2.admin
→ true

user1.admin? ? '権限あり' : '権限なし'
→ 権限なし

user2.admin? ? '権限あり' : '権限なし'
→ 権限あり

? 'true(1)時の表示' : 'false(0)時の表示'

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Ajaxを用いた非同期いいね機能の実装

目標

ezgif.com-video-to-gif.gif

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

ログイン機能、投稿機能を実装済み。

いいね機能の実装

テーブル

usersテーブル

    カラム          データ型     
name string
introduction text
profile_image_id string

booksテーブル

    カラム          データ型     
title string
body text
book_id integer
user_id integer

favoritesテーブル

    カラム          データ型     
user_id integer
book_id integer

モデル

user.rb
    has_many :favorites, dependent: :destroy

    def favorited_by?(book)
        favorites.where(book_id: book.id).exists?
    end
book.rb
    has_many :favorites, dependent: :destroy
favorite.rb
    belongs_to :user
    belongs_to :book

ルーティング

routes.rb
    resources :books do
        resource :favorites, only: [:create, :destroy]
    end

コントローラー

favorites.controll.rb
class FavoritesController < ApplicationController
    def create
        @book = Book.find(params[:book_id])
        # いいねボタンを連打しても1回しかいいね出来ない様に条件をつける
        unless current_user.favorited_by?(@book)
            favorite = current_user.favorites.new(book_id: @book.id)
            favorite.save
            redirect_to @book
        end
    end

    def destroy
        @book = Book.find(params[:book_id])
        favorite = current_user.favorites.find_by(book_id: @book.id)
        favorite.destroy
        redirect_to @book
    end
end

ビュー

1. 投稿一覧をパーシャル化

books/index.html.erb
    <% @books.each do |book| %>
        <tr>
            <%= render 'books', book: book %>
        </tr>
    <% end %>

3. showページ等でもいいねボタンを使い回したいので、さらにいいねボタンをパーシャル化

books/_books.html.erb
    <td>
        <%= render 'favoritebutton', book: book %>
    </td>

4. いいねボタンを作成

books/_favoritebutton.html.erb
# current_userがその投稿をいいねしているかによって表示を変えている
<% if book.favorited_by?(current_user) %>
    <%= link_to book_favorites_path(book), method: :delete, remote: true do %>
    <i class="fa fa-heart" aria-hidden="true" style="color: red;"></i>
    <%= book.favorites.count %>
    <% end %>
<% else %>
    <%= link_to book_favorites_path(book), method: :post, remote: true do %>
    <i class="fa fa-heart-o" aria-hidden="true"></i>
    <%= book.favorites.count %>
    <% end %>
<% end %>

非同期機能の実装

1. jQueryの導入

Gemfile
    gem 'jquery-rails'
ターミナル
    $ bundle
application.js
    //= require rails-ujs
    //= require activestorage
    //= require turbolinks
    //= require jquery
    //= require_tree .

2. いいね後のジャンプ先を削除

favorites.controll.rb
class FavoritesController < ApplicationController
    def create
        @book = Book.find(params[:book_id])
        unless current_user.favorited_by?(@book)
            favorite = current_user.favorites.new(book_id: @book.id)
            favorite.save
        end
    end

    def destroy
        @book = Book.find(params[:book_id])
        favorite = current_user.favorites.find_by(book_id: @book.id)
        favorite.destroy
    end
end

3. パーシャルの親要素にクラスをつける

books/_books.html.erb
    #eachで呼び出されている各投稿のいいねボタンに対して、それぞれ一意のクラスを付けている
    <td class="favoritebutton_<%= book.id %>">
        <%= render 'favoritebutton', book: book %>
    </td>

4. JavaScriptファイルの作成

favorites/create.js.erb
$(".favoritebutton_<%= @book.id %>").html("<%= j(render 'books/favoritebutton', book: @book ) %>");
favorites/destroy.js.erb
$(".favoritebutton_<%= @book.id %>").html("<%= j(render 'books/favoritebutton', book: @book ) %>");

$(".favoritebutton_<%= @book.id %>")
➡︎「3」で付けたクラスを指定

.html("<%= j(render 'books/favoritebutton', book: @book ) %>");
➡︎いいねボタンのパーシャルをrenderしている

参考サイト

https://freecamp.life/rails-favorite-ajax/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エラー「...mysql2/client.rb:90: [BUG] Segmentation fault at 0x00000000000000」に苦しんだ

エラー「...mysql2/client.rb:90: [BUG] Segmentation fault at 0x00000000000000」
で非常に苦しみました。
改善したので、メモしておきます。

Rails sはできるが、どこかページを読み込もうとしようと、エラーが発生します。

改善の参考になった記事は、下記になります
エラー「...mysql2/client.rb:90: [BUG] Segmentation fault at 0x00000000000000」(セグフォ/セグメンテーション違反)に対応した
これでmysqlをアンインストールします。

mysqlの再インストール方法は、自分の環境構築方法である
VS codeの初期設定とRuby on Railsの環境設定のMysqlの構築をしました。

しかし、
エラー「Library not loaded: /usr/local/opt/mysql/lib/libmysqlclient.21.dylib (LoadError)」が表示されました

この対応方法は下記のサイトが有効でした
https://note.com/shoki_rails/n/nf7b51ba48084

$ bundle exec gem uninstall mysql2
> 3ですべてのバージョンを削除する
$ bundle install
$ rails s
>改善
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails 】部分テンプレート使用時のディレクトリ命名方法について/partialディレクトリとsharedディレクトリの使い分け

★気になっていたこと
RailsではDRYの原則が定められていることから、頻繁に使用する「部分テンプレート」。

使用していく中で、「partial」「shared」と
似たような意味合いを持つ単語が出てきて混乱していました。

★結論
大変ざっくりとしていますが、「partial」「shared」の使い分けは以下のようです。

①「shared」を使用する状況としてはdeviceのgemを使用しているタイミング。

device入れたときは自動でsharedディレクトリが生成されるので、
その中に使用するファイルを格納する。
スクリーンショット 2020-04-04 10.58.24.png

②device外で使用するファイルについては、現場によってディレクトリ名は異なる。
「partial」と絶対命名するわけではない。

細かい部分ですが、少し気になったので備忘録として残します。

※例
スクリーンショット 2020-04-04 11.02.41.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails-APIでsorceryを使ったらundefined local variable or method `form_authenticity_token'と怒られた

概要

Rails-APIにて認証用のgem sorceryを使ってloginメソッドを叩いた時に

undefined local variable or method `form_authenticity_token'

と怒られたので解決策を書きます。

環境

ruby 2.6.5
Rails 6.0.2.1
sorcery (0.14.0)

結論

ログイン処理を行うコントローラーに下記を追加すればOK

sessions_controller.rb
private

def form_authenticity_token; end

原因

こちらの記事によると、ユーザーがログインする際にCSRFトークンがリセットされるのですが、それを行うform_authenticity_tokenメソッドがRails-APIに存在しないかららしいです。

このメソッドがないと動作が保証されないので、とりあえず自前で空メソッドを用意してくれ、とのことでした。

見た目はブサイクになりますが仕方なくこれで対応して無事に動きました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】migrationファイルまとめ(カラム追加・削除・変更)

はじめに

学習中の備忘録です。

概要

  • カラムのデータ型一覧
  • モデル作成時
  • カラム追加
  • カラム削除
  • データ型変更
  • カラム名変更
  • モデル作成時(外部キー制約付)
  • カラム追加時(外部キー制約付)

カラムデータ型一覧

データ型 型の種類 用途
integer 数字 金額、回数など
string 文字(短文) ユーザー名、メールアドレスなど
text 文字(長文) 投稿文など
boolean 真偽 はい・いいえの選択肢など
datetime 日付と時刻 作成日時、更新日時など
float 浮動小数 小数点含む6桁までを丸めずに保存
decimal 精度の高い少数 小数点以下桁数揃えて正確に保存
timestamp より細かい日時 より細かい日時
time 時間 時間
date 日付 日付
binary バイナリデータ バイナリデータ

※floatとdecimalについて詳しくはこちら
https://qiita.com/y-ken/items/b8601092d0261c3a556d
※datetime,time,dateについて詳しくはこちら
https://qiita.com/y-ken/items/b8601092d0261c3a556d

前提

  • rails 5.2.4.1
  • コマンド入力はファイル名のみですのでファイルは編集してください。

モデル作成時

postモデルを作成。

rails g model post

migrationファイルを編集してpostsテーブルにpostカラムをstring型で作成。

class CreatePosts < ActiveRecord::Migration[5.2]
  def change
    create_table :posts do |t|
      t.string :post
      t.timestamps
    end
  end
end

カラム追加

migrationファイル作成。

rails g migration AddTitleToPosts

postsテーブルにtitleカラムをstring型で追加。

class AddTitleToPosts < ActiveRecord::Migration[5.2]
  def change
    add_column :posts, :title, :string
  end
end

カラム削除

migrationファイル作成。

rails g migration RemoveTitleFromPosts

postsテーブルのtitleカラムを削除。

class RemoveTitleFromPosts < ActiveRecord::Migration[5.2]
  def change
    remove_column :posts, :title, :string
  end
end

データ型変更

migrationファイル作成。

rails g migration ChangeDatatypeNameOfPosts

postsテーブルのnameカラムをtext型に変更。

class ChangeDatatypeNameOfPosts < ActiveRecord::Migration[5.2]
  def change
    change_column :posts, :name, :text
  end
end

カラム名変更

migrationファイルを作成。

rails g migration RenameNameColumnToPosts

postsテーブルのnameカラムのカラム名をtitleに変更。

class RenameNameColumnToPosts < ActiveRecord::Migration[5.2]
  def change
    rename_column :posts, :name, :title
  end
end

モデル作成時(外部キー制約付)

postsテーブルにuser_idという外部キー付のカラムを作成。

class CreatePosts < ActiveRecord::Migration[5.2]
  def change
    create_table :posts do |t|
      #~省略~
      # user_idという名前で users.id への外部キー制約をはる
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end

カラム追加時(外部キー制約付)

postsテーブルにuser_idという外部キー付のカラムを作成。

class AddUserIdToPosts < ActiveRecord::Migration[5.2]
  def change
    #user_idという名前で users.id への外部キー制約をはる
    add_reference :posts, :user, foreign_key: true
  end
end

※foreign_key オプションを省略してしまうと、外部キー制約がはられません。
※indexを作りたくない場合はオプションで明示的に index: false を記述。

まとめ

今回は個人的に日頃使用するものでまとめました。
コマンド入力や外部キーについては参考記事にもっと詳しく書いてあります。

参考記事
https://qiita.com/dawn_628/items/13fa64dc6d600e921ce3
https://qiita.com/publichtml/items/1fba15d8071fab66d043 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む