20210220のRubyに関する記事は20件です。

Railsで開発中にデータベースのレコードを削除したい時は?

目的

開発中に作成したデータベース内のレコードをすべて削除したい。
例)ユーザーテーブルの「test_user」など適当に作ったユーザーデータ

方法

ターミナルで「rails db:reset」コマンドを実行。
これでデータベースの中身が空っぽになる。

terashimatakaya@MacBook-Air furima-34501 % rails db:reset
Dropped database 'furima_34501_development'
Dropped database 'furima_34501_test'
Created database 'furima_34501_development'
Created database 'furima_34501_test'

Railsガイドv6.1 「4.3データベースをリセットする」によるとこのコマンドで行ったことは、「rails db:drop db:setup」と同等とのこと。
注意点としては「rails db:migrate」の意味は含まれていない点。

解説

「rails db:drop」コマンド

データベースを削除するコマンド。usersテーブルやらが全て削除される。
「rails db:create」する前の状態に戻ると思えばOK。

「rails db:setup」コマンド

データベースの作成、スキーマの読み込み、シードデータを用いてデータベースの初期化を実行。
実際にRailsがやってる処理の流れは、
①既存のdb/schema.rbを読み込む
②①の情報を頼りにデータベースを再作成
③再作成のタイミングでdb/seeds.rbに開発用のサンプルデータがあればそれもこのタイミングで再度流し込む

補足

なぜ「db/schema.rb」を参照するのか?

簡単に説明すると、古いマイグレーションファイルたちもイチから順に実行するとエラーが起きやすくなるから。
それよりも常に最新の状態に保たれているだろうschema.rbの情報をもとにしたほうがエラーが起こらず再度データベースを作成できるから、といったかんじだろうか。
詳細は以下のリンクで確認してください。

Railsガイドv6.1 「6.1 スキーマファイルの意味について」

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

「omniauth」とDevise「recoverableモジュール」を同時に使う時の挙動分岐

タイトルがいまいち意味不明ですが、下記のやりたいことを実装する上で調べたことが非常に勉強になったので記録しておきます。

前提条件

・認証にdeviseを使用。googleなどの外部サービスのログインも実装している

やりたいこと

①通常のemail、passwordでログインした場合、ユーザー情報を変更する(edit_user_registration_path)時にパスワード変更フォームを表示。情報変更の際に現在のパスワードの入力を要求するように設定。

②外部サービスログインを使った場合は(edit_user_registration_path)でパスワード変更フォーム、現在のパスワード入力欄を非表示。情報変更の際に現在のパスワード入力も不要な設定にする。

こういった挙動を実装したい。

実装

①ログイン認証の方式によって、Viewの表示を変更する。

app/views/devise/registrations/edit.html.erb

    <%if current_user.uid == nil %>  
      <div class="field">
        <%= f.label :password %> <br />
        <%= f.password_field :password, autocomplete: "new-password" , class: "form-control form-control-comment" %>
        <% if @minimum_password_length %>
          <br />
          <em><%= @minimum_password_length %> characters minimum</em>
        <% end %>
      </div>
        <p class="comment">(変更したくない場合は空白のままにしてください)</p>

      <div class="field">
        <%= f.label :current_password %> <br />
        <%= f.password_field :current_password, autocomplete: "current-password", class: "form-control form-control-comment" %>
      </div>
        <p class="comment">(変更するには現在のパスワード入力が必要です)</p> 
    <% end %>

通常のメールアドレスとパスワードのログインの場合はプロバイダから返ってくる、uidはnilになっている。

それをif文でViewの表示を分岐させた。

②ユーザー情報を更新する時にパスワード要否を分岐させる

deviseの初期設定ではユーザー情報を更新する時は現在のパスワード入力を求められる。

app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
.
.
.

 protected
  def update_resource(resource, params)
    if current_user.uid == nil
      resource.update_with_password(params)
    else
      resource.update_without_password(params)
    end
  end
end

どういうことだろう?

rails gで自作したUsers::RegistrationsControllerDevise::RegistrationsControllerを継承している。

情報を更新する時の仕組みは
Devise::RegistrationsControllerのupdateメソッドを読んでいる。
色々辿っていくと、

  def update_resource(resource, params)
    resource.update_with_password(params)
  end

このupdate_with_passwordメソッドにつながる。

require 'devise/strategies/database_authenticatable'

module Devise
  module Models
.
.
.

      def update_with_password(params, *options)

      ...

      current_password = params.delete(:current_password)

        if params[:password].blank?
          params.delete(:password)
          params.delete(:password_confirmation) if params[:password_confirmation].blank?
        end

        result = if valid_password?(current_password)
          update(params, *options)
        else
          assign_attributes(params, *options)
          valid?
          errors.add(:current_password, current_password.blank? ? :blank : :invalid)
          false
        end


.
.
.
  end
end

このメソッドは現在のパスワードが入力されていない、もしくは間違っている場合は情報を更新しないようになっている。

このupdate_with_passwordと同じところにupdate_without_passwordメソッドもある。
これこそがパスワードなしで情報を変更できるメソッド。

仕組みがわかればUsers::RegistrationsControllerupdate_resourceメソッドを上書きする。
やりたいことはViewと同じであり、現在のユーザーがプロバイダから返ってくるuidを保持しているかnilかで発火させるメソッドを分岐させた。

学び

ググれば色んな方が情報発信してくれているが、一次情報を見る癖もつけないといけない

参考

https://github.com/heartcombo/devise/tree/769506e96cd45b36a311eeca293ce0228c58e5f3

https://wbtaro.hatenablog.com/entry/2020/03/20/231246

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

Rubyインストールに伴うMSYS2インストール時のエラーについて

環境

  • Windows 10
  • Ruby 3.0.0

どこで引っかかったか

Rubyをインストールする

RubyInstallerを使ってインストールした
ここは特に問題なし。

MSYS2をインストールする

ここで以下のようなエラーで出てうまくインストールできていないような感じ

