takumiのエンジニア技術ブログ

takumiのエンジニア技術ブログ

未経験からエンジニアを目指しています!技術定着の為にこのブログにアウトプットしていきます!気軽にコメント等いただけるとありがたいです!

Deviseなしで、Twitterログインを実装する(OAuth認証)

f:id:golikyua:20200109135521j:plain


 

 

現在作成しているアプリケーションにdeviseなしでtwitteログインを実装したので、アウトプットします。

OAuth認証という認証により、twitter,facebook等のユーザー情報でログインできるよにしま。

OAuth認証については以下の記事が分かりやすかったので、一度読んでみてください。

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be

 

ruby 2.5.7

rails 5.2.3

 

 

やること

・twitterAPIを取得する。

・gemのインストール

・ルーティングの設定、APIキーの設定、OAuth認証の設定

twitterログインを実装

 

 

●twitterAPIを取得する。

twitterAPIの取得は以下の記事を参考させて頂き取得できました。

https://digitalnavi.net/internet/3072/

 

 

●gemのインストール

Gemfile

#twitter認証の為のgem
gem 'omniauth' gem 'omniauth-twitter'
#APIキーをGitHubに上げな為に環境変数に入れます。このgemは.envファイルから環境変数をロードする為のgemです。 gem 'dotenv-rails'

※APIキーをGitHub上に上げない方法は以前ブログに書いたので参考にしてみてください。

https://golikyua.hatenablog.com/entry/2019/12/02/135230

 

$bundle install

 

 

 

 

 

 ●ルーティングの設定、環境変数の設定、OAuth認証の設定

 

ルーティングの設定

routes.rb

Rails.application.routes.draw do

 ・・・
  
  get 'auth/:provider/callback', to: 'sessions#create'

 ・・・

end

今回はログイン機能をsessionsコントローラーのcreateアクションで実装したので、上のようにルーティングを設定しました。

 

 

 

環境変数の設定

.env

TWITTER_KEY="取得したAPI key"
TWITTER_SECRET="取得したAPI secret key"

これでローカル環境で、APIキーを使用することができますが、本番環境ではまだ使用できません。

 

自分の場合、アプリケーションをherokuにデプロイしていたので、heroku側でも環境変数を設定します。

 

コンソールで以下の設定を行います。

$ heroku config:set TWITTER_SECRET="取得したAPI key"
$ heroku config:set TWITTER_SECRET="取得したAPI secret key"

 

以下のコマンドで設定されている環境変数一覧が確認できるので、設定されているか確認してみましょう。

$ heroku config

 

 

OAuth認証の設定

config/initializers/omniauth.rbを作成し、以下を記入する。

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET']
end

 

 

 

twitterログインを実装

 

 userモデルに以下のカラムを追加します。

class AddColumnsToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :uid, :string
    add_column :users, :provider, :string
    add_column :users, :image_url, :string
  end
end
$ rails db:migrate

 

 

 

user.rb内に以下のメソッドを追加します。

class User < ApplicationRecord

・・・

  def self.find_or_create_form_auth(auth)
    provider = auth[:provider]
    uid = auth[:uid]
    name = auth[:info][:name]
    image = auth[:info][:image]

    self.find_or_create_by(provider: provider, uid: uid) do |user|
      user.name = name
      user.image_url = image
    end
  end

end

自分の場合これだけでログインしようとしたら、バリデーションに引っかかってログイン出来なかったので、バリデーションを変更します。

 

class User < ApplicationRecord

  validates :name, presence: true, unless: :uid?, length: { maximum: 30 }
  validates :email, presence: true, unless: :uid?, uniqueness: true, length: { maximum:255 },
                format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i.freeze }

  validates :password, format: { with: /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]{8,30}+\z/i }, length: { in: 8..30 }, unless: :uid?
  has_secure_password validations: false
  has_one_attached :image, dependent: :destroy
  has_many :posts, dependent: :destroy
  has_many :favorites, dependent: :destroy
  has_many :favorite_posts, through: :favorites, source: 'post'
  has_many :reviews, dependent: :destroy

 ・・・

end

 自分の場合nameカラム、emailカラム、passwordカラムにunless: :uid?を追加したら、バリデーションに引っかからなくなりました。

 

もしバリデーションに引っかかってログイン出来ない状態だったら、コメントアウトを使ってどこで引っかかっているか確認してみるといいと思います。

 

