20201024のRailsに関する記事は30件です。

[Ruby] each_with_index と each.with_indexの違いを考えた

この記事ではmacOS Catalina10.15.6にインストールしたRuby 2.6.5を使っています。
2つの違いを区別するためにまとめてみました。

each_with_index

  • each_with_indexと2つの値をブロック変数に渡すことで、配列の要素の数だけ繰り返し処理その要素が何番目のものか表すことができます。
fruits = ["apple", "banana", "peach"]

fruits.each_with_index do |fruit, index|
  puts "#{index}番目のフルーツは#{fruit}です。"
end

結果

ただ、このままだと「0番目のフルーツはappleです。」となってしまいますね。

0番目のフルーツはappleです。
1番目のフルーツはbananaです。
2番目のフルーツはpeachです。

each.with_index

  • indexの引数に1を入れました。
  • すると、要素に割り振られる番号が1からスタートになります。
fruits.each.with_index(1) do |fruit, index|
  puts "#{index}番目のフルーツは#{fruit}です。"
end

結果

この通り、きれいに1から順番が始まっていますね。

1番目のフルーツはappleです。
2番目のフルーツはbananaです。
3番目のフルーツはpeachです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

データベースに登録するときにエラーメッセージを表示させたい

はじめに

 テストコードを書いていて、エラーメッセージを表示させるようにしたときに、わからなかったことなので、記録として残しておく。

エラーメッセージの表示方法

viewディレクトリに

_error_messages.html.erb
<% if model.errors.any? %>
  <div id="error_explanation" class="alert alert-danger">
    <ul>
      <% model.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

を作成し、renderメソッドで呼び出すようにする。
1行目のmodelは、今回データベースに何かを登録する場面を想定しているため。

呼び出す側のコード

<%= render 'error_messages', model: f.object %>

ファイルの位置によって、shared/error_messagesのように指定してあげる必要がある。

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

Dockerを組み込むために参考にした記事

参考にした記事

DockerをMacにインストールする
https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81

はじめてのDocker 導入から開発の流れまで
https://qiita.com/m-dove/items/173d08a5d8d910e10283

最初はわからなさすぎたのでYouTubeなども見ましたー。

もう環境構築で悩まない!Dockerを使ってRails環境構築!
https://www.youtube.com/watch?v=BZS8AHF3TTo&t=503s

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

即時に更新されるチャット機能(Action Cableの実装)

即時に更新されるチャットアプリ

  • Action Cableを用いた、リアルタイムチャットアプリを作成する

まとめてコードずらりします⬇︎

ターミナル
% cd projects
% rails _6.0.0_ new mini_talk_app -d mysql
% cd mini_talk_app
% rails db:create
ターミナル
% rails g controller messages new
% rails g model message text:text
% rails db:migrate
app/config/routes.rb
Rails.application.routes.draw do
  root 'messages#new'
  resources :messages, only: [:create]
end
app/controllers/messages_controller.rb
class MessagesController < ApplicationController
  def new
    @messages = Message.all
    @message = Message.new
  end

  def create
    @message = Message.new(text: params[:message][:text])
  end
end
app/views/messages/new.html.erb
<h3>mini_talk_app</h3>
<%= form_with model: @message do |f| %>
  <%= f.text_field :text %>
  <%= f.submit '送信' %>
<% end %>
<div id='messages'>
  <% @messages.reverse_each do |message| %>
    <p><%= message.text %></p>
  <% end %>
</div>

この時点で下記のようになっているはず

d6806db02ad04b485928bbd124c73f1a.png

Action Cableの実装(先ほどの続き)

ターミナル
% rails g channel message
app/channel/message_channel.rb
class MessageChannel < ApplicationCable::Channel
  def subscribed
    stream_from "message_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end
end
app/controller/messages_controller.rb
class MessagesController < ApplicationController
  def new
    @messages = Message.all
    @message = Message.new
  end

  def create
    @message = Message.new(text: params[:message][:text])
    if @message.save
      ActionCable.server.broadcast 'message_channel', content: @message
    end
  end
end
app/javascript/channels/message_channel.js
import consumer from "./consumer"

consumer.subscriptions.create("MessageChannel", {
  connected() {
    // Called when the subscription is ready for use on the server
  },

  disconnected() {
    // Called when the subscription has been terminated by the server
  },

  received(data) {
    const html = `<p>${data.content.text}</p>`;
    const messages = document.getElementById('messages');
    const newMessage = document.getElementById('message_text');
    messages.insertAdjacentHTML('afterbegin', html);
    newMessage.value='';
  }
});

現場からは以上です!

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

[chown]ファイルやディレクトリの所有者を変更する方法

chownコマンドとは?

ファイルやディレクトリのユーザーやグループの所有権を変更するためのコマンド。

$ chown [オプション] ユーザーorグループ ファイルorディレクトリ

使い方

1.対象のファイルのユーザー・グループの所有権を確認する。
※実行結果からユーザー所有権は「sample」グループ所有権「sample」であることがわかります。

$ ls -l
#=> -rw-rw-r— 1 sample sample 47 5月 21 08:42 test1.htmle.erb

2.ユーザー所有権を変更するには以下のように実行します。

$ chown root test1.htmle.erb

$ ls -l
#=> -rw-rw-r— 1 root samurai 47 5月 21 08:42 test1.htmle.erb

3.グループの所有権も変更する場合には引数にセミコロン「:」をはさんで「ユーザー:グループ」とする。

$ chown root:root test1.htmle.erb

$ ls -l
#=> -rw-rw-r— 1 root root 47 5月 21 08:42 test1.htmle.erb

オプション一覧

オプション 説明
-c 所有者情報を変更された場合に詳細を表示する
-f エラーメッセージを表示しない
-h シンボリックリンクの所有権も変更する
-R ディレクトリ内の所有権も変更する
-v 所有者変更情報の詳細を表示する(変更されなかった場合も表示)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Linux]かんたんに解説!権限確認と変更方法

パーミッションの確認方法

Linuxコマンドでカレントディレクトリ内のファイルやディレクトリの情報を確認する。

$ls -l

#=>
-rw-r--r--  1 user group      9  1 1 00:00 hoge.html.erb
drwxr-xr-x  6 user group  20480  1 1 00:00 ダウンロード

パーミッションの読み方

最初の1文字目はファイル種別を表す。
2〜4文字目はファイルの所有者に対する権限を表す。
5〜7文字目はファイルの所有グループに対する権限を表す。
8〜10文字目はその他に対する権限を表す。

権限の区分(設定する範囲)

種別 意味
- ファイル
d ディレクトリ
l シンボリックリンク

権限の基本種類

読み込み権限 書き込み権限 実行権限
r w x

権限の範囲

数値 権限 内容
0 --- 権限無し
1 --x 実行可
2 -w- 書込可
3 -wx 書込、実行可
4 r-- 読込可
5 r-w 読込、実行可
6 rw- 読込、書込可
7 rwx 読込、書込、実行可

-例-
-rw-r--r--

ファイル種別=ファイル
所有者=読み取りと書き込み権限有り
所有グループ=読み取り権限有り
その他=読み取り権限有り

アクセス権限の変更方法

$ chmod モード 対象ファイル名

$ chmod 764 hoge.html.erb

コマンドのオプション