エラー: mingw32: "David Macek <david.macek.0@gmail.com>" の署名は信頼されていません
エラー: mingw64: "David Macek <david.macek.0@gmail.com>" の署名は信頼されていません

詳細

C:\Windows\System32>ridk install
 _____       _           _____           _        _ _         ___
|  __ \     | |         |_   _|         | |      | | |       |__ \
| |__) |   _| |__  _   _  | |  _ __  ___| |_ __ _| | | ___ _ __ ) |
|  _  / | | | '_ \| | | | | | | '_ \/ __| __/ _` | | |/ _ \ '__/ /
| | \ \ |_| | |_) | |_| |_| |_| | | \__ \ || (_| | | |  __/ | / /_
|_|  \_\__,_|_.__/ \__, |_____|_| |_|___/\__\__,_|_|_|\___|_||____|
                    __/ |           _
                   |___/          _|_ _  __   | | o __  _| _     _
                                   | (_) |    |^| | | |(_|(_)\^/_>

   1 - MSYS2 base installation
   2 - MSYS2 system update (optional)
   3 - MSYS2 and MINGW development toolchain

Which components shall be installed? If unsure press ENTER [1,3] 3

> sh -lc true
MSYS2 seems to be properly installed
Install MSYS2 and MINGW development toolchain ...
> pacman -S --needed --noconfirm auto略ds-git
エラー: mingw32: "David Macek <david.macek.0@gmail.com>" の署名は信頼されていません
エラー: mingw64: "David Macek <david.macek.0@gmail.com>" の署名は信頼されていません
エラー: msys: "David Macek <david.macek.0@gmail.com>" の署名は信頼されていません
エラー: データベース 'mingw32' は無効です (無効または破損したデータベース (PGP 鍵))
エラー: データベース 'mingw64' は無効です (無効または破損したデータベース (PGP 鍵))
エラー: データベース 'msys' は無効です (無効または破損したデータベース (PGP 鍵))
Install MSYS2 and MINGW development toolchain failed
Installation failed: pacman failed

   1 - MSYS2 base installation
   2 - MSYS2 system update (optional)
   3 - MSYS2 and MINGW development toolchain

Which components shall be installed? If unsure press ENTER []

どうやって解決したか

  • MSYS2を更新
  • 再度ridk installを実施

という感じで進めました。

Rubyをインストールしたフォルダ内にあるMSYS2.exeを実行する

C:\Ruby30-x64\msys64\msys2.exe

表示されたコマンドラインでMSYS2を更新

以下issueを参考に、↓のコマンドを実行
https://github.com/msys2/MINGW-packages/issues/240

pacman-key --init
pacman-key --populate msys2
pacman-key --refresh-keys

この後↓を複数回実施する

pacman -Syuu

再度ridk installを実施したら、通りました。
やったぜ

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

【Ruby】rbenvを利用し、Macターミナル上にRuby3.0.0をインストールしました

はじめに

『プロを目指す人のためのRuby入門』を活用しRuby学習を深めるにあたり、
MacOSのターミナル上にRubyの最新版をインストールしました。
本記事はその備忘録です。

    前提条件
  • MacOS(M1チップ搭載モデル)
  • Homebrewインストール済み
  • rbenvをインストールし、Ruby3.0.0をインストールする予定

インストールまでの流れ

  1. 現在ターミナル上で使っているRubyのバージョンを確認
  2. Homebrewが使えるかどうか確認
  3. rbenvをインストール
  4. rbenvでインストール可能なRubyのバージョンを確認
  5. Rubyをインストールする
  6. ターミナル上でインストールしたRubyを使えるようにする
  7. ターミナル上で使えるRubyのバージョンを確認する

RubyのバージョンとHomebrewの確認

1.現在ターミナル上で使っているRubyのバージョンを確認

username$ ruby -v
=>ruby 2.6.3   #人によって異なります

私の場合は2.6のバージョンがデフォルトでインストールされていました。
この項目は人によって異なると思います。

2.Homebrewが使えるかどうか確認

username$ brew doctor
=>Your system is ready to brew.   #これが出力されたらOK!

上記以外の結果が出た場合は何らかのエラーが起こっているので、安全に使えないよ〜
という状態です。
エラーが解消できれば上記の結果が出力されるはずです。
ちなみに私の場合は、
Warning! 「コマンドラインツールをアップデートしてね!」
と出力されたため、CLTをアップデートさせたところ上記結果が出力されました。

rbenvインストール〜Rubyを使用可能にする

3.rbenvをインストール

username$ brew install rbenv
username$ rbenv --version   #rbenvのバージョンを確認&インストールできているか確認
=>rbenv 1.1.2               #(2021/2時点)

4.rbenvでインストール可能なRubyのバージョンを確認

username$ rbenv install --list
=>2.5.8
  2.6.6
  2.7.2
  3.0.0    ...etc    #インストールできるRubyのリストが出力されます

5.Rubyをインストールする

username$ rbenv install 3.0.0
username$ rbenv versions
=>* system (set by /Users/username/.rbenv/version)
  3.0.0

*system~ の出力結果は、.rbenv/version下で利用できるRubyのバージョンです。
rbenvを利用しRuby3.0.0のインストールはできていますが、
まだMacOS上で利用できるRubyは変更されていません。

6.ターミナル上でインストールしたRubyを使えるようにする
7.ターミナル上で使えるRubyのバージョンを確認する

username$ rbenv init
=>"$(rbenv init -)"

#bash_profileにパスを通す
username$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

#再読み込み
username$ source ~/.bash_profile

#ターミナル上のRubyバージョンを確認
username$ ruby -v
=>ruby 3.0.0

これで最新版Rubyをターミナル上で使えるようになりました!
bash_profile が見つからない、とエラーになった場合は、
ホームディレクトリにbash_profileファイルを作成する必要があります。

まとめ

今回は下記の流れでターミナル上でRubyを利用できるようにしました。

  1. 現在ターミナル上で使っているRubyのバージョンを確認
  2. Homebrewが使えるかどうか確認
  3. rbenvをインストール
  4. rbenvでインストール可能なRubyのバージョンを確認
  5. Rubyをインストールする
  6. ターミナル上でインストールしたRubyを使えるようにする
  7. ターミナル上で使えるRubyのバージョンを確認する

他設定にあたり必要な項目などありましたら、随時追記していきます!

参考記事

rbenvでrubyのバージョンを管理する
rbenvによるRubyのインストールからHello, World!まで

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

cannot load such file -- nokogiri/nokogiri (LoadError) を解決する方法

この記事では、
cannot load such file -- nokogiri/nokogiri (LoadError) を解決する方法
について書いていきます。

あくまで、僕自身が実際に行った方法ですので、全員がうまくいくとは限りません。

ですが、少しでも参考にしていただけたら幸いです。

解決法

実際のエラー

$ rails s
require': cannot load such file -- nokogiri/nokogiri (LoadError)

何行もあるエラーの最後にこのような文が記載されていました。
gem install nokogiriなどを行っても変わりませんでした。

$ which ruby
/usr/bin/ruby

which ruby でrubyの場所を調べたところ、
/usr/bin/ruby に存在している事がわかりました。

$ [[ -d ~/.rbenv  ]] && \
  export PATH=${HOME}/.rbenv/bin:${PATH} && \
  eval "$(rbenv init -)"

$ which ruby
/Users/XXXX/.rbenv/shims/ruby

このようにrubyの場所を変更します。

$ bundle install --path vendor/bundle

最後にbundle installを行うことでエラーが解消されると思います。

まとめ

$ [[ -d ~/.rbenv  ]] && \
  export PATH=${HOME}/.rbenv/bin:${PATH} && \
  eval "$(rbenv init -)“

$ bundle install --path vendor/bundle

この2つで僕自身の環境では治せました。
それぞれ環境は違うと思うので、参考程度によろしくお願いいたします。

質問や訂正等ございましたら、コメントの方でお願いできればと思います。
最後まで読んでいただきありがとうございました。

参考

cannot load such file -- nokogiri/nokogiri (LoadError) のエラーを解決するまで。 - Qiita

注意

この記事は、プログラミング初学者が書いており、内容に誤りがある場合がございます。
あしからずご了承願います。
また、誤りにお気付きの場合にはご指摘いただけると幸いです。
よろしくお願いいたします。

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

【Rails】アソシエーションで紐づいたレコードを自動保存

概要

中級者は知ってて当然なので、初心者向けです?

アソシエーションで関連付けられたモデルを保存する時に、片方のレコードに外部キーが設定されているせいで、「保存処理を2回に分けなきゃ」と思ったことはないですか?(保存するまでidが確定しないので)

そんなときどうするのというお話です

※ 自分もかつて良くないコードを書いていたので、同じような初心者の参考になってもらえれば幸いです

具体例

例えば本のモデル(Book)と著者のモデル(Author)があると想定します
book has_many authorsの関係)

Bookモデル?

カラム 補足
id integer
title string

Authorモデル:man_tone1:

カラム 補足
id integer
book_id integer 外部キー
name string

このとき初心者がやりがちなのが以下の書き方

books_controller
# createアクション

book = Book.new(book_params)
if book.save
  author = Author.new(book_id: book.id, name: author_params[:name])
    if author.save
      # 保存後の処理
    else
      # 失敗時の処理
    end
else
  # 失敗時の処理
end

# book_params, author_paramsの定義は省略してます

これだと以下のようにデメリットが大きいです

  • if文の階層が深くなる
  • bookだけ保存されてauthorが保存されない場合が出てしまう

早期リターンやtransactionでこれらを回避することもできますが、わざわざそんな面倒なことをしなくて済む方法があるのです✨(以下)

books_controller
# createアクション

book = Book.new(book_params)
author = book.build_author(author_params)
return ['保存成功時の処理'] if book.save

# 失敗時の処理

この場合bookの保存が成功した場合はauthorの保存も保証してくれます(片方だけ保存されることはない)

当然外部キーは関連付けられて保存されます
すっきりしていて見やすく、ファットコントローラ対策にもなります?
※ 早期リターンでリファクタもしてます

モデルの関係性が、belongs_tohas_manyとで書き方が変わるので注意が必要です(以下参照)

belongs_toで関連付けられたモデルのbuild

book.build_author

has_oneでの関連付けもこちらになります

has_manyで関連付けられたモデルのbuild

author.books.build

  • 1つだけ紐づくモデル
    => build_[モデル名の単数形]

  • 複数紐づくモデル
    => [モデル名の複数形].build

と覚えとけば良さそうです?
build_複数形にするとインスタンスが複数buildされるイメージがつくからこのように分けたんですかね?)

参考

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

rspecのテストでFactorybotが代入されなかった!?

WHY

rspecでテストコードの記述中、Factorybotで二つの変数を定義しようとしたところ、片方は代入されるのに、片方はされなかったことが起きたのでアウトプットしていきます。


問題

今回はテストコードを記述し、いざ実行!とするとこのようなエラー文がずらり、、、
fact.jpg

対処

最近エラーを解除するのがとても楽しくなってきた自分ですが、とりあえず最初に目が止まったのは
「 Failure/Error: user = FactoryBot.create(:user)」
というエラー、
まぁ調べるまでもなくuserに入っていないという意味でしょう、、しかし、コードをみてみると、

before do
    item = FactoryBot.create(:item)
    user = FactoryBot.create(:user)
    @sample = FactoryBot.build(:sample, user_id: user.id, item_id: item.id)
end

記述は上のitemと同じ、記述は間違っていない、、、となるとFactoryBot内がおかしいのか?とおもいましたが、
UserのFactoryBotは他のspecファイルでも使用しており、そこでは問題なく動作している、、、

続いてエラー文の下の方をみてみると
「Failure/Error: _query(sql, @query_options.merge(options))」
という文字が、調べてみるとテストではたまに起きて、変数が代入しきる前にテストが実行されたりすることで起こるのだそう、、、
解決策としてはsleepというものを使う。
なので書いてみた

before do
    item = FactoryBot.create(:item)
    user = FactoryBot.create(:user)
    @sample = FactoryBot.build(:sample, user_id: user.id, item_id: item.id)
    sleep(1)
end

これでテストは1秒ごとに実行される。

しかしエラーは治らなかった、となればやっぱり代入がされない何かの理由がある!!
ということで次に目が行ったのは
「Validation failed: Email has already been taken」という文字

直訳すると「メールはすでに使われている」というもの、
deviseを使っているので一意性があるのでしっかりバリデーションが仕事してくれています!

解決

ということでFactorybotをみにいくと、

FactoryBot.define do
  factory :user do
    nickname              { 'Test' }
    email                 { 'test@test' }
    password              { 'test111' }
    password_confirmation { password }
  end
end

このEmailに注目!
一意性のある値をテストするときはFakerを指定しなければならないのでemailの値を修正します!!

email                 { Faker::Internet.free_email }

その後テストはエラーなく無事解決しました!!

なお、「Failure/Error: _query(sql, @query_options.merge(options))」に関してはその後も出たので、
sleepはそのままにしています。

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

【AWS】デプロイ後にadminユーザーを追加する方法〜seedsファイルの実行〜

なんの話し?

AWSにデプロイした後にadminユーザーを追加する方法がわからなくて悩んだ結果の話しです。
開発環境ではseedsファイルにadmin: trueの記述をしていた。

awsにデプロイした後にadminユーザーを追加したい
などで検索するとIAMアカウント関連の結果ばかり出てきてしまい、なかなか欲しい情報にたどり着けない。
そもそもseedsを追加した時のコマンドは開発環境で下記のように実行していた。

開発環境のとき

% rails db:seed

そしてHerokuにデプロイした時にも同じことで悩み、実行したコマンドがこちら。

Herokuにデプロイしたとき

# アプリケーションのディレクトリで実行

% heroku run rails db:seed

EC2のリポジトリでRailsを起動するときのコマンドを見ていたら、
rails~コマンドを使えれば追加できるのではないか?」と考え、実行してみた。

AWSにデプロイしたとき

# EC2のリポジトリ内で実行

$ rails db:seed RAILS_ENV=production

これでadminユーザーが追加された。

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

Rubyで月の日数を当てるプログラムを作ってみた

はじめに

私は、勉強のアウトプットとしてミニアプリをつくったりします。今回は、少し複雑な条件分岐を用いて閏年を当てるプログラムを書きました。備忘録として残りしたいと思いますので宜しくお願いします。

閏年になる場合を加味する

Googleぐぐるとトップに出てきますが、
①西暦年が4で割り切れる年は閏年
②ただし、西暦年が100で割り切れる年は平年
③ただし、西暦年が400で割り切れる年は閏年
とこの三つの条件分岐があるみたいです!

さて早速、プログラミングを組んでいきます。

早速コードを書きます!

2月の日付のみ条件分岐にかけます。
理由としては、2月だけ日付の変動があるからです。

def get_days(year, month)
  month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  # month_daysに予め配列で1~12月までの日数を入れておきます。

  if month == 2 #2月を選んだ場合のみ、条件分岐にかけます。
    if year % 4 == 0 #4で割り切れる場合は、さらに条件分岐にかけます。
      if year % 100 == 0 && year % 400 != 0 #100で割り切れて、400で割り切れない場合の条件分岐です。
        days = 28
      else
        days = 29
    end
  else
    days = 28
  end
  else
    days = month_days[month - 1]
  end
return days
end

puts "年を入力してください:"
year = gets.to_i
puts "月を入力してください:"
month = gets.to_i

days = get_days(year, month)
puts "#{year}#{month}月は#{days}日間あります"


完成しました!

Javaでも作ってみた

しかし、今のJavaの知識では同じプログラムは作れず。
Googleの記事に上がっていたコードを参考にしました。

public class LeapYear1 {
    public static void main(String[] args) {
        int year = 2020;
        boolean check = false;

        if (year % 4 == 0) {
            if ((year % 100 ) == 0) {
                if ((year % 400 ) == 0) {
                    check = true; //閏年
                }
            }else {
                check = true; //閏年
            }
        }
        System.out.println(check ? "閏年です。" : "閏年ではありません。");
    }
}

閏年の判定ができるプログラムになりました。

最後に

Rubyだけではなく、Javaでもプログラムが組めるように徐々にやっていきたいなと思います。
ここまで読んでいただきありがとうございました!

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

【Rails】【nested_form】追加された要素自体のIDを取得する方法

結論

以下のように記述します。

 fields_for :items do |item|
  item.object_id #=> 「69870471010820」
 end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ActiveRecord::ConnectionNotEstablished を解消する

1.はじめに

railsでアプリを作成し、サーバーを立ち上げrails sを行い、http://localhost:3000/XXにアクセスしました。
すると、ActiveRecord::ConnectionNotEstablishedと表示されました。

2.使用環境

  • mac.os バージョン10.15.6
  • Ruby 2.6.6
  • Rails 6.0.3.5
  • psql (PostgreSQL) 12.6

3.実際のエラー

ActiveRecordのエラー

4.試したこと

4.1 データベースの確認

error.rb
# 利用可能な全てのデータベースを一覧表示する
hogehoge@hogenoAir sample_app % psql -l
psql: error: could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

4.2 postmaster.pidの削除

hogehoge@hogenoAir sample_app % rm /usr/local/var/postgres/postmaster.pid

こちらを試すと、うまくいきました。

補足 /usr/の場所

macの場合、Finderの移動タブから、フォルダへ移動を選択し、/usr/と入力すると表示されます。

5.まとめ

今回はリンクの5番に近い状況で、
① パソコンがフリーズ
② 全て保存・終了して再起動
③ 表題のエラーが表示される ...という流れでした。

②で全て終了していたはずが、うまく終了されなかったようです。
傷がついてしまったpostmaster.pidを削除することで、解決できました。

6.参考リンク

1:PostgreSQL 9.1.5文書
2.Postgresqlに接続できなくなった時
3.Postgres could not connect to server
4.【Mac】Finderから /usr / local ディレクトリを参照する
5.Mac+HomebrewでPostgreSQLが起動しない場合の対応

7.最後に

記事の感想や意見、ご指摘等あれば伝えていただけるとありがたいです。
読んでいただき、ありがとうございました。

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

|= or equal記法について

概要

  • 記法....変数1 ||= 変数2or関数
  • 意味....変数1がnilの場合,変数2を代入する。そうでない場合は,何も代入しない。

  • @foo ||= "bar"
  • @fooに値が存在する場合は,何も代入しない,nilの場合は文字列barを代入する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】特定のカラムのオプションだけを変更したい

Rails6.0.0でアプリを作っていて、既に作成済みのテーブルのカラムにオプションの制約をつけ忘れていた。
なので、特定のカラムのオプションだけを追加で設定する方法をまとめる。

現状

現状のマイグレーションファイルは以下のように「null:false」オプションを付け忘れているカラムがある。

class CreateItems < ActiveRecord::Migration[6.0]
  def change
    create_table :items do |t|
      t.string     :name
      t.text       :explain
      t.integer    :price
      t.references :user,            null: false, foreign_key: true
      t.integer    :category_id,     null: false
      t.integer    :item_status_id,  null: false
      t.integer    :shipping_fee_id, null: false
      t.integer    :prefecture_id,   null: false
      t.integer    :delivery_id,     null: false
      t.timestamps
    end
  end
end

手順

①新しいマイグレーションファイルを作る。

「rails g migration ChangeColumnOfItems」とコマンドを打つ(アッパーキャメル)。
コマンドの意味は「Itemsテーブルのカラムを変更せよ」という感じ。

terashimatakaya@MacBook-Air furima-34501 % rails g migration ChangeColumnOfItems
Running via Spring preloader in process 8220
      invoke  active_record
      create    db/migrate/20210220012237_change_column_of_items.rb

②マイグレーションファイルを自分で修正する。

「Railsガイドv6.1」のマイグレーションの章を参考に、以下のように記述した。
意味としては、「itemsテーブルのnameカラムにnull:falseのオプションを追加してね」という感じ。

class ChangeColumnOfItems < ActiveRecord::Migration[6.0]
  def change
    change_column_null :items, :name, false
    change_column_null :items, :explain, false
    change_column_null :items, :price, false
  end
end

③マイグレーションを実行

「rails db:migrate」を実行。

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

sqlcommenterをRuby on Railsのデモアプリケーションで動かしてみる

Googleが提供しているオープンソースのツールで、ORMが生成するSQLの調査を容易にしてくれるらしいので使ってみました。
具体的にはログなどに、コメントが含まれるようになり、ORMが生成したSQL文はどのコードで生成されたものかを知ることができるみたいです。

今回Rails APIモードのデモアプリケーションが用意されているので、どのようなコメントが出力されるか動かして確認してみます。
google/sqlcommenter | GitHub

sqlcommenterが対応する言語、フレームワーク、DBは以下

言語

  • Ruby
  • Python
  • JavaScript
  • Node.js
  • Java

フレームワーク

  • Ruby on Rails
  • Flask
  • Django
  • Express
  • Knex.js
  • etc...etc

DB

  • MySQL
  • MariaDB
  • PostgreSQL
  • SQLite
  • Google Cloud SQL

デモRailsアプリケーションで試してみる

google/sqlcommenter | Rails demo

RailsのAPIデモアプリケーションでとりあえずsqlcommenterがどんなものかを体験ができるみたいです。
ただ、sqlcommenter_railsはrubygemsではリリースされていないようなので、上記のREADMEの手順に沿って環境構築すればとりあえずは使うことができます。

APIは以下の2つが用意されています。

  • Get /posts
  • POST /posts

APIを叩くcurl

bash
curl localhost:3000/posts
bash
curl -X POST localhost:3000/posts -d 'title=my-post'

POST /postsを実行

bash
curl -X POST localhost:3000/posts -d 'title=my-post'
INSERT INTO "posts" ("title", "created_at", "updated_at") VALUES (?, ?, ?)
 /*action='create',application='SqlcommenterRailsDemo',controller='posts',db_driver='ActiveRecord::ConnectionAdapters::SQLite3Adapter',
 framework='rails_v6.0.3.5',route='/posts', traceparent='00-e71c784d7151a939ef4e8d886a326456-82ad00a116eaa038-01'*/

初期設定では以下の情報が出力されるようです。

  • action
  • application
  • controller
  • db_driver
  • framework
  • route
  • traceparent

設定を追加することで、出力する情報の変更 / 追加が可能です。

bash
mkdir config/initializers
touch config/initializers/marginalia.rb
vim config/initializers/marginalia.rb

# 追加
Marginalia::Comment.components = [ :action, :application, :controller_with_namespace, :hostname, :job, :line]
~
~
~

Rails serverを再起動して再度curlを実行すると先ほど追加した情報が表示されます

/*action='create',application='SqlcommenterRailsDemo',controller_with_namespace='PostsController',
hostname='xxxx',line='/app/controllers/posts_controller.rb:25:in `create\''*/

