20200627のRailsに関する記事は21件です。

ゲストログイン機能について

ポートフォリオの作成にあたり、ゲストログイン機能は必須です。
忙しい担当者の方が、わざわざ会員登録をするよりも
ささっと多くの機能を見る場合が多いためです。

ゲスト登録機能は、下記の記事の方法で搭載できました。

https://qiita.com/take18k_tech/items/35f9b5883f5be4c6e104#%E3%81%9D%E3%81%AE2-%E3%82%B2%E3%82%B9%E3%83%88%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E6%A9%9F%E8%83%BD%E3%81%AE%E5%AE%9F%E8%A3%85%E6%96%B9%E6%B3%95

こちらの「その2: ゲストログイン機能の実装方法」で実装しました。

その際、下記の2点は少しつまづいたので
記載指定おきます。

[1]routes.rbの設定

config/routes.rb
  devise_scope :user do
    post 'users/guest_sign_in', to: 'users/sessions#new_guest'
  end

こちらは、最初勝手に「devise_for :users」を修正するのかと思いましたが
追記する形で大丈夫でした。

デバイスを利用している方ですと

config/routes.rb
  devise_for :users

  devise_scope :user do
    post 'users/guest_sign_in', to: 'users/sessions#new_guest'
  end

こんな感じですね。

[2]modelの設定

あとは記事の通りにやればうまく行くのですが、
自分は1つだけエラーになりました。

上記の設定ですと、ゲストユーザーのアカウントは
メールアドレスとパスワードの作成だけされています。

そのため、アカウントの作成に「nickname」などが
必須項目であった場合、エラーが起こります。

その際は、モデルでその情報を追記してあげましょう。

models/user.rb
  def self.guest
    find_or_create_by!(nickname: 'ゲスト', email: 'guest@example.com') do |user|
      user.password = SecureRandom.urlsafe_base64
    end
  end

これで問題なく作成できました!

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

Mysql2::Error::ConnectionError: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock'

たまに以下エラーが起きることがあります。

# RAILS_ENV=production bundle exec rake db:migrate
rake aborted!
Mysql2::Error::ConnectionError: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

結論から言うと、DBの接続情報が誤っているためにこのエラーが起きます。

やることはconfig/database.ymlに関して以下のことになります

  • 次の値に対して
    • config/database.yml自体に設定している値
    • config/database.ymlから指定している環境変数
  • 次の事態が起きていないかどうか確認する
    • 値が間違っている
    • DB設定の値が更新されていて、更新漏れが起きている

私はconfig/database.ymlでは環境変数を指定することが多く、
いつの間にかに更新されていた環境変数を更新漏れしていたり、
dockerの他のコンテナで指定していたDB名と環境変数で指定していたDB名が間違っていたり
と度々このエラーに遭います。

他のエラーのときにもこのエラー起きるかもしれませんが、私の場合100%接続情報エラーなので、このエラーが紛らわしくて仕方ないです。
接続設定間違ってるよと言ってくれれば律儀に/var/run/mysqld/mysqld.sockがどういうものを調べに行ったり、ググらなくて済むので

他の場合は以下のQiitaの場合かもしれません
https://qiita.com/fujitora/items/d341c52706d1954cae28

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

初投稿。グループ機能作成中に起きた事

Routing Error

グループ機能作成中に起きたRouting Errorについて、
この画像では、controllerがgroupsになっている。
スクリーンショット 2020-06-27 17.58.09.png
下の画像では、groupsとなっているが、ここでgroupにした事でRouting Errorが起きてしまった。
スクリーンショット 2020-06-27 17.57.30.png

マイグレーションについて

例えばこの様マイグレーションファイルにdb:migrateで間違った記述をしてしまった時
スクリーンショット 2020-06-27 18.08.31.png
db:rollbackで戻して訂正する事ができる。
また、db:migrateで訂正完了です。

db:rollback

db:rollbackでは一つ前しか戻せない。

db:rollback STEP=数字

db:rollback STEP=数字を使う事で任意に所まで戻す事ができる。

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

Rails CRUD機能実装①(今回は、新規追加、削除)

この記事の目的

railsを用いた、CRUD機能をもったwebアプリケーションの作成手順です。鳥の写真と名前を投稿できるサイト(birdtweet)です。

railsアプリケーションの作成

railsのバージョン6.0.0を指定。-dの後ろに使用するDB(今回はmysql)を指定。

rails _6.0.0_ new birdtweet -d mysql

設定変更。

config/database.yml
...
#  encoding: utf8mb4
  encoding: utf8
...

DB作成

cd birdtweet
rails db:create

Gemfileの編集
プロジェクト直下にあるGemfileを開く

Gemfile
# gem 'mysql2', '>= 0.4.4'
gem 'mysql2', '>= 0.5.3'
........
(ファイル最下行)
gem 'pry-rails'

ターミナルで設定内容の更新

bundle update

modelの作成

ターミナルにて

rails g model bird

カラムを追記

db/migrate/2020********create_birds.rb
...
create_table :birds do |t|
      t.string :name
      t.text :pic
      t.timestamps
end
...

コマンドにて、変更内容の更新!

rails db:migrate

コンソールでDBにデータをいくつか入れます。

rails c
Bird.create(name: "ツノメドリ", pic: "https://cdn.pixabay.com/photo/2020/05/26/13/22/puffins-5223057_1280.jpg")
Bird.create(name: "カワセミ", pic: "https://cdn.pixabay.com/photo/2017/02/07/16/47/kingfisher-2046453_1280.jpg")
exit

コントローラの作成

以下のコマンドで、コントローラを作成します。

rails g controller birds

複数形にしたり、しなかったり混乱しますが、
モデルだけが単数形です!

ここからの流れ

各機能作成の流れはほぼほぼ同じです!

routes.rbに追記

index.html.erbに各機能へのリンクを追記(index意外)

birds_controller.rbに追記

機能名.html.erb作成

一覧表示機能

railsには7つのアクションがある。


  • index:一覧

  • show:詳細

  • new:新規作成画面への遷移

  • create:新規データの保存

  • edit:編集画面への遷移

  • update:編集内容の更新

  • destroy:削除

全機能を使わない場合は、onlyオプションで使うものだけを指定する。

config/routes.rb
# resourcesの後ろは、モデル名の複数形
  resources :birds, only: :index
app/controllers/birds_controller.rb
...
def index
    # インスタンス変数にBirdモデルのデータを格納します。
    @birds = Bird.all
  end
...

index.html.erbファイル作成
ビュー内でインスタンス変数を、eachメソッドを使って全て表示します。

app/views/birds/index.html.erb
<% @birds.each do |bird| %>
<%= bird.name %>
<div style=
"background-image: url(<%= bird.pic %>); 
 background-position: center center;
 background-size: cover;
 width: 300px; 
 height: 300px;
 margin-bottom: 10px;
 ">
</div>
<%end%>

以下のように、表示されました!!!
image.png

新規投稿機能

流れは同じく、routes → controller → viewなのですが、投稿画面への移動(new)からのデータ追加(create)なので、一連の流れを二回やります!

routes.rb
Rails.application.routes.draw do
  # resourcesの後ろは、モデル名の複数形
  # アクションが複数あるときは、配列の形にしてあげます
  resources :birds, only: [:index, :new]
end
index.html.erb
<%=link_to '新規投稿', new_bird_path, method: :get%>
...

image.png

birds_controller.rb
...
def new
  # Birdモデルのインスタンス化したものを、インスタンス変数に格納します。
  @bird = Bird.new
end
...

new.html.erb作成

app/views/birds/new.html.erb
<%# 遷移先urlを記載しなくても、インスタンス変数に入れたモデルから判断して、遷移してくれます %>
<%=form_with(model: @bird, local: true) do |form| %>
  <%= form.text_field :name, placeholder: "鳥の名前" %>
  <%= form.text_field :pic, placeholder: "鳥の写真のURL" %>
  <%= form.submit "投稿" %>
<%end%>

http://localhost:3000/birds/new
にアクセスすると投稿画面ができてます!
image.png

次は、入力情報を登録する機能です。

routes.rb
Rails.application.routes.draw do
  # resourcesの後ろは、モデル名の複数形
  # アクションが複数あるときは、配列の形にしてあげます
  resources :birds, only: [:index, :new, :create]
end

birds_controller.rb
formからのハッシュの形でデータが送られてきます。
全てのデータを受け取るのは危険です!

名前と写真のURLだけでいいのに、ログイン情報のキーを悪意で追加されて、他人のログイン情報を勝手に変更し乗っ取るということができてしまいます。
そこで、ストロングパラメータを使います。
また、privateを記述した行より下は、他のファイルから呼び出せないメソッドとなります。メソッドが増えたときに、見るべきファイルを減らせるメリットがあります。

birds_controller.rb
...
  def create
    # private下に定義したbird_paramで指定したパラメータを受け取り、保存する。
    Bird.create(bird_param)
  end

  private
  def bird_param
    # params.require(:モデル名).permit(:カラム名,:カラム名,......)
    params.require(:bird).permit(:name, :pic)
  end
...

create.html.erb作成