引数 内容
-v コマンド実行結果を表示
-c 変更があった場合のみ、実行結果を表示
-R 再帰的に変更
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails学習 1日目その2

Ruby on Rails5速習実践ガイド chapter2

2-6-1-1 Railsアプリケーションを新規作成する

$ rails new scaffold_app -d postgresql

rails new:Railsの新規ファイル作成
scaffold_app:ファイル名
-d postgresql:SQLの指定(-dはpostgreSQL用ですよという意味)

2-6-1-2 ユーザー管理機能のひな形を作る

$ bin/rails generate scaffold user name:string address:string age:integer

bin/rails:
scaffold:アプリを作る上での作業をまとめて簡単にしてくれるツール
user:モデル名(userモデル)
name:string address:string age:integer:3つの情報をテーブルに入れテーブルを作成。

Column YAMLの基本

YAMLは構造化されたデータを表現するためのフォーマット。

animal: &animal
 cat: 'ネコ'
 dog: 'イヌ'

animal_shop_1:
  <<: *animal
  hamster: 'ハムスター'

animal_shop_2:
  <<: *animal
  parrot: 'オウム'

animalを&animalとしておく
つまり&animal= cat: 'ネコ' dog: 'イヌ'となる

animal_shop_1は
<<: *animal
hamster: 'ハムスター'

つまり
 &animalとhamster: 'ハムスター'
cat: 'ネコ' dog: 'イヌ' hamster: 'ハムスター'となる

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

テストコードで「basic認証が通らないエラー」を対応

エラー時の状況

某フリマサイトのコピーアプリを実装したので、練習としてコントローラーの単体テストコードを記述していたところ、実行時にターミナル上で下記エラーが発生しました。

ターミナル
bundle exec rspec spec/requests/items_spec.rb

~中略~

 1) ItemsController GET #index indexアクションにリクエストすると正常にレスポンスが返ってくる
     Failure/Error: expect(response.status).to eq 200

       expected: 200
            got: 401

       (compared using ==)
     # ./spec/requests/items_spec.rb:20:in `block (3 levels) in <top (required)>'

~中略~
開発環境

Ruby 2.6.5
Rails 6.0.3.3
MySQL
Visual Studio Code
(Caprybara,Rspec,GoogleChrome)

考察・検証

root_pathへのアクセス時にHTTPステータスが401になっている(アクセスできていない)、というエラーな模様。
本アプリケーションはbasic認証を導入しており、それがが関係しているのではと予想しました。

basic認証とは

HTTP通信の規格に備え付けられているユーザー認証の仕組みのことで、Webサイトにアクセス制限をかけることのできる簡易的な方法の一つです。
サーバーとの通信が可能なユーザーとパスワードをあらかじめ設定しておき、それに一致したユーザーのみが、Webアプリケーションを利用できるようにします。

【サンプル画像】
スクリーンショット 2020-10-24 19.44.28.png
上記エラー文とbasic認証で調べた所、やはりテスト時にbasic認証が通らない事例が見受けられました。
参考記事(記事末に掲載)を元に、テスト時のリクエストにbasic認証のユーザー名とパスワードをどうにか渡せないかと試してみました。

結果

メンターさんを交えて格闘すること約2時間…
参考記事を元に様々な方法を試みるもエラーは解決せず。
そして、メンターさんが調べたところ…
「2020年現在、テスト時のbasic認証は推奨されていない」とのことでした。
なんてこった…!

〜出来ぬなら、やらぬ〜

エラー解決の方針を「テスト時にbasic認証を通す」から、
「本番環境以外(テスト時)はbasic認証をしない」へ変更。

application_controller.rb
class ApplicationController < ActionController::Base
  before_action :basic_auth if Rails.env.production? # テスト時はbasic認証を行わないように分岐

  private
  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == 'username' && password == 'password'
    end
  end
end

※usernameとpasswordは、実際は環境変数で設定しています。

before_actionにif Rails.env.production?を追記して、本番環境でのみ適用するようにしました。
お陰で、テスト時はbasic認証せずそのままトップページへアクセスし、テストを実行できるようになりました!

感想

まだまだ初学者なこともあり、エラーに直面すると問題箇所を真正面から解決しようとしがちです。
今回のように、エラーを避ける、「やらずに済む」方法が無いかと多方面から考えることも重要だと実感しました。

本記事が初投稿でした。少しでも参考になれば嬉しく思います。
最後まで閲覧いただき、誠にありがとうございました。

参考記事

ベーシック認証(Basic認証)とは?設定方法と注意点・エラーになる原因を解説
Rails×Basic認証×Rspecでつまづいた
RSpec3でBASIC認証がかかったアクションをテストする
Rails/Rspecテストをhttp基本認証で渡す
Basic認証を交えたテストコードの書き方

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

テストコードで「basic認証が通らないエラー」が出る場合の対応

エラー時の状況

某フリマサイトのコピーアプリを実装したので、練習としてコントローラーの単体テストコードを記述していたところ、実行時にターミナル上で下記エラーが発生しました。

ターミナル
bundle exec rspec spec/requests/items_spec.rb

~中略~

 1) ItemsController GET #index indexアクションにリクエストすると正常にレスポンスが返ってくる
     Failure/Error: expect(response.status).to eq 200

       expected: 200
            got: 401

       (compared using ==)
     # ./spec/requests/items_spec.rb:20:in `block (3 levels) in <top (required)>'

~中略~
開発環境

Ruby 2.6.5
Rails 6.0.3.3
MySQL
Visual Studio Code
(Caprybara,Rspec,GoogleChrome)

考察・検証

root_pathへのアクセス時にHTTPステータスが401になっている(アクセスできていない)、というエラーな模様。
本アプリケーションはbasic認証を導入しており、それがが関係しているのではと予想しました。

basic認証とは

HTTP通信の規格に備え付けられているユーザー認証の仕組みのことで、Webサイトにアクセス制限をかけることのできる簡易的な方法の一つです。
サーバーとの通信が可能なユーザーとパスワードをあらかじめ設定しておき、それに一致したユーザーのみが、Webアプリケーションを利用できるようにします。

【サンプル画像】
スクリーンショット 2020-10-24 19.44.28.png
上記エラー文とbasic認証で調べた所、やはりテスト時にbasic認証が通らない事例が見受けられました。
参考記事(記事末に掲載)を元に、テスト時のリクエストにbasic認証のユーザー名とパスワードをどうにか渡せないかと試してみました。

結果

メンターさんを交えて格闘すること約2時間…
参考記事を元に様々な方法を試みるもエラーは解決せず。
そして、メンターさんが調べたところ…
「2020年現在、テスト時のbasic認証は推奨されていない」とのことでした。
なんてこった…!

〜出来ぬなら、やらぬ〜

エラー解決の方針を「テスト時にbasic認証を通す」から、
「本番環境以外(テスト時)はbasic認証をしない」へ変更。

application_controller.rb
class ApplicationController < ActionController::Base
  before_action :basic_auth if Rails.env.production? # テスト時はbasic認証を行わないように分岐

  private
  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == 'username' && password == 'password'
    end
  end
end

※usernameとpasswordは、実際は環境変数で設定しています。

before_actionにif Rails.env.production?を追記して、本番環境でのみ適用するようにしました。
お陰で、テスト時はbasic認証せずそのままトップページへアクセスし、テストを実行できるようになりました!