image.png

GET /postsを実行

次にpostの一覧を取得してみます。

bash
curl localhost:3000/posts
SELECT "posts".* FROM "posts"
/*action='index',application='SqlcommenterRailsDemo',controller_with_namespace='PostsController',
hostname='xxxx',line='/app/controllers/posts_controller.rb:19:in `index\''*/

先程のPOSTと同じくactionや実際にSQL文が生成されたコードの位置を出力してくれました。

posts_controller.rbは以下のようになっています。

app/controllers/posts_controller.rb
#...
17 class PostsController < ApplicationController
18   def index
19    render json: Post.all
20   end
21
22   def create
23     title = params[:title].to_s.strip
24     head :bad_request if title.empty?
25     render json: Post.create!(title: title)
26   end
27  end

参考

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

N+1問題の発見方法と解決方法(備忘録)

概要

N+1問題とは必要以上にSQL文が発行されてパフォーマンスが低下する問題です。
N+1問題の解決方法
便利なgem bullet(N+1を検出してくれる)の導入
最後に

初学者のただの備忘録です。

環境

  • Ruby: 2.6.6
  • Rails: 6.0.3.5

実装例

例えば、パラメータのcategory_nameがDBに存在するかどうかによって
@ideasに代入される値が変わる処理があるとします。