app/views/birds/create.html.erb
<h3>投稿完了!</h3>
<a href="/birds">一覧へ</a>

image.png

image.png

今のままだと、何も入力しないで登録ができてしまいます。そこでバリデーションチェックするための記述をします。

app/models/birds.rb
class Bird < ApplicationRecord
  # 入力必須にしたいカラム名を書きます。
  validates :name, presence: true
  validates :pic, presence: true
end

これでからのデータを登録できなくなりました!!

削除機能

routes.rb
...
resources :birds, only: [:index, :new, :create, :destroy]
...

削除機能へのリンクの調べかたは、まずコマンドで

rails routes

とします。
すると、以下の出力がされます。
Prefixの値に「_path」をつけると、URIに記載されているURLが出力されます。
image.png

今回は削除なので、VerbにDELETEと描かれている行に注目します。Prefixは「bird」となっているので、「bird_path」とすればいいですね。メソッドは、「Verb」に「DELETE」とあるので「delete」とすれば良いですね。あとは、idも渡します!

index.html.erb
...
<%= bird.name %><%=link_to "削除", bird_path(bird.id), method: :delete%>
...

リンクが表示されました。
image.png

bird_controller.rb
...
  def destroy
    # 今回は抽出したデータをどこにも送らないので、@をつけません。
    bird = Bird.find(params[:id])
    bird.destroy
  end
...
destroy.html.erb
<a href="/birds">一覧画面</a>

image.png
image.png
つのめどりがちゃんと削除されています!

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

【Rails】form_withを使ったフォロー機能の実装手順を解説します

概要

form_withを使ってフォロー機能を実装する手順を書いていきます!

以下の機能を実装していきます
1.ユーザーをフォロー/フォロー解除できる機能
2.フォローユーザー/フォロワーの一覧表示機能

Railsでフォロー機能を作る方法の記事の方を参考にしており、特にフォロー機能に関するアソシエーションの詳しい解説などはこの記事が大変参考になりました!

前提

環境
 ・Rails 5.2.4.2
 ・Ruby 2.6.5
 ・Slim
 ・devise

加えて、User系機能が実装されていることが前提となります。

上記の環境でのRailsアプリケーションのセットアップ方法
 ・Railsアプリケーションをセットアップ後にdeviseとSlimを導入する手順

↓実装後はこんな感じになります。(見栄えが簡素ですみません…)↓
ezgif.com-video-to-gif (3).gif

フォロー/フォロー解除機能の実装

1.Relationshipモデル作成

$ rails g model Relationship user:references follow:references

・ここで作成されるrelationshipsテーブルが、フォローするユーザー&フォローされるユーザーにとっての中間テーブルとなる。

userをフォローする側のユーザーモデル、followをフォローされる側のユーザーモデルとして、中間テーブルには認識させます。


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

db/migrate/[timestamps]_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[5.2]
  def change
    create_table :relationships do |t|
      t.references :user, foreign_key: true
      t.references :follow, foreign_key: { to_table: :users }
 
      t.timestamps
 
      t.index [:user_id, :follow_id], unique: true
    end
  end
end

・userとfollowでそれぞれ参照先のモデルを分けているのは、フォローする側のユーザフォローされる側のユーザーとでは別々のモデルとして考える必要があるからです。

t.references :user, foreign_key: trueuserモデルに対する外部キーを張っています。

t.references :follow, foreign_key: { to_table: :users }followモデルに対する外部キーを張っています。followモデルというのは実在しない架空のモデル名で、今この時勝手に作成してます。よって、{ to_table: :users }とする事で、参照元のモデルを教えてあげています。

t.index [:user_id, :follow_id], unique: trueuser_idとfollow_idの組み合わせに一意性を持たせることでデータの重複を防ぎます、つまり同じユーザーを2度フォローされる事を防いています。


3.DBの内容を保存

$ rails db:migrate


4.RelationshipモデルにUserに対するアソシエーションを記述する。

app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :user
  belongs_to :follow, class_name: "User"
 
  validates :user_id, presence: true
  validates :follow_id, presence: true
end

belongs_to :userこれはいつも通りのアソシエーションです。

belongs_to :follow, class_name: "User"followクラスに所有される事を記述していますが、上述したようにfollowは架空のクラスですので、class_name: "User"によって参照元のモデルを教えてあげます。


5.UserモデルファイルにRelationshipに対するアソシエーションを記述する。

app/models/user.rb
class User < ApplicationRecord
 
#==============あるユーザーがフォローしているユーザーとのアソシエーション=================
  has_many :relationships, foreign_key: "user_id",
                           dependent: :destroy
  has_many :followings, through: :relationships, source: :follow
#==========================================================================
 
#==============あるユーザーをフォローしてくれてるユーザーとのアソシエーション================
  has_many :passive_relationships, class_name: "Relationship",
                                   foreign_key: "follow_id",
                                   dependent: :destroy
  has_many :followers, through: :passive_relationships, source: :user
#===========================================================================
end

・上記のRelationshipモデルファイルでは、あるユーザーがフォローしているユーザーにアクセスするためのアソシエーションと、あるユーザーがフォローされているユーザーにアクセスするためのアソシエーションを記述しています。
 つまりどういうことか、詳しくは図で後述します。

5-1.まずは、あるユーザーがフォローしているユーザーとのアソシエーションの解説です!

has_many :relationships, foreign_key: "user_id", dependent: :destroyこれは、あるUserが中間テーブルrelationshipsの外部キーuser_idにアクセスする事を可能にするアソシエーションを宣言をしています。

以下のようにイメージすると分かり易いかと思います!

スクリーンショット 2020-06-27 13.44.10.png

has_many :followings, through: :relationships, source: :followこれは、あるUserが中間テーブルrelationshipsのfollow_idを通して、followingモデルにアクセスする事を可能にするアソシエーションを宣言をしています。

イメージで言うと以下のような感じです!

スクリーンショット 2020-06-27 13.57.34.png

・上記の2行により、User.followingsとする事でUserがフォローしているユーザー達を取得できるようになります。これが取得できるようになると、あるUserのフォローユーザーの一覧表示などが簡単に行うことができます。

5-2.次は、あるユーザーをフォローしてくれているユーザーとのアソシエーションの解説です!

has_many :passive_relationships, class_name: "Relationship", foreign_key: "follow_id", dependent: :destroyこれは、あるUserがpassive_relationshipsのfollow_idへアクセスする事を可能にするアソシエーションを宣言をしています。
  passive_relationshipsは、この場で勝手に作った架空のモデルなので、class_name: "Relationship"として参照元のモデルを教えています。

イメージは以下の通りです!

スクリーンショット 2020-06-27 14.13.33.png

has_many :followers, through: :passive_relationships, source: :userこれは、あるUserがpassive_relationshipsのuser_idを通してfollowersへアクセスする事を可能にするアソシエーションの宣言をしています。

イメージは以下の通りです!

スクリーンショット 2020-06-27 14.26.37.png

・上記の2行により、User.followersとすれば、Userをフォローしてくれているユーザー達を取得することが可能になります。これを使えば、あるユーザーをフォローしてくれているユーザーの一覧表示などが簡単に行えるようになります!


6.Userモデルファイルに、フォロー関連のインスタンスメソッドを定義する

app/models/user.rb
class User < ApplicationRecord
  .
  .
  .
  #<アソシエーション関連の記述省略>
  .
  .
  .
  def following?(other_user)
    self.followings.include?(other_user)
  end

  def follow(other_user)
    unless self == other_user
      self.relationships.find_or_create_by(follow_id: other_user.id)
    end
  end

  def unfollow(other_user)
    relationship = self.relationships.find_by(follow_id: other_user.id)
    relationship.destroy if relationship
  end
end

following?(other_user)User.followings.include?(@user)などとすることで、Userが@userのことをフォローしてるかどうかを確かめます。フォローボタン実装の際に、フォローするボタンを表示するのか、それともフォローを解除するボタンを表示するのかを条件分岐させるために使います。

follow(other_user)User.follow(@user)とすることで、@userをフォローすることができるようにします。unless self == other_userで、自分のことをフォローできないようにしてます。find_or_create_byを使って、@userのidレコードが存在する場合はそれを参照し、なければ作成するようにしてます。

unfollow(other_user)User.unfollow(@user)とする事で、@userのフォローを解除できるようにします。

ユーザーをフォローするメソッド、フォロー解除するメソッドの定義ができたので、次はこれらのメソッドを使って実際にDBへアクセスしてフォロー・フォロー解除する機能をrelationshipsコントローラを作成して、実装していきます!


7.relationshipsコントローラを作成する。

$ rails g controller relationships 
app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController

  before_action :set_user

  def create
    following = current_user.follow(@user)
    if following.save
      flash[:success] = "ユーザーをフォローしました"
      redirect_to @user
    else
      flash.now[:alert] = "ユーザーのフォローに失敗しました"
      redirect_to @user
    end
  end

  def destroy
    following = current_user.unfollow(@user)
    if following.destroy
      flash[:success] = "ユーザーのフォローを解除しました"
      redirect_to @user
    else
      flash.now[:alert] = "ユーザーのフォロー解除に失敗しました"
      redirect_to @user
    end
  end

  private
  def set_user
    @user = User.find(params[:relationship][:follow_id])
  end