続いてsessionsコントローラーの設定を行います。

app/contorllers/sessions_controller.rb

class SessionsController < ApplicationController

・・・

  def create
    auth = request.env['omniauth.auth']
    if auth.present?
      user = User.find_or_create_form_auth(request.env['omniauth.auth'])
      session[:user_id] = user.id
      flash[:success] = "ユーザー認証が完了しました。"
      redirect_to user
    else
      @user = User.find_by(email: params[:session][:email])
      if @user && @user.authenticate(params[:session][:password])
        log_in @user
        flash[:success] = "ログインしました"
        redirect_to @user
      else
        flash.now[:danger] = "ログインに失敗しました"
        render 'new'
      end
    end
  end

 ・・・

end

ポイントは if文で、twitterログインと通常のログインを分けることです。これをやらないとエラーが発生します。(だいぶ苦労しました。)

 

最後に、ログインする為のビューを設定します。

app/views/sessions/new.html.erb

<%= link_to "Twitterアカウントでログイン", "/auth/twitter" %>

 

これで上手く行くと思います。

 

 

 

githubにpushする際に環境変数を使ってAPI_KEYを隠す方法

f:id:golikyua:20191202135218j:plain

 

 

作成中のアプリでGoogle mapを使用指定と思いGoogle Cloud PlatformでAPI_KEYを取得しました。

githubにpushする際に、API_KEYを隠さずにそのままpushしたら、Google Cloud

Platformからメールで怒られたので、API_KEYを隠してpushする方法を学んだので合うプットします。

 

やること

・gem 'dotenv-rails' をインストール

・.envを作成して、環境変数API_KEYを代入する。

API_KEYをそのまま書いている部分を環境変数に変更する。

・.gitignoreファイルに.envを追加して、git管理下から外す。

 

gem 'dotenv-rails' をインストール

gemfileにgem 'dotenv-rails'を記入して、bundle install

Gemfile

...
...

gem 'dotenv-rails'

 

 

.envを作成して、環境変数API_KEYを代入する。

ルートディレクトリ直下に.envを作成する。

.env

GOOGLE_API_KEY = "<自分のAPI_KEY>"

以下のようにコンソールで確認すると代入したAPI_KEYの値が出力されます。

[1] pry(main)> puts ENV["GOOGLE_API_KEY"]
<自分のAPI_KEY>
=> nil

 

 

 

API_KEYをそのまま書いている部分を環境変数に変更する。

自分の場合application.html.erbにそのままのAPI_KEYを書いていたので以下のように変更

<html>

  <head>
  ...
  ...
   
   <script src="//maps.google.com/maps/api/js?v=3.23&key=<%= E 
   NV["GOOGLE_API_KEY"] %>"></script>
 

 

.gitignoreファイルに.envを追加して、git管理下から外す。

.envがpushされたらなんの意味もないので.gitignoreに以下のように.env.を追加

.gitignore

...
...


/.env

 

 

これでpushすれば環境変数だけが見えて、API_KEYを隠せました。

めっちゃ簡単!!

 

CarrierWaveを使用した画像をアップロードする方法

f:id:golikyua:20191107184855j:plain

CarrierWaveを使用して画像投稿機能を実装したのでアウトプットします。

 

前提

ruby 2.6.3

Rails 5.0.7.2

開発環境 AWS cloud9

 

やること

・データベースに画像保存用のカラムを作成

・gem 'carrierwave'のインストール

・画像をアップロードするためのコードを作成

・config/application.rbで設定変更

・モデルに関連付ける

 

 

・データベースに画像保存用のカラムを作成

$ rails g model Topic user_id:integer description:string image:string

モデルを作成してimageカラムに「画像がどこにあるか」という情報を持たせる。

 

$ rails db:migrate

maigrationファイルをデータベースに反映させる

 

 

 

・gem 'carrierwave'のインストール

 

Gemfile

gem 'carrierwave'

Gemfileに上のgemを書いてbundle install

 

 

 

・画像をアップロードするためのコードを作成

 

コンソールで以下のコマンドを入力

$ rails g uploader image

このコマンドで app/uploaders/image_uploader.rb が作成される。

 

 

・config/application.rbで設定変更

class Application < Rails::Application
  config.autoload_paths += Dir[Rails.root.join('app', 'uploaders')]
end

 