app/controllers/ideas_controller.rb
def index
  category = Category.find_by(name: params[:category_name])
  if category.present?
    @ideas = category.ideas
    render formats: :json
  else
    @ideas = Idea.all
    render formats: :json
  end
end

categoryとideaの関係は

app/models/category.rb
has_many :ideas, dependent: :destroy
app/models/idea.rb
belongs_to :category

N+1問題が発生してない場合

category.present?がtrueの場合は@ideasにcategory.ideasが代入されて
レンダリングされる。
そのときのログを一応確認(わかりやすいように一部ログをはしょっています)
CategoryとIdeaが1回ずつ読み込まれてます。N+1問題は発生していません。

ターミナル
  Category Load (0.5ms)  SELECT `categories`.* FROM `categories` WHERE `categories`.`name` = 'hoge1' LIMIT 1
  ↳ app/controllers/api/v1/ideas_controller.rb:13:in `index'
  Idea Load (0.4ms)  SELECT `ideas`.* FROM `ideas` WHERE `ideas`.`category_id` = 1
  ↳ app/views/api/v1/ideas/index.json.jbuilder:2

N+1問題が発生している場合

category.present?がfalseの場合、@ideasにIdea.allが代入されてレンダリングされる。
ログを確認するとCategory Loadが何回も...
これがN+1問題です。

ターミナル
  Idea Load (0.5ms)  SELECT `ideas`.* FROM `ideas`
   app/views/api/v1/ideas/index.json.jbuilder:2
  Category Load (0.3ms)
   app/models/idea.rb:4:in category_name
  Category Load (0.3ms)
   app/models/idea.rb:4:in category_name
  CACHE Category Load (0.0ms)
   app/models/idea.rb:4:in category_name
  Category Load (0.3ms) 
   app/models/idea.rb:4:in category_name
  Category Load (0.4ms)
   app/models/idea.rb:4:in category_name
  Category Load (0.3ms)
   app/models/idea.rb:4:in category_name
  CACHE Category Load (0.0ms) 
   app/models/idea.rb:4:in category_name
  Category Load (0.4ms)
   app/models/idea.rb:4:in category_name
  CACHE Category Load (0.0ms)
   app/models/idea.rb:4:in category_name
  Category Load (0.4ms)
   app/models/idea.rb:4:in category_name

N+1問題を解決する方法

結論から言いますとincludes、preload、eager_loadのいずれかを使用すると解決できます。

他の記事ではよくincludesで解決してますが、結局のところincludesは条件によって
preloadしたりeager_loadしたりする。preloadはエラー(例外)を発生させることもあるので
eager_loadを使用するのが無難だと思います。

詳しく知りたい方は
下記の記事を読んでみてください。

ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い

では、解決していきます。

eager_load (LEFT OUTER JOIN)の場合

app/controllers/ideas_controller.rb
# def index
#   category = Category.find_by(name: params[:category_name])
#   if category.present?
#     @ideas = category.ideas
#     render formats: :json
#   else
      @ideas = Idea.eager_load(:category)  # もとはIdea.all
#     render formats: :json
#   end
# end
ターミナル
  Category Load (0.6ms)  SELECT `categories`.* FROM `categories` WHERE `categories`.`name` = 'hoge1ddd' LIMIT 1
  ↳ app/controllers/api/v1/ideas_controller.rb:13:in `index'
  Rendering api/v1/ideas/index.json.jbuilder
  SQL (0.5ms)  SELECT `ideas`.`id` AS t0_r0, `ideas`.`category_id` AS t0_r1, `ideas`.`body` AS t0_r2, `ideas`.`created_at` AS t0_r3, `ideas`.`updated_at` AS t0_r4, `categories`.`id` AS t1_r0, `categories`.`name` AS t1_r1, `categories`.`created_at` AS t1_r2, `categories`.`updated_at` AS t1_r3 FROM `ideas` LEFT OUTER JOIN `categories` ON `categories`.`id` = `ideas`.`category_id`
  ↳ app/views/api/v1/ideas/index.json.jbuilder:2