end

relationshipsコントローラでは、ユーザーをフォローするcreateアクション、フォローを解除するdestroyアクションしか機能しないので、createアクション、destroyアクションだけを記述します。
  
フォロー/フォロー解除ボタンフォームからは、params[:relationship][:follow_id]と言う形でデータが送信されるので、find(params[:relationships][:follow_id])と言う形でデータを受け取ります。

before_action :set_userそれぞれのアクションでは必ず@userを取得する事になるので、set_userプライベートメソッドを定義してからbefore_actionを使ってあらかじめ取得させています。

createアクションログインしているユーザー(current_user)が、取得した@userをフォローしています。Userモデルファイルで定義したfollow(other_user)インスタンスメソッドを使用しています。

destroyアクションログインしているユーザーが、取得したユーザーのフォローを解除しています。Userモデルファイルで定義したunfollow(other_user)インスタンスメソッドを使用しています。

実際にDBへアクセスしてフォロー・フォロー解除する機能が仕上がったので、次は実際にフォロー・フォロー解除するためのボタンの実装を行います!


8.フォロー/フォロー解除ボタンをform_withで実装する。

・始めに、app/viewsディレクトリ配下に、relationships/_follow_form.html.slimファイルを作成してください。

app/views/relationships/_follow_form.html.slim
- unless current_user == user
  #フォロー解除ボタン
  - if current_user.following?(user)
    = form_with model: @relationship, url: relationship_path, method: :delete, local: true do |f|
      = f.hidden_field :follow_id, value: user.id
      = f.submit "フォローを解除する"
  #フォローボタン
  - else
    = form_with model: @set_relationship, url: relationships_path, local: true do |f|
      = f.hidden_field :follow_id, value: user.id
      = f.submit "フォロー"

- unless current_user == user自分自身のことをフォローできないよう、ボタン自体の表示を切り替えています。ここでのuserとは、このパーシャルを配置するビューが取得する@userのことを指します。

8-1.フォロー解除ボタンの解説

- if current_user.following?(user)ログインユーザーが相手ユーザーをフォローしているかどうかを確かめています。フォローしていればフォロー解除ボタンを表示し、フォローしていなければフォローボタンを表示するようにしています。

= form_with model: @relationship, url: relationship_path, method: :delete, local: true do |f|こちらはフォロー解除用のボタンです。

  model: @relationship@relationshipはインスタンス変数で、ログインユーザーと相手ユーザーとのRelationshipを取得しています。
  (app/views/users/show.html.slimにこのフォローボタンのフォームを配置するなら、app/controllers/users_controller.rbファイルのshowアクションに、@relationshipインスタンス変数を定義する事になります。)

  url: relationship_pathは、relationshipsコントローラのdestroyアクションへのルーティングです。ルーティングの記述はあとで行います。

  method: :deleteでは、HTTPリクエストでDELETEメソッドを指定しています。

  local: trueは、これを付けないとフォームの送信の仕様がAjaxになり、データを送信できなくなるのでform_withでは必ず記述します。

f.hidden_field :follow_id, value: user.id:この部分は、実際に完成形のソースコードを見てみるとわかりやすいです。__

スクリーンショット 2020-06-27 15.28.15.png

:follow_idを指定することで、params[:relationship][:follow_id]と言う形でデータが送信されます。relationshipsコントローラのset_userプライベートメソッドで定義した部分では、このデータを受け取っています。

value: user.idを指定することで、:follow_idにフォローしている相手ユーザーのidが格納されて、データが送信されます。

8-2.フォローボタン

= form_with model: @set_relationship, url: relationships_path, local: true do |f|相手ユーザーをフォローするためのフォームです。

  model: @set_relationship@set_relationshipインスタンス変数では空のモデルを生成しており、この空のモデルを参照して、params[:relationship][:follow_id]と言う形でデータが送信されるようにします。

  url: relationships_pathは、relationshipsコントローラのcreateアクションへのルーティングです。ルーティングの記述は後で行います。

= f.hidden_field :follow_id, value: user.id:これも実際の完成後のソースコードを確認してみます。

スクリーンショット 2020-06-27 15.47.01.png

フォロー解除ボタンと同じで、params[:relationship][:follow_id]と言う形でデータが送信されてます。

・フォロー/フォロー解除ボタンの作成が完了しましたので、次はこのデータが正常にアクションへ送られるようルーティングを設定します!


9.relationshipsコントローラへのルーティングを追加する。

config/routes.rb
Rails.application.routes.draw do
  .
  .
  #<他のルーティングは省略>
  .
  .
  resources :relationships, only: [:create, :destroy]

$ rails routes | grep relationshipコマンドで、追加されたルーティングを確認してみます。

$ rails routes | grep relationship
  relationships  POST   /relationships(.:format)                                                        relationships#create
  relationship   DELETE /relationships/:id(.:format)                                                             relationships#destroy

・これでrelationships_pathでフォロー/relationship_pathでフォロー解除できるようになってるのが確認できたかと思います。


10.フォロー/フォロー解除ボタンを配置する。

・相手ユーザーのページに飛んだ際にフォローしたり、フォロー解除できるようにする場合を考えて、app/views/users/show.html.slimファイルに配置していきたいと思います。

app/views/users/show.html.slim
h1 ユーザープロフィールページ
p
  = "名前 : "
  = @user.username

#================フォローボタンの追加=============================

  = render 'relationships/follow_form', user: @user

#============================================================

p= link_to "プロフィール編集", edit_user_path(current_user)

・user: @user__パーシャルに記述したuserが、showアクションで定義されている@userを参照しています。__

・ボタンの配置はできましたが、このままではボタンは機能しません。先ほども述べたように、@relationshipインスタンス変数と、@set_relationshipインスタンス変数を、showアクションに定義してあげなければならないからです。


11.app/controllers/users_controller.rbのshowアクションに、インスタンス変数を定義する。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def show
    @user = User.find(params[:id])
    @relationship = current_user.relationships.find_by(follow_id: @user.id)  
    @set_relationship = current_user.relationships.new
  end
  .
  .
  .
end

@relationship = current_user.relationships.find_by(follow_id: @user.id)フォロー解除フォームで参照しているインスタンス変数です。ここでcurrent_userと@userとのrelationshipsを取得することで、フォームにてparams[:relationship][:follow_id]のようにデータを送信することができるようになります

@set_relationship = current_user.relationships.newフォローするフォームで参照しているインスタンス変数です。params[:relationship][:follow_id]と言う形にしてデータを送信したいので、このように空のインスタンス変数を生成してます。

・以上で、ユーザーをフォロー/フォロー解除する機能の実装が完了しました!
 次は、フォローしているユーザー/フォローしてくれているユーザーを一覧で表示する機能を実装していきます!


フォローユーザー/フォロワーの一覧表示機能

1.始めにフォローユーザー/フォロワーの一覧ページへのリンクをビューに作成する

・app/views/users/show.html.slimに、そのユーザーのフォローユーザー/フォロワーの一覧ページへのリンクを追加します。

app/views/users/show.html.slim
h1 ユーザープロフィールページ
p
  = "名前 : "
  = @user.username

#==============フォロー/フォロワー一覧ページへのリンク=================

  = link_to "フォロー: #{@user.followings.count}", followings_user_path(@user.id)
  = "/"
  = link_to "フォロワー: #{@user.followers.count}", followers_user_path(@user.id)

#============================================================

  = render 'relationships/follow_form', user: @user
p= link_to "プロフィール編集", edit_user_path(current_user)

・フォローユーザー/フォロワーへのアクセスは、先ほどUserモデルファイルで定義したfollowings/followersを使います。

@user.followings.countユーザーのフォローしているユーザーの数を表示してます。
followings_user_path(@user.id)URLがusers/:id/followingsとなるパスを指定したいので、左記のように記述します。この形のパスを指定できるように、ルーティングの記述を後で行ってあげます。

・フォロワー一覧ページへのアクセスも、フォローユーザー一覧ページへアクセスするのと同じような方法で記述しています。


2.フォロー/フォロワー一覧ページへのルーティングを追加する。

config/routes.rb
Rails.application.routes.draw do
  .
  .
  .
  resources :users do
    get :followings, on: :member
    get :followers, on: :member
  end
  .
  .
  .
end

・上記のように記述したら$ rails routes | grep followコマンドでルーティングを確認してみます。

$ rails routes | grep follow
  followings_user  GET    /users/:id/followings(.:format)                                                          users#followings
  followers_user   GET    /users/:id/followers(.:format)                                                           users#followers

followings_user_pathはフォローユーザー一覧のページへ、followers_user_pathはフォロワー一覧ページへ飛ぶようになったので、これでOKです。

・followingsアクションも、followersアクションもusersリソースにネストさせたので、usersコントローラで2つのアクションを定義します。


3.usersコントローラにfollowingsアクション、followersアクションを追加する。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def followings
    @user = User.find(params[:id])
    @users = @user.followings.all
  end

  def followers
    @user = User.find(params[:id])
    @users = @user.followers.all
  end
  .
  .
  .