・モデルに関連付ける

app/models/topics.rb

class Topic < ApplicationRecord
  mount_uploader :image, ImageUploader
end

これでcarrierwaveの設定は完了。

 

 

☆画像タイプの制限と画像サイズの制限についても学んだので書いておく

 

画像タイプの制限、画像サイズの制限共に以下のコマンドで作成された app/uploaders/imageuploader.rbのコードを変更する

$ rails g uploader image

 

・画像タイプの制限

ファイル内のコードでコメントアウトされている以下の文のコメントアウトを解除する。

app/uploaders/imageuploader

def extension_whitelist
    %w(jpg jpeg gif png)
 end

 

・画像サイズの制限

今回は10MB以下の画像のみ使えるようにする。

これもファイル内に以下のメソッドを足すだけ

app/uploaders/imageuploader

def size_range
    1..10.megabytes
end

 

 

 

Githubへのpushの流れ

f:id:golikyua:20191106195829j:plain


 

githubへpushを勉強したのでアウトプットします。

 

前提

開発環境 AWScloud9

 

 

やること

sh公開鍵の登録

githubに登録する用の秘密鍵/公開鍵のペアを作成する。

$ cd~/.ssh
$ ssh-keygen
#色々と入力が求められるが空欄でEnterでも可能
$ cat ~/.ssh/id_rsa.pub

作成した公開鍵の内容を表示し、コピペしてgithubに登録。

 

pushしたいディレクトリをgit管理下に置く

githubにpushしたいディレクトリに移動して下のコマンドを実行するとディレクトリをgit管理下に置くことができる。

$git init

 

pushするディレクトリをcommitする

#このコマンドで対象をステージングエリアへ(git addの後に続く「.(ピリオド)」は、今のディレクトリ配下全てのファイルという意味)
$git add . 

#このコマンドでローカルリポジトリへコミットする。 -mの後はコミットメッセージ
$git commit -m "コミットメッセージ"

 

リモートリポジトリを登録する

#以下のコードでリモートリポジトリを登録する
$git remote add origin git@github.com:アカウント名/リポジトリ名.git

※間違ったリモートリポジトリを登録してしまった場合

#以下のコマンドで変更可能
$git remote set-url <リポジトリの名前> <新しいリポジトリのURL>

#以下のコマンドでリモートリポジトリのURLを確認できる
$ git remote -v

 

githubにpushする

#以下のコマンドでpushできる
$git push -u origin master



参考
http://tetsuyai.hatenablog.com/entry/20110912/1315798082

 

 

ペジネーション

f:id:golikyua:20190924212855j:plain


ペジネーションについて勉強したのでアウトプットします!


目標

「タスク一覧の表示数を制限して、複数のページに分割して表示する

やること

・kanimariのインストール
・ページ番号に対応した範囲の内容を表示する
・ビューにペジネーションのための情報を表示する


本題

・kaminariのインストール

Gemfileに「gem 'kaminari'」を記入して、「bundle」を実行。


・ページ番号に対応した範囲の内容を表示する。

ページ番号に対応するデータの範囲を検索の部分はkaminariが提供するpageというスコープを使うこと簡単にできる!

これを用いてindexアクションを編集

app/controller/tasks_controller.rb

def index
   @q = current_user.tasks.ransack(params[:q])
   @tasks = @q.result(distinct: true).page(params[:page])
...
end

ちなみにデフォルトで、1ページあたりに表示できる件数は25件になっている。



・ビューにペジネーションのための情報を表示する
ペジネーションを行う際に必要な情報は以下の3つ
1.現在どのページを表示しているかの情報
2.他のページに移動するためのリンク
3.全データ何件なのかという情報

kaminariはこれらを表示するためのヘルパーメソッドが用意されている
1.2の目的の為にpaginateと3の目的の為にpage_entries_info
app/views/tasks/index.html.slim

...

.mb-3
   = paginate @tasks
   = page_entries_info @tasks

...

これで、一覧ページが複数に分割されてが表示される!

タスク一覧画面に検索機能を実装する(ransack)

f:id:golikyua:20190923173318j:plain

 

検索機能の実装について勉強したので、アウトプットしたいと思います!

 

目標

 

「タスク一覧画面に検索機能を追加」

 

やること

 

・Ransackのインストール

・名称による検索機能の実装

・登録日時による検索機能の実装