何回もCategoryがLoadされていたSQL文が発行されなくなりました。

preloadの場合

app/controllers/ideas_controller.rb
# def index
#   category = Category.find_by(name: params[:category_name])
#   if category.present?
#     @ideas = category.ideas
#     render formats: :json
#   else
      @ideas = Idea.preload(:category)  # もとはIdea.all
#     render formats: :json
#   end
# end
ターミナル
  Category Load (0.4ms)  SELECT `categories`.* FROM `categories` WHERE `categories`.`name` = 'hoge1ddd' LIMIT 1
  ↳ app/controllers/api/v1/ideas_controller.rb:13:in `index'
  Rendering api/v1/ideas/index.json.jbuilder
  Idea Load (0.5ms)  SELECT `ideas`.* FROM `ideas`
   app/views/api/v1/ideas/index.json.jbuilder:2
  Category Load (0.4ms)  SELECT `categories`.* FROM `categories` WHERE `categories`.`id` IN (1, 2, 3, 4, 5, 6, 7)
  ↳ app/views/api/v1/ideas/index.json.jbuilder:2

includesの場合

app/controllers/ideas_controller.rb
# def index
#   category = Category.find_by(name: params[:category_name])
#   if category.present?
#     @ideas = category.ideas
#     render formats: :json
#   else
      @ideas = Idea.includes(:category)  # もとはIdea.all
#     render formats: :json
#   end
# end
ターミナル
  Category Load (0.3ms)  SELECT `categories`.* FROM `categories` WHERE `categories`.`name` = 'hoge1ddd' LIMIT 1
  ↳ app/controllers/api/v1/ideas_controller.rb:13:in `index'
  Rendering api/v1/ideas/index.json.jbuilder
  Idea Load (0.4ms)  SELECT `ideas`.* FROM `ideas`
   app/views/api/v1/ideas/index.json.jbuilder:2
  Category Load (0.2ms)  SELECT `categories`.* FROM `categories` WHERE `categories`.`id` IN (1, 2, 3, 4, 5, 6, 7)
  ↳ app/views/api/v1/ideas/index.json.jbuilder:2