・followingsアクション内では、@userのフォローしているユーザー全てを、followersアクション内では、@userのフォロワー全てを取得しています。


4.フォローユーザー/フォロワー一覧を表示するビューを作成する。

・app/views/usersディレクトリに、followings.html.slimファイルとfollowers.html.slimファイルを作成します。

app/views/users/followings.html.slim
h2 フォローしている方々

- @users.each do |user|
  hr
  = "username: "
  = link_to user.username, user_path(user.id)
app/views/users/followers.html.slim
h2 フォロワーの皆さん

- @users.each do |user|
  hr
  = "username: "
  = link_to user.username, user_path(user.id)

・以上で、フォローユーザー/フォローユーザー一覧ページの作成が完了しました!

参考にさせて頂いた記事

 Railsでフォロー機能を作る方法

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

[Rails]ローカル環境の変更が本番環境に反映されない件

ローカル環境の変更が本番環境に反映されない

①ローカル環境でコードを変更
②localhost:3000で確認(問題なし)
③変更内容をコミットしてGitHubにプッシュ
④自動デプロイを実行
⑤本番環境にて変更を確認するが反映されていない

なぜだろう...?

解決方法

④の自動デプロイを実行する前にUnicornをkillしなければならなかった。

なのでEC2サーバー内の対象アプリのリポジトリに移動し、ps aux | grep unicornを実行。
次にunicorn masterの行の2列目の数字列をコピーし、
EC2サーバー内の対象アプリのリポジトリでkill 'コピーした数字列'を実行。

するとUnicornが停止するので改めて自動デプロイを実行。
そうするとローカル環境の変更が本番環境にも反映されます!

手順をまとめると▼
①ローカル環境でコードを変更
②localhost:3000で確認(問題なし)
③変更内容をコミットしてGitHubにプッシュ
④EC2サーバの対象アプリのリポジトリに移動
⑤ユニコーンをkill
⑥自動デプロイを実行

よかったら参考にしてください!

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

[Rails] ヘルパーメソッドform_withについて[基礎]

学習中に理解が浅かったところなのでアウトプットしながら整理したいと思います。
基礎から丁寧に書いていきます。もし指摘あればお願いいたします。

ヘルパーメソッドとは

そもそもrailsでは、viewでHTMLタグを出現させたりテキストを加工するためのメソッドが予め用意されています。これらをヘルパーメソッドといいます。
form_withはヘルパーメソッドの一種です。

*ヘルパーメソッドの一例
ヘルパーメソッド 使用用途
form_tag 投稿ページなどにおけるフォームの実装
link_to      リンクの実装
simple_format 投稿した文章を自動で見やすく整形する
submit_tag    submit機能をつけたいときに使う。
text_field_tag
などなど、、
自作することも可能らしいです。
【参考記事】
https://qiita.com/Yukaaaah/items/19e524fd0c0e4a3451f1

form_withについて

Rails 5.1というバージョンから推奨されているフォーム実装のためのヘルパーメソッドです。
form_tag/form_forは非推奨となっているらしいです。(この2つのメソッドについては説明を省略します。)

<!-- form_tagを使用した例 -->
<%= form_tag('/posts', method: :post) do %>
  <input type="text" name="content">
  <input type="submit" value="投稿する">
<% end %>
<!--form_withを使用した例-->
<%= form_with model: @post, local: true do |form| %>
  <%= form.text_field :content %>
  <%= form.submit '投稿する' %>
<% end %>

form_withの特徴としては、
①自動でパスを選択してくれて、HTTPメソッドを指定する必要が無いこと
②コントローラーから渡された、ActiveRecordを継承するモデルのインスタンスが利用できること(上記では@postがそれに該当)

またこの場合は、
①新規投稿の場合
②既存の投稿を呼び出した場合
で処理が変わってきます。

①新規投稿の場合

posts_controller.rb
def new
  @post = Post.new
end

投稿ボタンをクリックしたらcreateアクションに送られます。

new.html.erb
<%= form_with model: @post, class: :form, local: true do |form| %>
  <%= form.text_field :title, placeholder: :タイトル, class: :form__title %>
  <%= form.text_area :content, placeholder: :ブログ本文, class: :form__text %>
  <%= form.submit '投稿する', class: :form__btn %>
<% end %>

②既存の投稿を呼び出した場合

posts_controller.rb
def edit
  @post = Post.find(params[:id])
end
edit.html.erb
<%= form_with model: @post, class: :form, local: true do |form| %>
  <%= form.text_field :title, placeholder: :タイトル, class: :form__title %>
  <%= form.text_area :content, placeholder: :ブログ本文, class: :form__text %>
  <%= form.submit '投稿する', class: :form__btn %>
<% end %>

投稿ボタンをクリックしたらupdateアクションに送られます。

まとめ

比較してみるとnew.html.erbとedit.html.erbのフォーム部分は同じです!!!
そのためフォーム部分を部分テンプレート化できて記述量を減らせます。
モデルの@postの中身があるかどうかで自動で判断して送ってくれるのです。

このほかにも
例えばform_with model: [@￰post, @￰comment]のように複数モデルを渡すことができます。
それは別記事で書ければと思います、、

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

rails db:〇〇 まとめ 随時更新

rails db:create

DBを作成

rails db:drop

DBを削除

rails db:migrate

DBにmigrationファイルの実行
自動的に db:schema:dump も実行され、schema.rbも更新される

rails db:seed

テーブルに初期データが追加

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

調べる力 各方法まとめ プロジェクト内とGemで定義されている場合

動機

新しいプロジェクトに参加した時、もうメソッドだらけでどこに定義されているか調べることにかなり苦労しました。早くキャッチアップするためには、「早く調べて理解する力」が非常に重要と感じまとめてみました。

調べ方も一長一短あります。

プロジェクト内で定義されているメソッドの場合

1. git grep -n <メソッド名>

  • エディタの全文検索より、ターミナルで色付きで表示されるので見やすいです
  • -n オプションで行番号がつくので、コマンド + パスクリックでエディタで開きます!
  • デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とすると多少絞られる。

2. ブラウザでGithubの検索窓に入力し、"in this repository"で検索

  • この方法は1.と結果は同じなのですが、マッチした箇所の上下数行もまとめて表示されます。
  • エディタを使いたくない時にいいです
  • デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とダブルクオーテーションをいれると絞られる

3. pry-docsのshow-source機能を使う

  • Railsコンソールを開き、$ <メソッド名> OR show-source <メソッド名>
  • 定義箇所のdefからendまでを表示してくれます!
  • パスも出るので、コマンド + クリックでエディタで表示できます。
  • デメリット:user.method を調べたい時、Userインスタンスを作成してからでないと調べられないので手間。

4. Pryの"step"機能を使う

  • binding.pryで停止した時に、stepを実行すると、そのメソッド内に"入る"ことができます。
  • 処理の流れと変数の定義も見ることができ、深い理解ができます。
  • Gemなど深いところも見れます。
  • デメリット:binding.pryの設置と、処理を走らせ止めることがめんどくさい。

ピカワカ【Rails】Pryについて徹底解説!

Gemなどプロジェクト外で定義されている場合

1. Dashで検索する

  • Dashとは、素のRuby、Gem、JavaScriptなど色んなドキュメントの横断検索ができるツールです。
  • ソースコードに加えて、そのメソッドの説明付きなので理解も早くなります。
  • VSCodeに拡張機能"Dash"をいれることで、コマンド + H でDash検索に飛べます
  • デメリット:たまに調べたいGemがなかったりする

2. bundle show <gem名>

  • Gemのインストールされたディレクトリが表示されるので、VSCodeならコマンド + クリックで別ウィンドウで開くことができます。
  • デメリット:gitやpryは使えないので、エディタの横断検索でメソッドを見つける必要があります。

3. Pryの"step"機能を使う

  • プロジェクト内で定義されているメソッドの場合と同じです。

4. ブラウザでGithubの検索窓に入力し、"in this repository"で検索

  • プロジェクト内で定義されているメソッドの場合と同じです。

その他

1. Rubymineの定義元ジャンプを使う

  • VSCode使用者なので使ったことないです。が、RubyやRails、Gem内の定義ならDashでほぼまかなえる気がします。
  • デメリット:高い、遅い

2. VSCodeの拡張機能 Solargraphを使う

  • Rubyメソッドの上にカーソルを載せるだけで、そのドキュメントや使用例が表示されます。
  • 補完機能もあり
  • デメリット:Dockerを使用する場合は辛い。SolargraphはそのLanguage serverを起動させ続ける必要がある。Solargrapコンテナを立ち上げ、Portの設定をVSCodeにする必要がある。参考:Docker環境でsolargraphを使う方法

最後に

  • 皆様の「調べ方」が知りたいです。
  • 他に良い方法があれば、コメントにて教えてくださいm(_ _)m
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

調べる力 各方法まとめ プロジェクト内とRails/Gem内で定義されている場合

動機

新しいプロジェクトに参加した時、もうメソッドだらけでどこに定義されているか調べることにかなり苦労しました。早くキャッチアップするためには、「早く調べて理解する力」が非常に重要と感じ、先輩エンジニアにも聞きながらまとめてみました。