・検索条件を絞る

 

 

本題

 

・Ransackのインストール

Gemfileに「gem 'ransack'」を入力し、bundlerで「bundle」を実行。

 

・名称による検索機能の実装

ransackをインストールするとransackメソッドがモデルに追加される。

 

ransackメソッドを使用して、コントローラを修正する。

def index
@q = current_user.tasks.ransack(params[:q]) @tasks = @q.result(distinct: true).recent ... end

 

タスク一覧画面に検索フォームを作成する。

ranscakによりsarch_form_forという検索フォームを作成されるヘルパーが提供される!

h1 タスク一覧

= search_form_for @q, class: 'mb-5' do |f|
  .form-group.row
    = f.label :name_cont, '名称', class: 'col-sm-2 col-form-label'
    .col-sm-10
      = f.search_field :name_cont, class: 'form-control'
 .form-group
  = f.submit class: 'btn btn-outline-primary' = link_to '新規登録', new_task_path, class: 'btn btn-primaty mb-3'

※ranscakを利用する際は、検索フィールドの名前を一定のルールに従ってつける。

今回は名称に「検索フォームで入力した文字が含まれている」という検索を実現したいので「name_cont」という名前をつける。

 

検索時のSQLを調べると、PostgreSQLのILIKE(大文字と小文字の区別をしない)が使われていた。これは検索フィールドの名前を「name」にした結果である。

 

また「_cont」は、検索文字列を含むものを検索するransackが用意したマッチャー。

 

よって「name_cont」を検索フォームの名前にすることで、入力した文字列を含むタスクを検索できるようになる。

 

・登録日時による検索

タスク一覧の検索フォームに登録日時のフィールドを追加する。

h1 タスク一覧

= search_form_for @q, class: 'mb-5' do |f|
  .form-group.row
    = f.label :name_cont, '名称', class: 'col-sm-2 col-form-label'
    .col-sm-10
      = f.search_field :name_cont, class: 'form-control'
  .form-group.row
    = f.label :created_at_gteq, '登録日時', class: 'col-sm-2 col-form-label'
    .col-sm-10
      = f.search_field :created_at_gteq, class: 'form-control'
  .form-group
    = f.submit class: 'btn btn-outline-primary'

= link_to '新規登録', new_task_path, class: 'btn btn-primary mb-3'

登録日時検索フィールドで使用した_gteqは、greater than or equalの略で、「入力した値より大きいか同じ」なものを検索する。

日時の場合は、「同じ日にちかそれより未来の日にち」を検索する。

 

登録日時の検索後のSQLでは、比較演算子の「>=」が使われている!

 

・検索条件を絞る

どうやらユーザーが意図的にパラメータを加工すると、他のカラムを使った検索ができてしまうらしい。

それを防ぐために、検索結果に制限をかける。

app/models/task.rb

class Task < ApplicationRecord


def self.ransackable_attributes(auth_object = nil)
  %w[name created_at]
end

def self.ransackable_associations(auth_object = nil)
  []
end

...

end

 ransackable_attributesで、検索対象にすることを許可するカラムを指定する。

nameとcreate_atを指定することで、他のデータを検索フォームに入力しても無視される。

 

ransackable_associationsは、検索条件に含める関連を指定できる。

このメソッドを空の配列を返すようにオーバーライドすることで、検索条件に意図しない関連を含めないようにすることができる。

 

 

まとめ

・ransackをインストール

・ransackメソッドを使って、コンソールの編r集

・search_form_foを使い、ビューに検索フォームを作成する。

・rasackable_attributes, ransackable_associationsメソッドで検索条件をつける。

 

ransackめちゃめちゃ便利!!

 

 

 

 

 

Active Storageを使用したタスク管理アプリへの画像添付(development環境)

f:id:golikyua:20190922105837j:plain

 

 

画像ファイル添付について勉強したのでアウトプットする!

 

目標

「タスク管理アプリのタスク詳細画面に画像を表示する」

 

やること

・Active Storageをタスク管理アプリで使うための準備。(Rails5.2以降はgemのインストールはされている)

・タスクモデルに画像を添付できるようにする。

 

大まかにはこんな感じ!

 

本題

 

Active Storageをタスク管理アプリで使うための準備

 

マイグレーションファイルの出力。

$bin/rails active_storage:install

 

 