おまけ

Ideaモデルに複数の関連付けがある場合は下記のように書くことができます。

app/controllers/ideas_controller.rb
# def index
#   category = Category.find_by(name: params[:category_name])
#   if category.present?
#     @ideas = category.ideas
#     render formats: :json
#   else
      @ideas = Idea.eager_load(:category, :hoge, :huga)
#     render formats: :json
#   end
# end

gem "bullet"の導入方法

N+1問題を検出してくれるgemです。
通常はターミナルに出ますが、jquery等でカスタムもできます。
今回それは割愛

Gemfile
gem "bullet"

bundleだけでも可能

ターミナル
$ bundle install

yでconfig/environments/development.rbを自動作成

ターミナル
$ bundle exec rails g bullet:install
  Would you like to enable bullet in test environment? (y/n) 

N+1問題が発生するとターミナルに下記のようにでます。(一部省略)
IdeasテーブルにCategoriesテーブルをincludesしてねということ

ターミナル
user: shu
GET /api/v1/ideas?category_name=
USE eager loading detected
  Idea => [:category]
  Add to your query: .includes([:category])

以上。

間違っている等ありましたらご指摘頂けると幸いです。

最後に

N+1問題の解決方法自体はそんなに難しいコードを書くわけではないですがSQLを読んで
なにが起こっているのかしっかり理解することが大切だと思います。
(解決できたらいいやーでは逆に遠回りになることを身を持って感じております。)

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