調べ方も一長一短あります。

プロジェクト内で定義されているメソッドの場合

1. git grep -n <メソッド名>

  • エディタの全文検索より、ターミナルで色付きで表示されるので見やすいです
  • -n オプションで行番号がつくので、コマンド + パスクリックでエディタで開きます!
  • デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とすると多少絞られる。

2. ブラウザでGithubの検索窓に入力し、"in this repository"で検索

  • この方法は1.と結果は同じなのですが、マッチした箇所の上下数行もまとめて表示されます。
  • エディタを使いたくない時にいいです
  • デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とダブルクオーテーションをいれると絞られる

3. pry-docsのshow-source機能を使う

  • Railsコンソールで、$ <メソッド名> OR show-source <メソッド名>
  • 定義箇所のdefからendまでを表示してくれます!
  • パスも出るので、コマンド + クリックでエディタで表示できます。
  • デメリット:user.method を調べたい時、Userインスタンスを作成してからでないと調べられないので手間。

4. Pryの"step"機能を使う

  • binding.pryで停止した時に、stepを実行すると、そのメソッド内に"入る"ことができます。
  • 処理の流れと変数の定義も見ることができ、深い理解ができます。
  • Rails/Gemなど深いところも見れます。
  • デメリット:binding.pryの設置と、処理を走らせ止めることがめんどくさい。

ピカワカ【Rails】Pryについて徹底解説!

Gemなどプロジェクト外で定義されている場合

1. Dashで検索する

  • Dashとは、素のRuby、Rails, Gem、JavaScriptなど色んなドキュメントの横断検索ができるツールです。
  • ソースコードに加えて、そのメソッドの説明付きなので理解も早くなります。
  • VSCodeに拡張機能"Dash"をいれることで、コマンド + H でDash検索に飛べます
  • デメリット:たまに調べたいGemがなかったりする

2. bundle show <gem名>

  • Gemのインストールされたディレクトリが表示されるので、VSCodeならコマンド + クリックで別ウィンドウで開くことができます。
  • デメリット:gitやpryは使えないので、エディタの横断検索でメソッドを見つける必要があります。

3. Pryの"step"機能を使う

  • プロジェクト内で定義されているメソッドの場合と同じです。

4. ブラウザでGithubの検索窓に入力し、"in this repository"で検索

  • プロジェクト内で定義されているメソッドの場合と同じです。

その他

1. Rubymineの定義元ジャンプを使う

  • VSCode使用者なので使ったことないです。が、RubyやRails、Gem内の定義ならDashでほぼまかなえる気がします。
  • デメリット:高い、遅い

2. VSCodeの拡張機能 Solargraphを使う

  • Rubyメソッドの上にカーソルを載せるだけで、そのドキュメントや使用例が表示されます。
  • 補完機能もあり
  • デメリット:Dockerを使用する場合は辛い。SolargraphはそのLanguage serverを起動させ続ける必要がある。Solargrapコンテナを立ち上げ、Portの設定をVSCodeにする必要がある。参考:Docker環境でsolargraphを使う方法

最後に

  • 皆様の「調べ方」が知りたいです。
  • 他に良い方法があれば、コメントにて教えてくださいm(_ _)m
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メソッドの定義場所 調べる方法まとめ プロジェクト内とRails/Gem内で定義されている場合

動機

新しいプロジェクトに参加した時、もうメソッドだらけでどこに定義されているか調べることにかなり苦労しました。早くキャッチアップするためには、「早く調べて理解する力」が非常に重要と感じ、先輩エンジニアにも聞きながらまとめてみました。

調べ方も一長一短あります。

プロジェクト内で定義されているメソッドの場合

1. git grep -n <メソッド名>

  • エディタの全文検索より、ターミナルで色付きで表示されるので見やすいです
  • -n オプションで行番号がつくので、コマンド + パスクリックでエディタで開きます!
  • デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とすると多少絞られる。

2. ブラウザでGithubの検索窓に入力し、"in this repository"で検索

  • この方法は1.と結果は同じなのですが、マッチした箇所の上下数行もまとめて表示されます。
  • エディタを使いたくない時にいいです
  • デメリット:あくまで文字列検索。かなり沢山ヒットしたりする。 "def メソッド名"とダブルクオーテーションをいれると絞られる

3. pry-docsのshow-source機能を使う

  • Railsコンソールで、$ <メソッド名> OR show-source <メソッド名>
  • 定義箇所のdefからendまでを表示してくれます!
  • パスも出るので、コマンド + クリックでエディタで表示できます。
  • デメリット:user.method を調べたい時、Userインスタンスを作成してからでないと調べられないので手間。

4. Pryの"step"機能を使う

  • binding.pryで停止した時に、stepを実行すると、そのメソッド内に"入る"ことができます。
  • 処理の流れと変数の定義も見ることができ、深い理解ができます。
  • Rails/Gemなど深いところも見れます。
  • デメリット:binding.pryの設置と、処理を走らせ止めることがめんどくさい。

ピカワカ【Rails】Pryについて徹底解説!

Gemなどプロジェクト外で定義されている場合

1. Dashで検索する

  • Dashとは、素のRuby、Rails, Gem、JavaScriptなど色んなドキュメントの横断検索ができるツールです。
  • ソースコードに加えて、そのメソッドの説明付きなので理解も早くなります。
  • VSCodeに拡張機能"Dash"をいれることで、コマンド + H でDash検索に飛べます
  • デメリット:たまに調べたいGemがなかったりする

2. bundle show <gem名>

  • Gemのインストールされたディレクトリが表示されるので、VSCodeならコマンド + クリックで別ウィンドウで開くことができます。
  • デメリット:gitやpryは使えないので、エディタの横断検索でメソッドを見つける必要があります。

3. Pryの"step"機能を使う

  • プロジェクト内で定義されているメソッドの場合と同じです。

4. ブラウザでGithubの検索窓に入力し、"in this repository"で検索

  • プロジェクト内で定義されているメソッドの場合と同じです。

その他

1. Rubymineの定義元ジャンプを使う

  • VSCode使用者なので使ったことないです。が、RubyやRails、Gem内の定義ならDashでほぼまかなえる気がします。
  • デメリット:高い、遅い

2. VSCodeの拡張機能 Solargraphを使う

  • Rubyメソッドの上にカーソルを載せるだけで、そのドキュメントや使用例が表示されます。
  • 補完機能もあり
  • デメリット:Dockerを使用する場合は辛い。SolargraphはそのLanguage serverを起動させ続ける必要がある。Solargrapコンテナを立ち上げ、Portの設定をVSCodeにする必要がある。参考:Docker環境でsolargraphを使う方法

最後に

  • 皆様の「調べ方」が知りたいです。
  • 他に良い方法があれば、コメントにて教えてくださいm(_ _)m
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]本番環境(EC2)でエラーログを見る方法

エラーログとは

エラーログとはエラーが発生した場合にその情報を記録している場所のことです。

ローカル環境のエラーログ 確認方法

Railsではエラーが発生した場合、下記に添付している画像のようなエラー画面が発生する。
これはRailsの特徴の一つで、ローカル環境ではエラーログを探さなくても詳細を表示してくれます。

aad9dc14644070bf4e4d9f438a126895.jpeg

本番環境のエラーログ 確認方法

一方、本番環境ではエラーが発生した場合、エラー情報を記録しているエラーログを確認しなければなりません。(マストではないが、エラーログを確認することは重要)

エラーログの確認方法は以下の通り▼

EC2サーバにログイン後、対象アプリのリポジトリに移動。
次に対象のリポジトリ直下のcurrentディレクトリに移動。

[ec2-user@****** [対象のリポジトリ]]$ cd curren

current直下のlogに移動▼

[ec2-user@****** current]$ cd log

logまで移動したらlsコマンドで直下のファイルを検索。すると以下のように表示される▼

[ec2-user@****** log]$ ls
production.log  unicorn.stderr.log  unicorn.stdout.log

production.log ▶︎ 本番環境に関するエラー情報が記録されている。
unicorn.stderr.log ▶︎ unicornの起動時にエラーが発生した場合にエラー情報が記録される。

下記コードを実行すると対処のファイルへ移動し、エラーログを確認できる。

[ec2-user@****** log]$ tailf '対象のログ'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【RAILS】エラー対応記録-herokuデプロイ時「Uglifier::Error: Unexpected token name «of», expected punc «;». To use ES6 syntax,…」

ローカル環境でjavascriptを実装して、herokuの本番環境にデプロイした際に、エラーが出て対応したときの記録を残します。

環境

macOS Catalina 10.15.5
Ruby: 2.5.1
Rails: 5.2.4.1

現象

terminal
$ git push memo-space master
Enumerating objects: 139, done.
Counting objects: 100% (139/139), done.
Delta compression using up to 16 threads
Compressing objects: 100% (107/107), done.
Writing objects: 100% (113/113), 15.19 KiB | 3.80 MiB/s, done.
Total 113 (delta 71), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:

...