感想

まだまだ初学者なこともあり、エラーに直面すると問題箇所を真正面から解決しようとしがちです。
今回のように、エラーを避ける、「やらずに済む」方法が無いかと多方面から考えることも重要だと実感しました。

本記事が初投稿でした。少しでも参考になれば嬉しく思います。
最後まで閲覧いただき、誠にありがとうございました。

参考記事

ベーシック認証(Basic認証)とは?設定方法と注意点・エラーになる原因を解説
Rails×Basic認証×Rspecでつまづいた
RSpec3でBASIC認証がかかったアクションをテストする
Rails/Rspecテストをhttp基本認証で渡す
Basic認証を交えたテストコードの書き方

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

sudoを理解した上で使おう!

sudo

スーパーユーザ(や他のユーザ)の権限でコマンドを実行するときに使うコマンド。

コマンド 概要
-A パスワード入力用のコマンドを使用する(コマンドは「/etc/sudo.conf」または環境変数「SUDO_ASKPASS」で設定)
-n パスワードを要求するプロンプトを表示しない(パスワード入力が必要なコマンドの場合はエラーとなって実行できない)
-p 文字列 パスワード入力時のプロンプトを指定する(デフォルトは「:」記号)
-S パスワードを端末ではなく標準入力から読み込む(パスワードの末尾には改行が必要)
-k 保存されている認証情報を無効にする(次回のsudo実行時には必ずパスワード入力が必要)。コマンドと一緒に使用可能
-K 保存されている認証情報を完全に消去する
-V 保存された認証情報を更新する
-u ユーザー コマンドを実行するときのユーザーを「ユーザー名」または「ユーザーID(#記号と数字)」で指定する
-g グループ コマンドを実行するときのグループを「グループ名」または「グループID(#記号と数字)」で指定する
-P sudoを実行するユーザーが所属するグループのままコマンドを実行する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rspecで多対多のFactoryを作ってSystemSpecでテストしたときのノート[RSpec, FactoryBot]

背景

Rspecで多対多の関係を作って、SystemSpecでテストをしました。
元々のテーブル構造の複雑さもあり、かなり苦労したので、以下にノートをまとめます。

実行完了は下記の通りです。

  • Rspec 3.9
  • Rails 5.2.4.2

テーブル構造

テーブル構造は、このようになっています。メインのテーブルは、offices(ある会社の支社)とshops(営業先である店舗)で、office(支社)はevebtを開いて、そこに営業先である店舗shopsが参加します。shopsは必ず何らかのcategoryを持っています。

Image from Gyazo

起こっていた問題

モデルメソッドの関係で、どうしてもeventsfactoryを作った時に、それに紐づく、shopsが必要になりました。しかし、構造上、このように書いたのでは、エラーが出てしまいました。

let!(:shop){FactoryBot.create(:shop)}
let!(:event){FactoryBot.create(:event)}
#=> ここで eventに紐づくshopがないとエラーになる構造になっていた
let!(:event_shops){FactoryBot.create(:event_shop, event: event, shop: shop)}

対処方法

そこで、eventfactory生成時に、同時に関連するshopも定義できるようなfactoryを作成することにしました。

eventsのファクトリを作る

作成したFactoryは以下のようになっています。

spec/factories/events.rb
FactoryBot.define do
  factory :event do
    office_id { nil }
    name { "テストイベント" }

    trait :with_shops do
      after(:create) do |event|
        category = FactoryBot.create(:category, :sequence)
        create_list(:shop, 1, events: [event], category: category)
      end
    end
  end
end

まず、eventのファクトリ内にwith_shopsというtraitを作成して、event作成時に関連するshopも作成できるようにします。create_listshopのファクトリを複数作成でき流ようにし(ここでは1つしか必要ないので1つだけ)、eventsからeventのインスタンスが複数入れられるように、[event]と配列で表記します。

なお、categoryがわざわざtraitを使って呼び出してあるのは、後ほど解説します。

System Specで適切に呼び出せるようにする

spec/factories/categories.rb
FactoryBot.define do
  factory :category do
    name { "医療系" }

    trait :sequence do
      sequence(:id, 100)
      name { "服飾系" }
    end
  end
end
spec/system/events_spec.rb
let!(:event) { FactoryBot.create(:event, :with_shops, office: office, shops: [shop]) }
# ここからcategoryをshopに当てはめることはできない

次に、categoryのファクトリですが、ビューの中で、eventは複数回生成されます。そのため、同時に生成されるshopcateogoryは毎回別のid(FK)にしないと、FKの重複エラーになってしまいました。

また、system specからeventshopscategoryを当てはめようとすると、spec/factories/events.rb内で、shopのファクトリを作成しようとしたときに、「外部キーが存在しません」とエラーになってしまいました。

そのため、sequenceを利用して、一つ一つ違うid(FK)のcategoryを作成するようにしました。

終わりに(参考サイトなど)

解決に長い時間がかかってしまいましたが、なんとか解決できてよかったです!
今回はDBの構造が複雑だったため、このような複雑なSpecを書かねばならなくなってしまいましたが、
今後はDB設計をしっかりして、もっとシンプルなテストで済むようにしたいです。。。。

▼特に参考にさせていただいた記事など
- FactoryGirlで「多対多」や「複数の1対多」のアソシエーションを設定する
- Factorybotのtraitを使って、has_manyが2重にある複雑なassociation付きのデータを用意する
- FactoryBot (旧FactoryGirl) の sequence と .next

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

Rails 3 から PostgreSQL 10 に接続するときに使ったコード

Rails 3.0.1 で PostgreSQL 10 に繋いでテストする必要があり、 initializer を追加することになりました。

config/initializers/backport_pg_10_support_to_rails_3.rb

if Rails.env.test?
  module ActiveRecord
    class Base
      class << self
        def establish_connection(*arg)
        end
      end
    end
  end
  require 'active_record/connection_adapters/postgresql_adapter'
  module ActiveRecord
    module ConnectionAdapters
      class PostgreSQLAdapter < AbstractAdapter
        # Resets the sequence of a table's primary key to the maximum value.
        def reset_pk_sequence!(table, pk = nil, sequence = nil)
          unless pk and sequence
            default_pk, default_sequence = pk_and_sequence_for(table)
            pk ||= default_pk
            sequence ||= default_sequence
          end
          if pk
            if sequence
              quoted_sequence = quote_column_name(sequence)
              if ActiveRecord::Base.connection.select_value('SELECT version()').include?('PostgreSQL 10')
                # language=sql
                sql =<<-EOS
                  SELECT setval('#{quoted_sequence}', (SELECT GREATEST(MAX(#{quote_column_name pk})+(SELECT seqincrement FROM pg_sequence WHERE seqrelid = '#{quoted_sequence}'::regclass), (SELECT seqmin FROM pg_sequence WHERE seqrelid = '#{quoted_sequence}'::regclass)) FROM #{quote_table_name(table)}), false)
                EOS
              else
                # language=sql
                sql =<<-EOS
                  SELECT setval('#{quoted_sequence}', (SELECT GREATEST(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
                EOS
              end
            else
              @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
            end
          end
        end
      end
    end
  end
end

require 'active_record/connection_adapters/postgresql_adapter' を入れて、 def reset_pk_sequence!(table, pk = nil, sequence = nil) を上書きしましたが、 それだけだと establish_connection が存在しないというエラーが出るので、 require の前に establish_connection を記述しました。

reset_pk_sequence!https://github.com/rails/rails/blob/3-0-stable/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb から基本的にコピーで、 変更したのは SQL 作成部分です。

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

Bootstrap4でflashメッセージに閉じるアイコンボタン(Dismissing)を実装する

前提条件

すでにbootstrap4を導入していてる人。

bootstrap3でもできますが少し記述が変わるので以下参考

https://bootstrap-guide.com/components/alerts

やりたい事

flashメッセージの横に✖️を出してメッセージを消せるようにしたい
(参考画像)http://drive.google.com/uc?export=view&id=1ckdEvoEdn79roPkAsarFpTbnf-pq_Ejl

実装

以下のコードを部分テンプレートファイルに記載

layout/_flashes.html.erb
<% flash.each do |key, value| %>
  <div class="alert alert-<%= bootstrap_alert(key) %> alert-dismissible fade show" role="alert">
  <button type="button" class="close" data-dismiss="alert" aria-label="閉じる">
    <span aria-hidden="true">&times;</span>
  </button>
    <strong>
      <%= value %>
    </strong>
  </div>
<% end %>

コードの説明

<% flash.each do |key, value| %>はコントローラなどでflashメッセージを記載したときにeachで毎回出るように回しています。
<span aria-hidden="true">&times;</span>&times;は✖️のエスケープ文字です。正直これは直接✖️と書いても良いらしい。
エスケープ処理について理解したい方は下記のURLを参考に
https://qiita.com/n_hirai/items/df0a21d2409ee47973e5

最後に

さすがbootstrap と言ったところでしょうか。少しの記述で簡単に実装できます。
エスケープ処理についても少し理解できたので良かったです。

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

RailsにVue.jsを導入する

備忘録

今回はrailsで開発中のアプリにVue.jsを導入したので備忘録として記録します。
初めてrailsでVue.jsを使う人の助けになればと思います。
また、初心者のため間違いなどあればご指摘お願いします!
ruby 2.6.5
Rails 6.0.3.3

ステップ1webpackerのインストール

インストール済みの方はスキップして下さい
Gemgile内に記述

gem "webpacker", github: "rails/webpacker”

記述できたらbundle installしましょう。

インストールできたらターミナルで以下を実行

$ bin/rails webpacker:install

これでwebpackerのインストールが完了

ステップ2 Vue.jsのインストール

ターミナルで以下コマンドを入力

$ rails webpacker:install:vue

これでVue.jsに必要なファイルなどが作成されました。

ステップ3 Vue.jsの読み込み

app/views/layouts配下のapplication.html.erbファイルに以下を記述

 <%= javascript_pack_tag "hello_vue" %>

正常に読み込まれると写真のようにviewに表示されます。
https://gyazo.com/57e56686d2113920659f3dc9410f3a15

最後に

以上でrailsでVue.jsを導入する事ができました!
比較的に簡単に導入する事ができるのでよければ使ってみて下さい!
何か間違えなどあればご教授下さい!

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

【Rails】video_tagで動画が再生されない

Railsのvideo_tagを使用した実装

Railsで画像を表示させる時はimage_tagを使用しますが、動画を再生したい時にはvideo_tagを使用します。
ところがどっこい動画が再生されないのはどうしてなのでしょう!
ちなみにRailsの公式ドキュメントは以下です。

Rails公式ドキュメント【video_tag】

再生されない記述

<% @posts.each do |post| %>
 <%= video_tag(post.movie, size: "500x300", autoplay: true) %>
<% end %>

postsテーブルのmovieカラムからデータをとってきています。
動画サイズを500×300に指定、autoplay: trueとすることで自動再生されるはず、、、されません。

原因とコード追加

Rails公式ドキュメントを見てもらえればわかる通り、mutedというオプションがあります。
デフォルトだとmuted: folseとなっており、音声再生が行われる状態になっています。
自動で音声などが再生され始めるとユーザーにとって負担となるため、Chromeではautoplaymutedがどちらも有効になっていないと自動再生されないようです。
mutedを追加した記述が以下です。

<% @posts.each do |post| %>
 <%= video_tag(post.movie, size: "500x300", autoplay: true, muted: true) %>
<% end %>

おわりに

読んでいただきありがとうございました。
どなたかの動画再生で役に立てば幸いです。

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

[Rails]Active Storageの使用方法

はじめに

Active Storageを使うと、画像などのファイルのアップロードを簡単にするメソッドが使用できたり、画像を保存するテーブルを簡単に作成できます。

目次

  1. 画像加工ツールの導入
  2. Active Storageのインストール
  3. 画像の保存方法
  4. 画像の表示

1. 画像加工ツールの導入

まず画像加工に必要な画像変換ツールと、これをRailsから使えるようにするためのgemを導入します。
ImageMagicはコマンドラインから画像の作成やサイズ変更、保存形式などの変更ができます。
以下のコマンドを実行します。

ターミナル
brew install imagemagick

ImageMagicはgemではないのでRubyやRailsで扱うには、MiniMagickというgemを導入する必要があります。
MiniMagicはImageMagicをRubyで扱えるようにするためのgemです。
次に画像サイズの変更をするためのgemをインストールします。
ImageProcessingはMiniMagicではできない、画像サイズを調整する機能を提供するgemです。
以下のようにgemfileの一番下に追記します。
その後、コマンドでbundle installを実行します。

gemfile
gem 'mini_magick'
gem 'image_processing', '~> 1.2'

2. Active Storageのインストール

ターミナルで以下を実行します。

ターミナル
rails active_storage:install

Active Storageに関連したマイグレーションが作成されるので、続けて以下を実行します。

ターミナル
rails db:migrate

3. 画像の保存方法

Active Storageのテーブルに画像を保存するために、アソシエーションの定義とimageカラムの保存の許可を設定します。
今回はhas_one_attachedメソッドを使って、一つの画像ファイルを添付できるようにしました。

app/models/post.rb
class Post < ApplicationRecord
  ~~
  has_one_attached :image
  ~~
end

これでpostsテーブルに画像ファイルの紐付けができました。
このとき、postsテーブルにカラムを追加する必要はありません。

次にストロングパラメーターにimageを追加し、画像ファイルの保存を許可します。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  ~~
  def post_params
    params.require(:post).permit(:name, :image, :price, :evaluation, :description, :category_id, :shop_name).merge(user_id: current_user.id)
  end
  ~~
end

4. 画像の表示

Railsのヘルパーメソッドであるimage_tagメソッドを用いて画像を表示します。

<%= image_tag post.image, class: "post-img" %>

attached?メソッドを使うことで画像が存在しないときのエラーを防ぐことができます。

<%= image_tag @post.image ,class:"user-box-img" if @post.image.attached? %>

上記のように記述することで画像が存在する場合のみimage_tagが読み込まれます。

Active Storageを導入している場合、variantメソッドを用いることができます。
variantメソッドを使用することで、画像ファイルの表示サイズを指定することができます。

<%= image_tag post.image.variant(resize: '500x500'), class: "post-img" %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【rails】センテンス集

ヘルパーメソッド

ビューファイルではヘルパーメソッドを使用することができました。これまでに登場した「form_with」や「link_to」などもヘルパーメソッドであり、あらかじめ定義されている処理です。これらのヘルパーメソッドは、HTMLの要素が戻り値となっています。より詳しく言うと、文字列としてHTMLの要素が戻り値となる、ということです。

モジュール

モジュールとは、Rubyにおける「インスタンスを生成できないクラス」のようなもののことです。
下記のように定義します。

module Sample1
 class Test
 end
end

module Sample2
 class Test
 end
end

Sample1::Test
Sample2::Test

クラスとモジュールの使い分けとして、具体的なオブジェクトを生成したい場合(インスタンスを生成したい場合)はクラスを使用し、処理だけ(メソッドなど)が必要な場合はモジュールを使用するという認識で構いません。

Helper

Helperとは、ヘルパーメソッドを作成できるモジュールです。
app/helpers配下に用意されており、application_helper.rbや、各コントローラーに対応したhelperのファイルに処理を記述することでヘルパーメソッドとして使用できます。実際には〇〇Helperという形で定義されます。

module ApplicationHelper
  def sample
    # sampleというヘルパーメソッドです
  end
end

このようにApplicationHelper内へメソッドとして定義すると、下記のようにビューファイルで使用できます。

<%= sample %>

ビューファイルへRubyの処理をたくさん記述してしまった場合や、繰り返し同じ処理を記述する場合は、可読性が落ちてしまうのでヘルパーメソッドとして切り出しましょう。

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

開発を便利にしてくれるもの!

初めに

この記事ではオリジナル開発を便利にしてくれるツールを色々紹介していきます~

  1. vscodeの便利機能
  2. コンソールの出力を表形式に

1. vscodeの便利機能

zenkaku

コードの中に全角が入っているとエラーがよく起こります。
image.png

これを防ぐために、全角を灰色にしてくれる拡張ツール "Zenkaku"をインストールしましょう。vscode左の四角が4つあるアイコンから拡張ツールを入手できます。

image.png

そうすると、以下のように全角が灰色になるので、全角エラーを撲滅できるでしょう!
image.png

japanese language pack ~

英語が苦手で日本語表記にしたいという方におすすめ!
拡張機能であるjapanese language pack ~をインストールし、vscodeを再起動すればメニューなどが日本語化されているはずです。
image.png

indent-rainbow

特にhtml/cssを開発しているとき、階層構造を半角スペース4つ分(tabキー)のインデントで表すと整理されて見やすくなります。
このインデントを虹色にしてくれる拡張機能が"indent-rainbow"です。ぜひインストールしておきましょう!

image.png

auto close tag

タグの閉じ忘れや、タグが別タグとクロスしてしまうミスが起こりがち!
image.png
auto close tagはHTMLやXMLでタグを記載するときに、開始タグを打ち終わったときに自動的に終了タグを生成してくれるプラグインです。
HTMLやXMLを書いていて、特にタグの入れ子が多くなってきた時とか、終了タグを忘れてエラーになることってないですか?この Auto Close Tag を入れれば終了タグを毎回書かずに、開始タグを書いたら自動的にタグを閉じてくれます。
HTMLを書く効率が格段に上がるので、イチオシのプラグインです!
image.png

auto rename tag

さらに修正効率アップ!
auto rename tagは開始タグを修正したときに、自動的に対になる終了タグを修正してくれるプラグインです。
Auto Close Tagの親戚のようなプラグインです!。HTML/XMLの要素を修正することはよくあるかと思いますが、このプラグインは開始タグを書き換えれば終了タグも自動的に書き換えてくれるので、修正効率はとても上がるかと思います。
Auto Close Tagと併せて使うとあなたのVSCode環境は間違いなく快適になるでしょう!
image.png

Beautify

開発を行っていて、ついつい段落を変え忘れたりコードがガタガタになっても修正がめんどくさくなってしまうことはよくありますよね、、、
image.png
そんな方に!
BeautifyはJavascriptやCSS、SASS、HTMLコードを綺麗に整形してくれる拡張機能です!

ガタガタのコードもCommand+Shift+Pでコマンドパレットを開き、beautifyでコマンドを選択すれば一発で綺麗にしてくれます。
image.png

endwise

自動でendを挿入してくれる拡張機能です!これでendを忘れる心配なし!
image.png

vscode-icons

vscode-iconsを導入すればファイルアイコンが見やすくなります!
image.png

HTML CSS Support

HTMLを書くときに、CSS側で作ったClass名が候補に出るようになります。よく書き間違いでスタイル当たってなかったりしますが、これならバッチリ。

live share

共同開発の効率が上がること間違いなし!
Live Share は、使っているプログラミング言語や構築しているアプリの種類に関係なく、リアルタイムで他のユーザーと共同で編集やデバッグができます。 現在のプロジェクトを瞬時に安全に共有したり、共同でデバッグ セッションを開始したり、ターミナル インスタンスを共有したり、localhost の Web アプリを転送したり、音声通話などを行うことができます!

詳しい導入を知りたい方は下記を参考に↓
https://qiita.com/Shota_Fukuda/items/1358b8eb5e3e8354d1c7

2. コンソールの出力を表形式に

開発をしていると、データベースの中身(レコード)を確認したいときがよく来ます。コンソールを用いて確認できますが、初期状態では全レコードが横並びで非常に見づらいです。しかし、HitbというGemを利用すると、表形式で出力できるようになります。

Hirb導入前
image.png

Hirb導入後
image.png

まず、以下の二行をgemfileに記述します。

Gemfile
gem 'hirb'
gem 'hirb-unicode'

その後、ターミナルにて

$ bundle install

この時点でHirbは利用できるのですが、毎回コンソール中にHirb.enableを入力する必要があります。面倒くさいのでデフォルトでHirbを利用するように設定します。
ルートディレクトリー(appやdbと同じ階層)に.irbrcファイルを作成します。

image.png

.irbrcファイル中に以下を記述します。

if defined? Rails::Console
  if defined? Hirb
    Hirb.enable
  end
end

これにて、コンソールの出力がいつでも表形式になります!

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

【忘備録】Rails構築について

この記事は

初心者の自分がRailsを構築するにあたりメモを忘れないように記事にした。(2020/10/24時点)

Rails構築手順に関しては、下記の記事を参考に進めた。
【完全版】MacでRails環境構築する手順の全て

1.Command Line Toolsインストール時に失敗した話
 とりあえず記事通りにインストールしようとすると下記のエラーが発生した。

% xcode-select --install
xcode-select: note: install requested for command line developer tools

 その後、下記のようなポップアップが表示された。
 このソフトウェアは、現在ソフトウェア・アップデート・サーバから入手できないため、インストールできません。

 [解決策]
 ・More Downloads for Apple Developers から自分の動作環境に合うXcodeをインストール。 

 インストール後、Xcodeのバージョンを確認すると下記のエラーが発生
 原因、対応方法について調査してみる。

% xcodebuild -version
xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

 [解決策]
 ・完全版?Xcodeをインストール(自分はApp Storeからダウンロードしました)
  インストール後、以下のコマンドを入力

% sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

#↓↓Xcodeのversion確認↓↓
% xcodebuild -version
Xcode 12.1
Build version 12A7403
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RuboCop]警告を無効化する方法

RuboCopに怒られすぎて嫌になってませんか?

「ここはしょうがないだろ!」と言いながらどうにか頑張って怒られないようにする経験が誰しもがあるかと思います。
そこで、警告を無効化するための方法を紹介します。
使いすぎてもRuboCopの意味がなくなるので注意ですが、使いどころを見極めて活用してみましょう!

無効化する方法

指定範囲の全ての警告を無効化したい場合

# rubocop:disable all

〜ここに処理を記述〜

# rubocop:enable all

指定範囲の特定の警告を無効化したい場合

# rubocop:disable Style/LineLength, Style/StringLiterals

〜ここに処理を記述〜

# rubocop:enable Style/LineLength, Style/StringLiterals

単一行の全ての警告を無効化したい場合

for x in (0..19) # rubocop:disable all

単一行の特定の警告を無効化したい場合

for x in (0..19) # rubocop:disable Style/AvoidFor
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フォーム入力の結果を表示する方法

はじめて記事を書かせていただきます
プログラミングの学習をはじめて、1ヶ月の初心者です

ruby on railsでfurimaアプリを作成しています
ユーザー登録をして、商品を出品し、他のユーザーが購入すると、取引が完了する流れです

現段階の仕様では、ユーザーの出品商品の入力が終わると、topページに移動するようにしています

def create
  @item = Item.new(item_params)
  if @item.valid?
     @item.save
     return redirect_to root_path #topページに戻る
  else
    render :new
  end
end

*itemsテーブルに出品商品の情報が登録されます

ここで「商品の出品が完了しました」と表示されるページを追加し、topページへクリックで戻るようにする方法を検討しています

  • コントローラーに、パスを記述する方法でやってみる

まず、結果表示のHTML文書を作成(kanryouとする)
routesファイルに記述

resources :items  do
  resources :orders, only: [:index, :create]
  member do
    get 'kanryou'
  end
end

コントローラーに記述

def create
  @item = Item.new(item_params)
  if @item.valid?
     @item.save
     return redirect_to kanryou_item_path(@item.id)
  else
     render :new
  end
end

以上でうまくいきました

参考にさせていただきました
https://qiita.com/imayasu/items/19f43a5726ed2170f611

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

[Rails]変換・検証・検索するためのメソッドまとめ

変換系のメソッド

to_h

ハッシュに変換するメソッド。

to_a

配列に変換するメソッド。

to_s

文字列に変換するメソッド。

to_i

数値に変換するメソッド。

to_sym

シンボルを返すメソッド。

encode

文字列の文字コードを変換する。

文字列.encode('UTF-8')

gsub

特定の文字を別の文字へ置換したり、正規表現を用いて該当箇所を置換したり削除したりできる。

文字列.gsub(置換したい文字列, 置換後の文字列)

文字列.gsub(/正規表現/, 正規表現に該当した箇所を置換した後の文字列)

compact

nilの要素を取り除いた配列を新しく生成して返します。

a = [1, nil, 'abc', false]
b = a.compact
b #=> [1, 'abc', false]

join

配列要素を指定したもので区切ることができるメソッド。

p ["apple", "orange", "lemon"].join(',') #=> "apple,orange,lemon"

each_slice

ある要素数ごとに分割したい場合に使う。

[1..10].each_slice(2) do |num|
  puts num
end
#=> [1,2,3,4,5],[6,7,8,9,10]

検証系のメソッド

request.post(patch,get,delete)?

リクエストの種類がpost(patch,get,delete)かどうかを検証している。

valid?

バリデーションを実行した結果、エラーが無ければtrue、あればfalseを返す。

invalid?

バリデーションを実行した結果、エラーが無ければfalse、あればtrueを返す。

present?

値があればtrue、なければfalseを返す。

nil?

nilの場合のみtrue、それ以外はfalseを返す。

empty?

空の文字列や配列の場合にtrue、それ以外はfalseを返す。(nilに使うとエラーになる。)

blank?

nilか空の場合はtrue、それ以外はfalseを返す。

zero?

中身が0(無いか)を調べるメソッド。文字列やtrue&false以外に使える。

any?

中身が存在する際にtrueを返すメソッド。一件だけ検索するので高速処理。2つ目と3つ目は同義。

Sample.where(name: "侍1").any?
#=> true

Sample.any? do | sample |
  sample.name == "侍1"
end
#=> true

Sample.any? { | sample | sample.name == "侍1" }
#=> true

yes?

ユーザーがyesといった場合にtrueを返す。

freeze! if yes?("Should I freeze the latest Rails?")

list << '土曜日' if holiday_saturday&.yes?

検索系のメソッド

pluck

テーブルから指定したカラムの配列をデータ型で返す。

Product.pluck(:id)

map

各要素へ順に処理を実行してくれるメソッド。

配列変数.map {|変数名| 具体的な処理 }

array = ["a", "b", "c"]
array = array.map {|item|  item.upcase } #=>["A", "B", "C"]

detect

各要素をブロックで評価し「真(true)」となる要素の最初の1件を返す。findメソッド的なやつ。

array = [1, 2, 3, 1, 2, 3]
detect = array.detect {|v| v==1 } #=>1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さまざまなマイグレーション操作を使いこなす

マイグレーションの適用を理解しよう

1.「マイグレーション」ファイルを作成し、RubyでDBの構造(スキーマ)を変更するコードを記述
2.作成した「マイグレーション」ファイルをrails db:migrateコマンドを使ってデータベースに適用する(migrateする)。

$ rails db:migrate

マイグレーションファイルの更新・変更・削除はコマンドを実行しないと適用されない。

マイグレーションの上げ下げ

マイグレーションを適用する = 上げ
適用を取り消す = 下げ

schema.rb

Railsは現在のデータベース構造をdb/schema.rbに自動出力します。
マイグレーションのを適用したり外したりすると自動的に出力され、また

db:schema.rb:dump

で手動出力する事も可能

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

[Github]開発時によく使うコマンドまとめ

ブランチの作り方

$ git checkout develop

$ git checkout -b 作成するブランチ名

$ git push -u origin 作成したブランチ名

プルリクエスト作成方法

1.以下のコマンドを実行する。

$ git add .

$ git commit -m "コミット内容"

$ git push origin develop

2.右上の緑色のCompare & pull requestボタンをクリック。

3.プルリクエスト内容を記入する。

4.どのブランチからどのブランチにpull requestするかを確認する。

-例-
developブランチからsampleブランチへのpull request

sample ... develop

5.「Create pull request」ボタンを押して、Pull Requestを作成。

最新情報をローカルに反映させたい時

1.commit、pushまで終わらせた状態で以下のコマンドを実行する。

git checkout develop

git pull origin develop

git checkout 開発用ブランチ

git merge origin develop

2.もし、コンフリクトが発生した場合は、以下を実行する。

# コンフリクト内容を解消した上で、、
$ git add .

$ git commit -m "コミット内容"

$ git push origin 開発用ブランチ

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

N+1問題を甘く見るな!

はじめに

これまで「N+1問題」というものを知ってはいたものの、そこまで規模の大きい開発をしたことが無かったのであまり意識してこないまま今日まで過ごしてきました。。
しかし、実際に現場で働くと、規模が大きい開発ではデータの件数が何万件というものはザラにあるのでこれからは意識しないといけないと気付かされました(泣)
皆さんも、実際に現場入った際に必ずと言っていいほど必要な知識だと思うので是非ここで押さえておいてください!!
※ちょくちょくSQL用語が出てきますが、そこは理解している程で進めさせていただきます。

まず、N+1問題とは?

簡単に言えば、ループ処理の中で都度SQLを発行してしまい、大量(必要以上)のSQLが発行されてパフォーマンスが低下してしまう問題のことです。
一つ例えを出しましょう。
AさんというUserのProduct(商品)を10件取得して、Aさんの商品一覧ページを表示したい時には、、

AさんのUserデータを取得するために1回
10件のProductsデータを取得するために10回
の合計11回のクエリを発行し、表示したいデータを取得することになります。

11件ほどでは大したことはないのですが、これが10000件や20000件だと大変です。
1回のクエリで0.001秒の時間がかかるとして、10000件とか20000件だと10秒や20秒もレスポンスに時間がかかってしまいます。ここまで待たされるのは面倒ですよね?
この問題はコードの書き方次第で解決することが出来て、
適切に書けば、仮に10000件のProductsであっても、2回のクエリで取得できるんです(笑)

問題解決するためのメソッドたち

さて、この問題を解決するためのやり方を見ていきましょう。
基本的には以下で紹介する4つのメソッドを活用していけば良いです。使いどころはそれぞれ少し違ったりするので使い分けられるように出来るようにしましょう。

メソッド キャッシュ クエリ 用途 データ参照
joins しない INNER JOIN 絞り込み できる
eager_load する LEFT JOIN キャッシュと絞り込み できる
preload する JOIN せずそれぞれSELECT キャッシュ できない
includes する 場合による キャッシュ、必要なら絞り込み できる

joins

デフォルトでINNER JOINを行う。LEFT OUTER JOINを行いたい時はleft_joinsを使う。
このメソッドは、キャッシュしないのでメモリを必要最低限に抑えることが出来ます。
また、JOINした先のデータを参照せず、絞り込み結果だけが必要な場合などに使えば良いです。

User.joins(:products).where(products: { id: 1 })
# SELECT `users`.* FROM `users` INNER JOIN `products` ON `products`.`user_id` = `users`.`id` WHERE `products`.`id` = 1

eager_load

指定したassociationをLEFT OUTER JOINで引いてキャッシュする。
クエリが1つで済むので高速処理が可能です。
1対1あるいはN対1のアソシエーションをJOINする場合(belongs_to, has_one)や、JOINした先のテーブルの情報を参照したい場合(Whereによる絞り込みなど)の際に使えば良いです。

User.eager_load(:products)
# SELECT `users`.`id`, `users`.`name`, `users`.`created_at`, `users`.`updated_at`, `products`.`id`, `products`.`user_id`, `products`.`created_at`, `products`.`updated_at` FROM `users` LEFT OUTER JOIN `products` ON `products`.`user_id` = `users`.`id`

preload

指定したassociationを複数のクエリに分けて引いてキャッシュする。
しかし、アソシエーション先のデータ参照(Whereによる絞り込みなど)は出来ません。
多対多のアソシエーションの時に使ったら良いです。注意点としては、データ量が大きいと、IN句が大きくなりがちで、メモリを圧迫する可能性があるので注意する。

User.preload(:products)
# SELECT `users`.* FROM `users`
# SELECT `products`.* FROM `products` WHERE `products`.`user_id` IN (1, 2, 3, ...)

includes

簡単に言うと、eager_loadとpreloadを使い分けてくれます。
しかし、includesは利用しない方が良いっぽいです。
なぜなら、includesは、preloadとeager_loadをよしなに振り分けるので。preloadと、eager_loadの特徴を理解していれば、includesが登場する場面は、ほとんどないと思います。データが少ないうちはincludesしていても問題にならないかもしれませんが、データが増えてきたときにジワジワと問題が顕在化してくるので、includesの挙動も正しく知っておきましょう。

User.includes(:products)
# SELECT `users`.* FROM `users`
# SELECT `products`.* FROM `products` WHERE `products`.`user_id` IN (1, 2, 3, ...)

Bullet gemを使いこなしN+1問題を早期発見する

どれだけ気を付けていても抜けが出てしまうのが人間です。
そこをカバーしてくれるgemがBulletです。

Bulletの使い方

1.Gemfileに以下を追記する。

group :optimization do
  gem 'bullet', '~> 6.1.0'
end

2.実行環境をoptimizationとして、development.rbをコピーして、以下を記述する。

config/enviroments/optomization.rb

config.after_initialize do
  # 最適化することを許可している。
  Bullet.enable = true

  # 問題点をJSのAlertで表示することを許可している。
  Bullet.alert = true

  # ログをファイルにとることを許可している。
  Bullet.bullet_logger = true

  # ブラウザのコンソールにN+1問題を表示することを許可している。
  Bullet.console = true

  # BulletがRailsのログに記録することを許可している。
  Bullet.rails_logger = true

  # 問題点をフッターに表示することを許可している。
  Bullet.add_footer = true
end

3.webpacker.ymlに、以下を追記する。

webpacker.yml
optimization:
  <<: *development

4.実行してN+1問題をチェックする。

$ bundle exec rails server -e optimization

テスト環境でのBulletの設定方法

1.test.rbに以下を追記する。

config/enviroments/test.rb
config.after_initialize do
  Bullet.enable = true
  Bullet.bullet_logger = true
  Bullet.raise = false
end

2.spec_helper.rbに以下を追記する。

spec/spec_helper.rb
if Bullet.enable?
  config.before(:each) do
    Bullet.start_request
  end

  config.after(:each) do
    Bullet.perform_out_of_channel_notifications if Bullet.notification?
    Bullet.end_request
  end
end

終わりに

ここまでN+1問題についてどのように対応していけばいいかを見てきました。
少しの意識を変えるだけでかなりレスポンスが早くなると思います!
では、今この時から意識して開発していきましょう!!!

参考

Ruby on Railsのコードに潜むN+1クエリ問題をBullet gemで発見して、Railsサイトのレスポンスを最適化

Bullet をテスト環境で実行する

ActiveRecordのincludesは使わずにpreloadとeager_loadを使い分ける理由

ActiveRecordのincludes, preload, eager_load の個人的な使い分け

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

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

Rails 投稿とユーザーの紐付け

はじめに

今、メモアプリを開発をしています。
全てのメモの一覧が表示されてしまうのですが、どうにか
メモ一覧ページにログインユーザーのメモのみの一覧を表示したいです。

今回はそれについて学習していきます。

メモとユーザーの紐付け

まず、各投稿が誰がしたのかを判別するためにnotesテーブル(投稿系のテーブル)に、user_idというカラムをたす。

class CreateNotes < ActiveRecord::Migration[5.2]
  def change
    create_table :notes do |t|
      t.text :title
      t.integer :user_id
      t.integer :category_id
      t.text :explanation

      t.timestamps
    end
  end
end
$ rails db:migrate:reset

メモに投稿したユーザーのidを保存

投稿を保存する際に、先ほど追加したuser_idカラムにも情報を入れる。
buildメソッドについて

notes.controller.rb
def create
    @note = current_user.notes.build(note_params)
    @note.save
    redirect_to notes_path
  end

メモ一覧ページにユーザーの投稿をのみを表示する。

 <div class='container'>
   <div class='row'>
    <h2>メモ一覧</h2>
    <table class='table'>
      <thead>
        <tr>
          <th>タイトル</th>
          <th>カテゴリー</th>
        </tr>
      </thead>
      <tbody>

        <% @notes.each do |note| %>
        <% if user_signed_in? && current_user.id == note.user_id %>   #ここを追加
        <tr>
          <td>
            <%= link_to note_path(note) do %>
              <%= note.title %>
            <% end %>
          </td>
          <td><%= note.category.name %></td>
        </tr>
        <% end %>
        <% end %>
      </tbody>
    </table>
   </div>
 </div>

メモ一覧のeach文(繰り返し)の中に記述。

<% if user_signed_in? && current_user.id == note.user_id %> 

この記述でログインしているユーザなのか?とログインユーザーのidとメモを投稿したユーザのidが同じか見ている。

trueなら表示される!
これで大丈夫だと思います!

最後に

説明が分かりにくいと思いますが何かの参考になれば幸いです。
また、間違っているところがあればご教授いただけるとありがたいです。

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

Ruby buildメソッド

buildメソッド

親モデルに属する子モデルのインスタンスを新たに生成したい場合に使うメソッド。
(親モデルと子モデルは、アソシエーション設定あり)
外部キーに値が入った状態でインスタンスが生成できる。

ユーザーに紐づくメモインスタンスを生成したい場合
※UserモデルとNoteモデルは、1対多のアソシエーションを設定しているとします。

note.controller.rb
 @note = current_user.notes.build(note_params)  ## 親モデル.子モデル.buildという形式

生成されるコメントインスタンスの中身
*pry-byebugで@noteインスタンスの中身を確認

 @note
 id: nil,
 title: "aaa",
 user_id: 2,
 category_id: 1,
 explanation: "ppppp",
 created_at: nil,
 updated_at: nil 

user_idに値が入っています。

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

【Ruby on Rails】Formオブジェクト(ActiveModel)のエラーメッセージを日本語化する方法

本記事の概要

・Formオブジェクトのバリデーションエラーが日本語にならない問題に悩んだので、備忘録として残しておきます。
・+α余談としてActiveRecordとActiveModelの違いについて少し触れました。

開発環境

Mac OS Catalina 10.15.4
ruby 2.6系
rails 6.0系

前提条件

gem 'rails-i18n'を導入してデフォルトの言語設定はすでに日本語にしています。
また、configのlocalsディレクトリにja.ymlファイルを作成済みです。

エラーメッセージを日本語化できなかった原因とその対策

結論から言うと、今回作成したFormオブジェクトはinclude ActiveModel:Modelによってactivemodelを継承したモデルであったにも関わらず
activerecordを継承したモデルとして日本語化を行おうとしたのが問題だったよう。

<誤っていたコードの記述>

ja.yml
ja:
  activerecord:
    models:
      detail: 詳細情報
      posts_tag: 投稿 
    attributes:
      user:
        name: 名前
      detail:
        age: 年齢
        pr: PR
        area_id: 居住地
        occupation_id: 職業
        interest_id: 興味
      posts_tag: 
        title: タイトル
        content: 概要
        name: タグ

<正しいコードの記述>

ja.yml
ja:
  activerecord:
    models:
      detail: 詳細情報
    attributes:
      user:
        name: 名前
      detail:
        age: 年齢
        pr: PR
        area_id: 居住地
        occupation_id: 職業
        interest_id: 興味

  activemodel:
    models:
      posts_tag: 投稿 
    attributes:
      posts_tag: 
        title: タイトル
        content: 概要
        name: タグ

余談

ちなみに、rails g modelで生成するデフォルトモデルが継承しているActiveRecordとActiveModelの違いは、DBとのやり取りが可能か否かにあります。

前者は、可能なのでモデル名.findなどですでに保存してあるデータを引っ張ってこられますが、後者のActiveModelは通常findメソッドは使えません。

他にも、バリデーションにおける`validates uniqueness: true等も、DBに保存されているデータを参照して一意性かどうか判断するので、ActiveModelで記述することはできません。

そんな感じで便利な一方、割とActiveModelはややこしいですね...
なにはともあれ、最後までお読み頂きありがとうございました!

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

Railsのbundle installで見かけないエラーが…それに対する解決法

はじめに

最近、講義やゼミでの研究も相まってAWS Cloud9でrailsを使う機会が増えている。
そんな中で初めて見たエラーだった。ネットで検索してもいまいち解決法が分かりにくかったので、ここにメモがてら残そうと思う

本題

では、どんなエラーが出たのか。以下の様なエラーだ。

$ budle install
There was an error while trying to write to
`/tmp/bundler-compact-index-xxxxxxxxx(date)-xxxxxx-xxxxxxx/versions`. There was insufficient space
remaining on the device.

ほほう…何だろう。最後の一文を見るとデバイスの容量が足りないという事みたいです。
という事は・・・デバイスのボリューム自体を増やす。もしくは使いそうにないファイルの中身を消去して、空きを作るなどが考えられます。では、前者を解決策1、後者を解決策2としたいと思います。

解決策1

これは、AWS Cloud9のボリュームを増やすという事です。記事がすでに上がっているので割愛したいと思います。

解決策2

では、何処のファイルの中身を消去しようとなりますが、私は/var/logを使う事が今回は無さそうなのでそちらを消去することにしました。その際に使用したコマンドが以下です。

$ sudo find /var/log/ -type f -name \* -exec cp -f /dev/null {} \;

最後に

という訳で、2つの解決策を提示しました。これで、容量が足りるようになると思うので、再度bundle installして貰えれば大丈夫です。
恐らくもっと良いやり方もあると思います。最後までお読みいただきありがとうございました。

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

ActiveRecord::StatementInvalid (SQLite3::SQLException: no such table: main.follows~

フォロー機能を実装するためにUserモデルと中間テーブルのRelationshipモデルを作成。

model/user.rb
.
.
  def follow(other_user)
    unless self == other_user
      self.relationships.find_or_create_by(follow_id: other_user.id)
    end
  end

こんな感じでfollowメソッドを作り、relation_controllerの該当アクション内で呼び出したところ、タイトルのエラーが出てきた。

テーブルがないと言われているけど必要なテーブルがあることはschemaファイルからも確認できたので混乱。

原因

原因としてはrelationshipsテーブルを作るときのmigrationファイルに問題があった

db/migrate/time_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: true

      t.timestamps

    end
    add_index :relationships, [:user_id, :follow_id], unique: true
  end
end

こちらのfollow_idカラムを作成する部分、t.references :follow, foreign_key: trueのforeign_keyがtrueとなっている部分が原因でした。
foreign_keyがtrueになっているとid名と同じテーブルが存在するか、つまりありもしないfollowテーブルがあるかを確認してしまうため、エラーが吐き出されていたようです。

解決法としては該当部分に

db/migrate/time_create_relationships.rb
 t.references :follow,  foreign_key: { to_table: :users }

と参照するテーブルを記述することで正しく動作しました。

rails g model relationships follow:referencesとターミナルで自動作成されたマイグレーションファイルはデフォルトでforeign_key:trueとなるので気を付けたいところです。

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