【Rails】 フラッシュメッセージ使い分け

はじめに

お恥ずかしながら、私は最近までフラッシュメッセージってflash[:notice] = ⚪︎⚪︎と書いておけばいいんだろうくらいの認識しかありませんでした。ポートフォリオ作成時にフラッシュメッセージの使い分けの方法があることを知り、少し理解を深めることができたので、自身の備忘録として残しておきたいと思います。

フラッシュメッセージって何?

何かの処理を行った後に画面に表示されるメッセージのことを指します。
例) ● ログイン時
    「ログインに成功しました」
   ● 記事の投稿時
    「記事の投稿に成功しました」

見本

スクリーンショット 2021-02-20 1.14.25.png

フラッシュメッセージの種類

● flash

次のリクエストまで表示させることができる。
= redirect_toの際に使用する。
※render時に使用すると次のリクエスト時までメッセージが画面に表示され続けてしまうので、不適切になります。

● flash.now

現在のリクエストまでしか表示できない。
= renderの際に使用する。


● notice

通知を出す時。主に成功した時のメッセージなど。

● alert

警告の通知を出す時。何かの動作に失敗時など。

コントローラー

今回は、投稿した際の保存成功時と失敗時にメッセージが表示されるようにしています。

books_contoroller.rb
def create
 @book = Book.new(book_params)
 @book.user_id = current_user.id
 if @book.save
  redirect_to books_path, notice: = "投稿に成功しました"
 else
  flash.now[:alert] = "投稿に失敗しました"
  render :new
 end
end

# 2行に分けて記述することも可能
# redirect_to books_path
# flash[:notice] = "投稿に成功しました"

ビュー

viewにもフラッシュメッセージが表示されるように記述を加えます。
※フラッシュメッセージを表示させるタイミングは何度もあるかと思いますので、部分テンプレートに書き出し、呼び出す形にしています。

books/index.html.erb
<%= render 'layout/flash' %>

<% @books.each do |book| %>
 <%= book.user.name %>
 <%= book.title %>
 <%= book.content %>
<% end %>

layout/_flash.html.erb
<% if notice %>
 <%= notice %>
<% elsif alert %>
 <%= alert %>
<% end %>

終わり

今回は以上になります。
私自身もプログラミング初心者ですが、同じ様な立場の方に少しでも参考になれば幸いです。
また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。

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

[Ruby on Rails] CSSが反応してくれない時の対処法

自身の備忘録を書いて行きます。

  
CSSを書いても全く反応してくれないことがありました。
なんでや?と思い調べて行くと、ちゃんと動いてくれるようになりました。

それの解決策を書いて行きます。

解決策

layouts/application.html.erb
 <%= stylesheet_link_tag 'application', media: 'all'%>

上記の記述をすることでCSSを読みこむようになりました。
こちらはCSSの類を全て読みこんでくれる記述らしいです。

  

その他に
ユーザー管理機能(devise使用の場合)の
ログイン等の配置を動かす場合は

config/initializers/devise.rb
config.scoped_views = true

上記の記述は元々、247行目でコメントアウトになっている
config.scoped_views = false
こういった記述になっている。

それをコメントアウトを外してtrueにすればいいらしい。

  

実際、両方やってたら問題なく動いているので、
今の所は大丈夫そう。

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

Rails ERDのススメ

About Rails ERD?

Railsのテーブル情報を自動生成するためのgem
https://github.com/voormedia/rails-erd
ER図をpdfで出力することができる。

こんな感じ⬇️
image.png

Getting started ?

for mac

brew intall graphviz

gemfile
gem install ‘rails-erd’, group: :development
  • 設定でmigrationの度に自動生成することも可能