remote:        Tasks: TOP => assets:precompile
remote:        (See full trace by running task with --trace)
remote: 
remote:  !
remote:  !     Precompiling assets failed.
remote:  !
remote:  !     Push rejected, failed to compile Ruby app.
remote: 
remote:  !     Push failed
remote: Verifying deploy...
remote: 
remote: !   Push rejected to memo-space.
remote: 
To https://git.heroku.com/memo-space.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/memo-space.git'

heroku Build Log

       Bundled gems are installed into `./vendor/bundle`
       Bundle completed (3.47s)
       Cleaning up the bundler cache.
-----> Installing node-v10.15.3-linux-x64
-----> Detecting rake tasks
-----> Preparing app for Rails asset pipeline
       Running: rake assets:precompile
       rake aborted!
       Uglifier::Error: Unexpected token name «of», expected punc «;». To use ES6 syntax, harmony mode must be enabled with Uglifier.new(:harmony => true).
       --
        51469     var month = d.getMonth() + 1;
        51470     var day   = d.getDate();
        51471     var hour  = ( d.getHours()   < 10 ) ? '0' + d.getHours()   : d.getHours();
        51472     var min   = ( d.getMinutes() < 10 ) ? '0' + d.getMinutes() : d.getMinutes();
        51473     var moment_end = year+"-"+month+"-"+day+" "+hour+":"+min;
        51474     var end_time = moment(moment_end).add(-9, 'hour').format("YYYY-MM-DD HH:mm");
        51475     var cookies = document.cookie;
        51476     var cookiesArray = cookies.split(';');
           =>     for (var c of cookiesArray){
        51478       var cArray = c.split('=');
        51479       if (cArray[0] == " user_id"){
        51480         var user_id = cArray[1]
        51481       }
        51482     }
        51483     var data = {
        51484       event: {
        51485         title: title,
       ==

Running: rake assets:precompile からの
rake aborted! からの
Uglifier::Error: Unexpected token name «of», expected punc «;». To use ES6 syntax, harmony mode must be enabled with Uglifier.new(:harmony => true). と出ています。

メッセージを調べると、デプロイしたjavascriptの仕様(ES6)にrailsが対応してコンパイルするためには、harmony modeを有効にしてね。ということらしいです。

参考情報を参考に以下のように対応しました。

変更前

config/environments/production.rb
# Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier

変更後

config/environments/production.rb
# Compress JavaScripts and CSS.
# config.assets.js_compressor = :uglifier
config.assets.js_compressor = Uglifier.new(harmony: true)

再度デプロイ

terminal
$ git push memo-space master
Enumerating objects: 147, done.
Counting objects: 100% (147/147), done.
Delta compression using up to 16 threads
Compressing objects: 100% (113/113), done.
Writing objects: 100% (119/119), 16.29 KiB | 3.26 MiB/s, done.
Total 119 (delta 74), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Ruby app detected

...

remote: -----> Preparing app for Rails asset pipeline
remote:        Running: rake assets:precompile
remote:        I, [2020-06-27T03:30:44.728297 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/application-00e4778fee8872d97056857c240d7fb1daece9a066e9bf5801bd6075927cff8e.js
remote:        I, [2020-06-27T03:30:44.728919 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/application-00e4778fee8872d97056857c240d7fb1daece9a066e9bf5801bd6075927cff8e.js.gz
remote:        I, [2020-06-27T03:30:51.980973 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/application-f0b2b81e6c5ce3009be95d8ca8a958201d38ff5d8f2458e2102f7a093d200f57.css
remote:        I, [2020-06-27T03:30:51.981882 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/application-f0b2b81e6c5ce3009be95d8ca8a958201d38ff5d8f2458e2102f7a093d200f57.css.gz
remote:        I, [2020-06-27T03:30:51.991623 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/jquery-ui/ui-icons_444444_256x240-31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d.png
remote:        I, [2020-06-27T03:30:51.992765 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/jquery-ui/ui-icons_555555_256x240-32175261daee76c82bb0edf0eea16a56421866fbc31e94f3c1d570aa114502f5.png
remote:        I, [2020-06-27T03:30:51.997797 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/jquery-ui/ui-icons_ffffff_256x240-350df1b7131037de20e83c5c0f3a41a770d2ac48b5762ea772b3f4a8a7b9d47a.png
remote:        I, [2020-06-27T03:30:51.998674 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/jquery-ui/ui-icons_777620_256x240-0b020fc6e696d88d296e7bb1f61f1eb2ad827848e2c7382a4c3e0999e702dd9b.png
remote:        I, [2020-06-27T03:30:51.999735 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/jquery-ui/ui-icons_cc0000_256x240-40985a64b4d5dd213fba27fcd862a1bd1b337a97674f6ff0b9ec20abcee4bc69.png
remote:        I, [2020-06-27T03:30:52.001638 #364]  INFO -- : Writing /tmp/build_972372ee6575db82336ea12d69910b2d/public/assets/jquery-ui/ui-icons_777777_256x240-faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f.png
remote:        Asset precompilation completed (22.03s)

...


remote: Verifying deploy... done.
To https://git.heroku.com/memo-space.git
   87fb9be..e4d0ecb  master -> master
terminal
$ heroku run rails db:migrate

完了〜

参考

https://qiita.com/terufumi1122/items/27bf288414569e13e050
https://github.com/lautis/uglifier/issues/127

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

RailsとDockerでよく使うコマンドまとめ

docker-compose build #コンテナの作成
docker-compose up #コンテナ立ち上げ
docker-compose down #コンテナをダウン ctrl+cでも止められるがserver.pidでエラーになる
docker-compose stop #コンテナをストップ
docker-compose up --build #docker関連のファイルの変更を反映した上に、サーバーを再起動
docker images #イメージ一覧
docker ps #動いているコンテナ一覧
docker ps -a #動いていないコンテナも含めた一覧
docker rm `docker ps -a -q` #コンテナの一括削除
docker rmi `docker images -a -q` #イメージの一括削除
mysql -u root -p -h localhost -P 3306 --protocol=tcp #DockerからMYSQLへ

docker-compose run web bundle install #bundle install
docker-compose run web rails db:create #db作成
docker-compose run web rails db:migrate #マイグレート

以上、RailsとDockerでよく使うコマンドたちでした!
参考になれば幸いです。
間違えなどありましたらご指摘お願いします。

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

【Rails】ブラウザのページタイトルをページごとに変更する方法

目標

スクリーンショット 2020-06-27 10.43.42.png

開発環境

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

前提

下記実装済み。

Slim導入

実装

1.application_helper.rbを編集

application_helper.rb
module ApplicationHelper
  # 追記
  def full_title(page_title = '')
    base_title = "Bookers"
    if page_title.empty?
      base_title
    else
      "#{ page_title } | #{ base_title }"
    end
  end
end

【解説】

◎ ベースとなるタイトル(アプリ名等)を設定し、変数へ代入する。

base_title = "Bookers"

◎ 引数で受け取った各ページのタイトルが空の場合はベースタイトルのみを表示する。

if page_title.empty?
  base_title

◎ 引数で受け取った各ページのタイトルが存在する場合は両タイトルを表示する。

else
  "#{ page_title } | #{ base_title }"

page_titleが「本一覧」だとすると、本一覧 | Bookersと表示される。

2.ビューを編集

application.html.slimを編集する。

application.html.slim
/ 変更前
title
  | Bookers

/ 変更後
title
  = full_title(yield(:title))

② 各ページのビューを編集する。

例として、books/index.html.slimのタイトルを設定します。

books/index.html.slim
/ 追記
= provide(:title, '本一覧')
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】エラーメッセージを個別に表示する方法

目標

ezgif.com-video-to-gif.gif

開発環境

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

前提

下記実装済み。

Slim導入
投稿機能実装

実装

1.application.rbを編集

application.rb
module Bookers2Debug
  class Application < Rails::Application
    config.load_defaults 5.2

    # 追記
    config.action_view.field_error_proc = Proc.new do |html_tag, instance|
      if instance.kind_of?(ActionView::Helpers::Tags::Label)
        html_tag.html_safe
      else
        class_name = instance.object.class.name.underscore
        method_name = instance.instance_variable_get(:@method_name)
        "<div class=\"has-error\">#{html_tag}
          <span class=\"help-block\">
            #{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")}
            #{instance.error_message.first}
          </span>
        </div>".html_safe
      end
    end
  end
end

【解説】

① エラーが出ていない場合はHTMLをそのまま表示する。

if instance.kind_of?(ActionView::Helpers::Tags::Label)
  html_tag.html_safe

② エラーが出ている場合はエラーメッセージをフォームの下に表示する。

else
  class_name = instance.object.class.name.underscore
  method_name = instance.instance_variable_get(:@method_name)
  "<div class=\"has-error\">#{html_tag}
    <span class=\"help-block\">
      #{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")}
      #{instance.error_message.first}
    </span>
  </div>".html_safe

◎ インスタンスのクラス名を変数へ代入

class_name = instance.object.class.name.underscore

◎ インスタンスのメソッド名を変数へ代入

method_name = instance.instance_variable_get(:@method_name)

◎ エラーメッセージ部分のHTMLを作成する。

"<div class=\"has-error\">#{html_tag}
  <span class=\"help-block\">
    #{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")}
    #{instance.error_message.first}
  </span>
</div>".html_safe

「タイトルを入力してください」と表示する場合、
#{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")}が、
「タイトル」にあたる部分で、
#{instance.error_message.first}が、
「を入力してください」にあたる部分になる。

2.エラーメッセージを日本語化

① Gemを導入

Gemfile
# 追記
gem 'rails-i18n'
ターミナル
$ bundle

application.rbを編集

application.rb
module Bookers2Debug
  class Application < Rails::Application
    config.load_defaults 5.2
    config.i18n.default_locale = :ja # 追記

    config.action_view.field_error_proc = Proc.new do |html_tag, instance|
      if instance.kind_of?(ActionView::Helpers::Tags::Label)
        html_tag.html_safe
      else
        class_name = instance.object.class.name.underscore
        method_name = instance.instance_variable_get(:@method_name)
        "<div class=\"has-error\">#{html_tag}
          <span class=\"help-block\">
            #{I18n.t("activerecord.attributes.#{class_name}.#{method_name}")}
            #{instance.error_message.first}
          </span>
        </div>".html_safe
      end
    end
  end
end

ja.ymlを作成し、編集

ターミナル
$ touch config/locales/ja.yml
ja.yml
ja:
  activerecord:
    attributes:
      book:
        title: タイトル
        body: 本文
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsチュートリアル 第1章 ゼロからデプロイまで【やってみた】

Railsチュートリアル第1章をやってみて終えたので、やったことや失敗したことを振り返ります。細かく見ていくので、チュートリアルしながらみてもらえれば嬉しいです。

※こちらの記事ではRailsチュートリアル第4版を使用しています。
現在はRailsチュートリアル第6版が出ています。
下記リリース時のnote
https://note.com/yasslab/n/n1543187c3ed6?magazine_key=md778735d3f77

ちなみに細かなことですが、読む字体を明朝体かゴシック体に変更することができます。自分はゴシック体にするととても読みやすくなったので、お好みに合わせて変更すると良いかと思います。

また2章で新しいアプリを作るので、この章でデプロイできなくても大丈夫です。

1.1.1 前提知識

RailsチュートリアルでProgeteの関連講座を紹介してますが、
これは本当にやった方がいいと感じます。
第1章でコマンドラインとGitコースが紹介されていますが、
「後ででもいけるだろ」と思って結局コマンドのミスで、アプリが作り直しになったりと、大変なことになりました!
始める前にこちらのコースをすることをおすすめします。

1.2.1 開発環境

クラウドIDEである、Cloud9をインストールする

1.2.2 Railsをインストールする

    $ printf "install: --no-document \nupdate:  --no-document\n" >> ~/.gemrc
    $ gem install rails -v 5.1.6     # -vでバージョンを指定 
    $ cd ~/environment    # 'environment' ディレクトリに移動する
    $ rails _5.1.6_ new hello_app

1.3.1 Bundler

Gemfile内にあるデフォルトの内容をバージョンを指定して記述、インストールします。

$ cd hello_app/
$ bundle install

上手くいかない時はbundle updateを実行

1.3.2 rails sever

$ cd ~/environment/hello_app/    #hello_appディレクトリへ移動
$ rails server    #サーバーを起動

rails serverを起動した後は、このターミナル内はリアルタイムで更新されていくので、注意すること。
画面をプレビューするとRailsのぺージが!!Yay!

1.3.3 Model-View-Controller

モデル・ビュー・コントローラーがそれぞれ関わっているというお話。

model: アプリケーションデータ、ビジネスルール、ロジック、関数
view: グラフや図などの任意の情報表現
controller: 入力を受け取りmodelとviewへの命令に変換する
--Wikipediaより引用

ここで一つの疑問が。
RailsはこのMVCアーキテクチャパターンを採用しているということ。「このパターンを採用している」ということは他のパターンもあるということ...?調べてみました。

・フォームとコントロール
・プレゼンテーション・モデル
・アプリケーション・モデル
・MVVM
・モデル・ビュー・プレゼンタ(MVP)

参考記事 https://www.atmarkit.co.jp/fdotnet/chushin/greatblogentry_10/greatblogentry_10_01.html

内容は上手く理解できなかったけど、MVC以外ももちろんあるよ!ということが分かりました。
構造からもう自由なんだなと思うとプログラミングはすごい
この詳しい内容もまた勉強していきます。

1.3.4 Hello,wold!

ビューも通さずHello,world!を表示させるため、以下のようなコードを記述しています。

titleapp/controllers/application_controller.rb
 def hello
    render html: "hello, world!"
  end

renderメソッドはrender("フォルダ名"/"ファイル名")のように使うと思っていましたが、こんな使い方もできるんですね

Railsガイド 2.2 renderを使用する
Railsガイドによると最早何でも出力できます、みたいな勢い。

ルーティングを設定してブラウザで表示させます。

1.4.1インストールとセットアップ

ここからGitでのバージョン管理ができるようにしていきます。

$ git config --global user.name "Your Name"
$ git config --global user.email your.email@example.com

ここでの名前とメールアドレスは今後一般に公開されるので注意。

リポジトリを作成するため、以下の内容をターミナル内で記述する

$ git init    #Gitの初期化
$ git add -A    #現在のディレクトリにあるすべてのファイルを追加
#ファイル追加後ステージングされ、コミット待ちになる
$ git status    #現在のステージングの確認
$ git commit -m "Initialize repository"    #リポジトリにコメント付きで反映させる
$ git log    #コミットメッセージの履歴を見る

いきなりリポジトリとは何だろう?という感じでしたが、こちらを読んだらとても分かりやすかったです。
【イラストで覚える】初心者のためのGitとGitHub用語集

1.4.3 Gitbucket

Gitbucketにソースコードをアップデートします。
他の開発者と共同作業できるのが大きなメリットです。

手順の中の、「公開鍵・暗号鍵」ってなんだろう?
とりあえず言われるがまま作ってみたけどよくわかっていないので、調べてみました。

インターネットを経由してデータのやりとりをする上で、情報が盗まれないようにするために暗号化が必要です。
そのために生まれたのが公開鍵暗号方式です。
この鍵はお互いが対応しています。
・公開鍵で暗号化したデータは秘密鍵(暗号鍵)でしかもとに戻せない
・秘密鍵(暗号鍵)で暗号化したデータは公開鍵でしかもとに戻せない

公開鍵は誰でも見れるようになっています。
データを送信する側は公開鍵で情報を取得、暗号化して送信します。
受信側は誰にも見せていない秘密鍵(暗号鍵)で暗号化された情報を元に戻し、見ることができます。
これで第三者に情報を見られることはありません。

このような方式をとっているため、情報のやりとりのために公開鍵を入れることが必要です。

参考記事
https://railstutorial.jp/chapters/beginning?version=5.1#sec-bundler

1.4.4 ブランチ、編集、コミット、マージ

$ git checkout -b modify-README    #一時的に使うトピックブランチを作成
$ git branch    #すべてのローカルブランチを確認
$ git commit -a -m "Improve the README file"    #すべてのファイルの変更を一括でコミットする
$ git checkout master    #masterブランチへ移動
$ git merge modify-README    #masterブランチにトピックブランチmodify-READMEをマージ
$ git push    #プッシュする

1.5.1 Herokuのセットアップ

HerokuではPostgreSQLデータベースを使う、とありますが、
PostgreSQLってなんだろう...

PostgreSQL は、オープンソースのリレーショナル・データベースです。商用データベ>ースに匹敵する本格的な機能と、オープンソースならではの利用の柔軟度が魅力です。
https://lets.postgresql.jp/map/intro

リレーショナルデータベースは、テーブル型で、関連付けられたデータの集合ということのようです。まだまだ難しい
https://aws.amazon.com/jp/relational-database/

SQLiteを加え、インストールします。

また、Herokuをインストールしてデプロイします。

$ heroku --version    #Herokuがインストールされているかとバージョンの確認
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)    #HerokuをクラウドIDEにインストール
$ heroku login --interactive
$ heroku keys:add
$ heroku create    #Herokuに新しいアプリケーションを作る
$ git push heroku master    #Herokuにデプロイ

ここで1章は終わりです。
ログを載せたかったのですが、デプロイが上手くいきませんでした。
後から考えると、ブランチを作り間違えたのと、Herokuは heroku createで生成されたアドレスからアクセスする必要があるということをいまいち理解していなかったためだと思います。

長くなってしまいましたが、ここまで読んでいただきありがとうございます。
間違いなどありましたら、教えていただけると嬉しいです。

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

slimでspanを挟みたくなったときの書き方

ブラウザにはこのように表示していきたいと思います。
「間」という文字をspanで間に挿入し、全て一行で表示させます。
左の文字(間)右の文字

コードは、こちらになります。

html.slim
p
  | 左のもじ(
  span
    || )右の文字

spanタグを書くときは、改行します。
そして、表示させる文字は、さらに改行し、インデントの後に「|」を書いて、その後に文字を書きます。
span以降の文字は、改行し、pタグの括りになるインデントで「|」を書き、その後に書きます。

参考記事

https://stackoverflow.com/questions/13514660/nesting-a-text-inside-a-tag-in-slim

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

Herokuで一定期間経つとアップロードした画像が消えてしまう理由とその対処法

今回起きた問題

初めて制作したアプリをデプロイしたはいいものの、画像を投稿して一定期間経つと「あれ....画像が消えてる!?」と画像が全て消えてデータが投稿文章だけになっていました。

原因

一定時間経つとHeroku上のリポジトリ (Dyno) が最後のコミットの状態にリセットされるため、画像が保存できないとのこと。

解決法

自分はクラウドストレージを使用して解決しました。今回はfogとawsのS3と画像アップロードにCarrierWaveを使用しています。

1.fog

fogとはRubyでクラウドサービスを使いやすくするためのGemです。
まずはfogをインストールします。gemfileの以下の記述をしたら bundle installをします

gem 'fog'

開発環境ではローカルファイルに画像保存で問題ありませんが、本番環境ではクラウドストレージサービス(fog)に保存するように条件分岐させます。

app/uploaders/〇〇〇〇_uploader.rb
if Rails.env.production?
  storage :fog
else
  storage :file
end

2.S3

S3はAWSのサービスの一つです。

ここでの流れは
①AWSのアカウントを作成する
②AWS Identity and Access Management (IAM)でユーザーとグループ(AmazonS3FullAccess)作成(アクセスキーとシークレットアクセスキーは後から使うのでcsvはダウンロードしておくと良い)
③作成したユーザーでログインし、サービス「S3」にてバケットを作成する
といったものです
参考記事
【AWS】【S3】作成手順 & アップロード手順 & アクセス権限設定手順

3.CarrierWaveの設定

config/initializers/carrierwave.rb
if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_credentials = {
      provider: 'AWS',
      aws_access_key_id: ENV['ACCESS_KEY_ID'],
      aws_secret_access_key: ENV['SECRET_ACCESS_KEY'],
      region: 'ap-northeast-1' # S3バケット作成時に指定したリージョン。
    }
    config.fog_directory  = 'food-pictures-bucket' # 作成したS3バケット名
  end
end

上記についてファイルに直接鍵の名前を書いてしまうと悪用されることがあるので、環境変数で設定しおきましょう。

4.Herokuの環境変数設定

$ heroku config:set ACCESS_KEY_ID=アクセスキー
$ heroku config:set SECRET_ACCESS_KEY=シークレットアクセスキー

ここまできたらターミナルで git push heroku master でデプロイしまししょう

終わりに

herokuでアップロードするだけでも他のクラウドサービスのことや秘密鍵のことについての知識が必要で大変でしたがこの方法でうまくいいきました。
この記事が少しでも参考になれば幸いです。

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

csvファイルのアップロードをRSpecでテストしたい

はじめに

新卒エンジニアの@yhorikawaです
csvをアップロードする機能を作っていたのですが、テストの方法がわからず苦戦したので
今後ファイルアップロード機能をテストしたいときのために備忘録として残します。

controller

csv_upload.rb
def csv_upload
  # csv読み込み部分
  csv = CSV.read(params[:csv])
  # このあと保存処理
end

このような形のcontrollerにparams[:csv]といった形でcsvを渡します。

テストコード

csv_upload_request_spec.rb
describe 'post #csv_upload' do
  let(:params) { { csv: fixture_file_upload('csv_file_path', 'text/csv') } }
  subject { post 'csv_upload', params }

  it 'upload_csv' do
    expect(subject).to be_success
  end
end

csvファイルをspec/fixtures/などに保存して
fixture_file_upload('csv_file_path', 'text/csv')にcsvファイルの相対パスを書くことで
ファイルをパラメーターとして渡すことができます

おわりに

fixture_file_upload を使うことで比較的簡単に実装することができました。
csvに限らずファイルのアップロード機能をテストしたいことはあると思うので記事を書きました。

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

【Redis】KEYSによるKey取得の危険性とSCANによる安全な対処

RedisにおけるKEYSの危険性、及びSCANによる対処をできる限り簡潔に紹介します。

Redis , KEYSとは

※分かる方は飛ばしてOKです

Redisは、KVS型(Key Value Store)データベースの一つであり、オープンソース(BSDライセンス)のインメモリデータ構造ストア(ストレージではなくメインメモリで処理するもの)です。僕の知る限りWebによくあるキャッシュを保存するのに使われることが多いです。

構造は極めて単純で、ひとつのKeyに対して一対のValueがあるというものです。

スクリーンショット 2020-06-26 19.06.13.png

キャッシュを扱う場合、Keyには「ページの部品のURL」、Valueには「CSSやJavaScriptなど」を保存します。

KEYSは、特定のパターンにマッチするKeyを検索する手法です(Redis(KEYS))。全てのKeyとパターンを比較するため、時間計算量はO(N)となります(Nはデータベース内のKeyの数)。つまり計算量は、保存されているKeyの数に直接影響します。

以下、KEYSコマンドの例。

redis> KEYS *name*
1) "lastname"
2) "firstname"

redis> KEYS a??
1) "age"

redis> KEYS *
1) "age"
2) "lastname"
3) "firstname"

KEYSによる全検索の危険

While the time complexity for this operation is O(N), the constant times are fairly low. For example, Redis running on an entry level laptop can scan a 1 million key database in 40 milliseconds.

公式リファレンスRedis(KEYS)にあるように、単純な参照の処理速度は高速のようです。

問題は、保存されたKeyの数が「数百万、数千万」という膨大な数の場合に起こります。その悲劇の順序は以下の通りです。

スクリーンショット 2020-06-26 22.28.40.png

1. KEYSのレスポンスが長くてRedisが何も返せない

Redisサーバは基本、単一の処理しか行えないシングルスレッドです。ゆえに、「数百万、数千万」という膨大な件数を参照しに行く場合、その処理が終わるまでレスポンスを返せない状態になります。

2. 他のRedisへのGETリクエストなどが詰まる

Redisが「数百万、数千万」という件数の参照を繰り返してる間にも、次々とRedisサーバに対するリクエストは増え続けます。ちなみに、もしRedisがマルチスレッド(シングルスレッドの逆で並列処理が可能)であれば、一つ重たいリクエストがあったとしても、軽い処理は次々とレスポンスされていくので最低限の動作は可能になります(後で詳しく紹介します)。

3. Redisのレスポンスを期待しているApp側(Railsなど)が詰まる

Redisはレスポンスを長時間返せないので、当然アプリケーション側は何もできない時間が増え、詰まります。

4. ユーザへリクエストを返せず、最悪の場合サーバダウン

レスポンスタイムが増えると、サーバへの負荷も増えます。結果、ユーザのリクエストに対し多くの時間を費やすことになり、最悪の場合高負荷によりサーバがダウンしてしまう危険性が高まるのです。

ちなみにこのような危険性が考慮されているため、公式リファレンスでも非推奨とされています。

Warning: consider KEYS as a command that should only be used in production environments with extreme care. It may ruin performance when it is executed against large databases. This command is intended for debugging and special operations, such as changing your keyspace layout. Don't use KEYS in your regular application code. If you're looking for a way to find keys in a subset of your keyspace, consider using SCAN or sets.

SCANでカーソルベースで参照しに行く

KEYSでのKey取得の危険性がわかったところで、解決策を考えます。

先ほど軽く触れたように、マルチスレッドであれば一つの重いリクエストがあっても詰まることはありません。しかしそれは現実的に不可能なので、別のアプローチを考えます。それが、公式でも推奨されているSCANという方法です。

SCANは、KEYSと同じく、パターンマッチするKeyを検索する手法です。SCANの特徴は、参照をカーソルベースで行うという点です。これにより、参照を一括で行うのではなく、分割して参照することが可能になります。ここで言うカーソルとは「次参照する位置を示すもの」で、次のカーソルがない(参照が終わっている)場合SCANは"0"を返します。詳しくはRedis(SCAN)

SCANコマンドの例。

redis> scan 0
1) "17"
2)  1) "key:12"
    2) "key:8"
    3) "key:4"
    4) "key:14"
    5) "key:16"
    6) "key:17"
    7) "key:15"
    8) "key:10"
    9) "key:3"
   10) "key:7"
   11) "key:1"

redis> scan 17
1) "0"
2) 1) "key:5"
   2) "key:18"
   3) "key:0"
   4) "key:2"
   5) "key:19"
   6) "key:13"
   7) "key:6"
   8) "key:9"
   9) "key:11"

プログラムで実装する場合は、カーソルが"0"になるまで(参照が終わるまで)SCANでリクエストを繰り返すようにします。ちなみに、2020/6/28(日)までに、SCANによるKey取得をRailsで実装する方法」を紹介するので、できればフォローしてもらえるとありがたいです(投稿し次第リンクを追記します)。

終わり

Redisそのもののデータ構造は単純でも、内部的な処理を調べていくと危険性も多くあるので非常に興味深いですね。

質問や編集リクエストがあればコメントにお願いします。

Greeting late

長期インターンを始めて2ヶ月目の@TsuboyaTaikiです。

現在Railsアプリの業務でRedisサーバを用いたキャッシュ周りの修正issueを担当しています。その過程でKEYSSCANといったバリュー検索について知ったことが多くあったので書き残しておきます。

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