db/migrate/xxxxxxxxxxxxxx_create_active_storage_tables.active_storage

# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
  def change
    create_table :active_storage_blobs do |t|
      t.string   :key,        null: false
      t.string   :filename,   null: false
      t.string   :content_type
      t.text     :metadata
      t.bigint   :byte_size,  null: false
      t.string   :checksum,   null: false
      t.datetime :created_at, null: false

      t.index [ :key ], unique: true
    end

    create_table :active_storage_attachments do |t|
      t.string     :name,     null: false
      t.references :record,   null: false, polymorphic: true, index: false
      t.references :blob,     null: false

      t.datetime :created_at, null: false

      t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
      t.foreign_key :active_storage_blobs, column: :blob_id
    end
  end
end

このマイグレーションファイルはActive Storageが利用する二つのテーブル、active_storage_blobs と active_storage_attachment を作成する。

 

二つのテーブルは、ActiveStorage::Bolb と ActiveStorege::Attachmentというモデルに紐ずいている。

ActiveStorage::Bolbは添付されたファイルに対応するモデルで、ファイルの実態をデータベース外で管理する!

ActiveStorege::AttachmenはActiveStorage::Bolbとアプリ内の様々なモデルを関連づける中間テーブル!

 

今回はTaskとActiveStorege::boldを紐づけます。

紐づけのイメージは以下の感じ!(※この画像はuserモデルとの紐づけだが、今回はTaskモデルとの紐づけ)

 

 

マイグレーションファイルをDBに反映させる

$bin/rails db:migrate

 

 

添付したファイルの実体を管理する場所について設定を行う。

Rails.application.config.active_storage.serviceにファイルを管理する場所の名前を与え、その名前に対応する設定を、config/storage.ymlに定義する。

(デフォルトでは、development環境のファイル管理場所はlocalとなっている)

 

config/environments/development.rb

# Store uploaded files on the local file system (see config/storage.yml for options)
  config.active_storage.service = :local

 

local設定がされているconfig/storage.yml

 test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
...

 

 

タスクモデルに画像を添付できるようにする。

 

1つのタスクに1つの画像を紐づける。

class Task < ApplicationRecord
  has_one_attached :image
 ...
end

has_one_attacthedというメソッドにより1つのタスクに1つの画像を紐づけること、その画像をTaskモデルからimageと呼ぶことを指定。

 

ビューを編集して新規登録画面、更新画面の両方で画像を指定できるようにする。

app/views/tasks/_from.html.slim(formパーシャル)

= form_with model: task, local: true do |f|
  .form-group
    = f.label :name
    = f.text_field :name, class: 'form-control', id: 'task_name'
  .form-group
    = f.label :description
    = f.text_area :description, rows: 5, class: 'form-control', id: 'task_description'
  .form-group
    = f.label :image
    = f.file_field :image, class: 'form-control'
  = f.submit nil, class: 'btn btn-primary'

form_withの中に新しくform-groupクラスを作成し、画像選択フォームを作成する。

 

画像フィールドのラベルが日本語表記になるように、config/locales/ja.ymlを編集

attributes:
      task:
        id: ID
        name: 名称
        description: 詳しい説明
        image: 画像
        created_at: 登録日時
        update_at: 更新日時

 

task_paramsメソッドを変更

app/controller/tasks_controllerdef task_params

def task_params
    params.require(:task).permit(:name, :description, :image)
  end

許可するパラメーターのキーとして:imageを追加する。

ビューを編集して取得した画像を、詳細画面で表示する。

 app/views/tasks/show.html.slimに以下を追加

...
tr th= Task.human_attribute_name(:image) td= image_tag @task.image if @task.image.attached?
...

 image_tagを使う場合は、実際に画像が添付されていないと、エラーが発生してしまうため、@task.image.attached?をいう判定メソッドを使用して、「画像が添付されている場合のみ、画像を表示する」というようにします!

 

まとめ

マイグレーションファイルを取得する

マイグレーションファイルの反映

 

一つのモデルに一つの画像を紐づける

新規登録、更新画面のビュー編集

imageラベルが日本語になるようにja.ymlファイルを編集

task_paremsを変更し、:imageを許可するパラメーターに追加

詳細画面のビュー変更 image_tagは画像がないとエラーになるため @task.image.attashed?で判定

 

 

参考文献

「現場で使える Ruby on Rails 5 速習実践ガイド 」