Pros and cons

?

クライアント・新入メンバーへの説明するのに便利

?

関連にもよるがテーブル数が50超えてくるとごちゃごちゃしてすごく見づらい

内部の開発に関しては、モデル数20以下の場合annotateで十分
ある程度の規模のサービスの場合、LucidChartやdraw.ioで作図するするのがオススメ

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

Docker,CircleCI,AWS,Railsでポートフォリオを作成

前書き

就職活動用のポートフォリオをDocker,CircleCI,AWS(ECS)を用いてWebアプリケーションを作成しました。
バックエンドにRuby on Railsを用いました。
今回は作成したポートフォリオの機能、参考記事などを紹介したいと思います。

概要と作成背景

今回作成したアプリはスイーツのデリバリーに特化したECサイトです。

  • ケーキ屋のアルバイト経験からお店の業務改善をしたい
  • 手軽にお店のスイーツを食べたい
  • 店舗で販売しているようなスイーツを配達するサービスが存在しない(自分調べ)

上記の理由により飲食店にもユーザーにもメリットのあるサービスを作成しようと思いました。

アプリのURL: http://sweetsdeli.com

GitHubのURL: https://github.com/sekine617/sweetsdeli

画面イメージ紹介

トップページ

新着商品、人気商品などを表示
人気商品はユーザーの購入履歴から購入数が多い商品を表示しています。
スクリーンショット 2021-02-19 15.07.58.png

商品一覧ページ

こちらではカテゴリー検索、商品名検索などができます。
スクリーンショット 2021-02-19 15.27.36.png

商品詳細ページ

こちらでは商品をカートに入れる、お気に入り登録、商品のレビュー投稿などができます。
スクリーンショット 2021-02-19 15.27.58.png

マイページ

マイページではプロフィール編集、お気に入り、購入履歴、投稿したレビューを閲覧できます。

スクリーンショット 2021-02-19 15.21.47.png

スクリーンショット 2021-02-19 15.25.44.png

スクリーンショット 2021-02-19 15.25.57.png

スクリーンショット 2021-02-19 15.30.25.png

ショッピングカート

こちらではカートに入れた商品の確認、個数の変更、カートから削除などができます。
スクリーンショット 2021-02-19 15.29.35.png

購入画面

カートに入れた商品から合計金額を確認し、クレジットカードによる決済が可能です。実際に決済する場合はpay.jpのてすとカードを使用してください。
スクリーンショット 2021-02-19 15.30.02.png
このようにカード情報の入力フォームがモーダルウィンドウで出てきます。
スクリーンショット 2021-02-19 15.46.47.png

使用言語

  • フロントエンド
    • jQuery
    • HTML
    • CSS/Sass
    • Bootstrap
  • バックエンド
    • Ruby on Rails
    • Ruby
    • PAY.JP(外部API)
  • インフラ
    • Docker/docker-compose
    • CircleCI
    • nginx
    • mysql
    • AWS(ECS, ALB, S3, RDS, Route53, ECR, VPC, IAM)

インフラ構成図

スクリーンショット 2021-02-19 18.41.03.png

開発環境

機能一覧

  • ユーザー関連(devise)
    • 登録機能
    • プロフィール編集機能
    • ログイン・ログアウト機能
  • 決済機能(PAY.JP API)
  • 人気商品表示機能
  • タグ機能
  • 商品登録機能
  • 画像アップロード機能(AWS S3バケット, carrierwave)
  • お気に入り機能(非同期処理, jQuery)
  • フラッシュメッセージ表示機能
  • レビュー投稿機能
  • カート関連
    • カート登録機能
    • カート編集機能
  • 住所自動入力機能(jQuery)
  • 商品名検索機能
  • カテゴリー検索機能
  • ページネーション機能(kaminari)

データベース設計

スクリーンショット 2021-02-19 21.28.45.png

テーブル説明

テーブル名 説明
users ユーザー情報
orders 注文管理(受け取り日時など)
orders_products  注文商品管理
products 商品情報
reviews 商品に対するレビューを管理
likes 商品へのお気に入り情報
address ユーザーの住所
cart_items cartsとproductsの中間テーブル
shops ショップ情報
carts カート追加した商品情報一時的に保存
product_tag_relations productsとtagsの中間テーブル
tags 商品のカテゴリ情報

ポイントはproductsテーブルのquantity_per_dayで、1日の提供数を指定し注文数が1日の提供数を上回らないようにしました。
また注文時に受け取り日時を指定し、各店舗ごと、日にちごとに管理が可能です。

苦労した点

CircleCIでAWSへの自動デプロイ

CircleCIを用いて開発環境のDockerイメージをECRにpushし、ECSのタスク定義を更新してデプロイをしましたが、config.ymlの設定でのエラーに悩まされました。
またAWSの各設定にもかなり悩まされ、インフラの知識がないことで時間が結構かかりました。

credentials.ymlでのpay.jpなどのアクセスキー管理でなかなかうまく行かずインデントの重要性がよくわかりました。

参考記事

Railsでのテストを参考にしました。
【Rails】はじめてのRSpec!テストコードを書こう!

deviseのログイン機能を参考にしました
【Rails】ログイン機能を実装する

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

git push -u origin mainでpushされずerrorになる

rails tutorialのversion 6.0の1章でgitの扱い方を学びながら進めていたがgit push -u origin mainをしたら下記のようなエラーに遭遇した

terminal

error: src refspec main does not match any
error: failed to push some refs to '自分のgithubアカウント'

原因

2020/10月からgithubはレポジトリ名をmasterからmainに変更したためmainでpushしてもレポジトリ名が違うためerrorになるとのこと

次のようにgit branchで調べるとその通りだった

% git branch
* master

ので次のようにブランチを変えて解決

% git branch -M main
% git branch
* main

まだまだ初学者なのだと実感させられる

参考になったURL

https://deepblue-ts.co.jp/tips/git-push-error/

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