20210127のRailsに関する記事は26件です。

【環境構築】Docker rails 備忘録

Docker使ってrailsでアプリを作成した際の備忘録

手順1

ファイル作成
既にDockerをインストールしているので
使用しているディレクトリの配下に任意の名前でディレクトリを作成し
以下作成したディレクトリにファイル準備。
・Gemfile
・Gemfile.lock
・Dockerfile
・docker-compose.yml
※ターミナルでのファイル作成コマンド:touch XXXXXX(※XXXに作成するファイル名)

手順2

作成したファイルの内容を編集

Gemfile
source 'https://rubygems.org'
gem 'rails', 5.2.2
Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /sample
WORKDIR /sample

COPY Gemfile /sample/Gemfile
COPY Gemfile.lock /sample/Gemfile.lock

RUN bundle install
COPY . /sample
docker-compose.yml
version: '3'

services:
    db:
        image: mysql:5.7
        environment:
            MYSQL_USER: root
            MYSQL_ROOT_PASSWORD: password
        ports:
            - "3306:3306"
        volumes:
            - ./db/mysql/volumes:/var/lib/mysql

    web:
        build: .
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        volumes:
            - .:/sample
            - gem_data:/usr/local/bundle
        ports:
            - 3000:3000
        depends_on:
            - db 
        tty: true
        stdin_open: true

volumes:
  gem_data:

手順3

Dockerコマンドを実行し、railsアプリケーションを作成
※Dockerを起動していない場合は次のコマンドを実行:
docker run -d -p 80:80 docker/getting-started

・rails new コマンド(DBにmysqlを指定)
docker-compose run web rails new . --force --database=mysql --skip-bundle

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

【Ruby】文字列の中に式や変数を埋め込みたい

文字列の中に式を埋め込みたい時

変数の値などを含む文字列を表示したい時、
Rubyでは文字列中に#{}で囲んだ式を埋め込むとスクリプト実行時にその式の評価した結果に置き換えられます。

sample = "sample_str"

message = "#{sample}さん、おはよう!" # => "sample_strさん、、おはよう!"

また数値(int)を代入した場合は計算結果に置き換えられます。

int1 = 5
int2 = 2

message = "5+2 = #{int1 + int2} です!" # => "5+2 = 7 です!"

まとめ

Rubyの「式展開」についてまとめました。
簡単ですけどよく使います。

Ruby 2.7.0 リファレンスマニュアル
https://docs.ruby-lang.org/ja/2.7.0/doc/spec=2fliteral.html#exp

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

ActiveModel::Serializer のインストール方法

ActiveModel::Serializer ( Gem ) のインストールの仕方を簡単に書いておきます!

ActiveModel::Serializer ( Gem ) のインストール その①

Gemfile に以下のコードを記述する。

Gemfile
gem 'active_model_serializers'

その後、ターミナル上で bundle install を実行。

terminal
bundle install

ActiveModel::Serializer ( Gem ) のインストール その②

ターミナル上で以下のコマンドを実行し、
Gemfile に gem 'active_model_serializers' を追記。

terminal
echo "gem 'devise_token_auth'" >> Gemfile

その後、ターミナル上で bundle install。

terminal
bundle install

まとめ

どちらの方法でもインストール可能です。

本日、Qiita に初投稿してみました!とても簡単な内容かもしれませんが、自分が検索したときにパッと出てこなかったのと、Markdown の書き方の勉強を兼ねて投稿してみました!

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

ActiveModel::Serializer ( Gem ) のインストール方法

ActiveModel::Serializer ( Gem ) のインストールの仕方を簡単に書いておきます!

ActiveModel::Serializer ( Gem ) のインストール その①

Gemfile に以下のコードを記述する。

Gemfile
gem 'active_model_serializers'

その後、ターミナル上で bundle install を実行。

terminal
bundle install

ActiveModel::Serializer ( Gem ) のインストール その②

ターミナル上で以下のコマンドを実行し、
Gemfile に gem 'active_model_serializers' を追記。

terminal
echo "gem 'devise_token_auth'" >> Gemfile

その後、ターミナル上で bundle install。

terminal
bundle install

まとめ

どちらの方法でもインストール可能です。

本日、Qiita に初投稿してみました!とても簡単な内容かもしれませんが、自分が検索したときにパッと出てこなかったのと、Markdown の書き方の勉強を兼ねて投稿してみました!

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

Ruby on Rails,AWS, Docker, CircleCIでポートフォリオを作成してみた

初めに

今回はRuby on Railsをメインに、AWS,Docker,CircleCIなど近年人気の高まっているインフラ技術を使用してポートフォリオを作成してみました。
本記事では、実装した機能や苦労した点、参考にした記事などを紹介していきたいと思います。
皆様のポートフォリオ作りに少しでもお役に立てれば幸いです。
今後、ポートフォリオをアップデートした際にはこちらの記事も随時アップデートしていきます。(2021年1月26日時点)

アプリの概要

レシピと料理に使った材料の投稿ができるアプリです。
レシピに乗っているあの材料はどこで手に入るんだろう??
料理初心者の誰もが感じる疑問を解消すべく、レシピ投稿機能のほか、料理に使用した材料の購入場所を投稿できるアプリを製作しました。

URLは下記になります、よかったら見て行ってください!
URL: https://www.cooknavi.xyz/
(レスポンシブデザインにも対応しておりますので、スマートフォンからの閲覧も可能です!)

GitHub: https://github.com/yutatsune/cooknavi

主な使用イメージ

トップページからゲストログイン(閲覧用)をクリック
「レシピを見てみる」または「材料を探してみる」をクリックし、一覧画面へ遷移します
cooknavi1
ログインが完了したら、レシピまたは材料の一覧画面の右下から新規投稿ができます
必要な項目を入力して、投稿するをクリック
cooknavi2
材料投稿画面にて、郵便番号を入力すると自動で住所が入力されます
cooknavi3
材料投稿画面で入力した住所の経度・緯度を自動で取得して、GoogleMapに表示されます
cooknavi4
投稿詳細画面でいいねをすることができます
cooknavi5

使用技術

  • フロントエンド
    • jQuery 1.12.4
    • HTML/CSS/Haml/Sass
  • バックエンド
    • ruby 2.6.5
    • Ruby on Rails 6.0.0
    • Google Maps API
  • インフラ
    • CircleCI
    • Docker 19.03.13/docker-compose 1.27.4
    • nginx 1.12.2
    • mysql 5.7.31
    • AWS ( EC2, ALB, ACM, RDS, Route53, VPC, IAM )
  • その他使用ツール
    • Visual Studio Code
    • draw.io

インフラ構成図

aws

VPCの構築、EC2へのデプロイ、RDSの導入、ELBの設定、ドメイン取得からのRoute53の設定、ACMでのSSL証明書の取得など基本的な部分は導入できました
今後は、S3や自動デプロイを導入していきたいです!

機能一覧

機能 概要
ユーザー管理機能 新規登録・ログイン・ログアウトできます
簡単ログイン機能 トップ画面のゲストログイン(閲覧用)をクリックすることで、簡単にログインできます
投稿機能 レシピまたは材料を画像を5枚までつけて投稿できます
投稿詳細表示機能 各投稿画面が詳細ページで見ることができます
投稿編集・削除機能 投稿者本人のみ投稿編集・削除できます
ユーザー一覧表示機能 登録したユーザーの一覧を見ることができます
ユーザー情報編集機能 ログイン中のユーザーでアカウント本人であればプロフィール編集できます
フォロー機能 ユーザー一覧画面から各ユーザーのフォローできます
フォロー一覧表示機能 フォローしているまたはフォローされているユーザーを見ることができます
いいね機能 投稿詳細ページからいいねすることできます
いいね一覧機能 いいねした投稿の一覧を見ることができます
コメント機能 投稿詳細ページから非同期通信でコメントできます
住所自動入力機能 材料投稿画面にて、郵便番号を入力するだけで住所が自動で入力されます
マップ表示機能 材料投稿画面で入力した住所の経度・緯度を自動で取得し、マップで表示することができます
レスポンシブデザイン スマートフォン向けの表示に対応

工夫した点

とにかく、単なるレシピ投稿アプリにならないように下記を頑張って実装しました!

  • 料理に使った材料の投稿機能
  • 住所自動入力機能
  • GoogleMapsAPI連携

レシピを投稿したユーザーが、使った材料をどこで買ったかも投稿できるようにしました
通販でいいんじゃ。。。というツッコミはなしでお願いします笑
実際にお店で色々見たりする楽しみだってありますから!

今後、新しい機能を思いついたら実装してみたいとおもいます

DB設計

database

各テーブルについて

テーブル名 説明
users 登録ユーザー情報
relationships フォロー・フォロワーのユーザー情報
recipes レシピの投稿情報
images レシピの投稿画像情報
comments 投稿レシピへのコメント情報
recipe_likes 投稿レシピへのいいね情報
materials 材料の投稿情報
material_images 材料の投稿画像情報
material_comments 投稿材料へのコメント情報
material_likes 投稿材料へのいいね情報

苦労した点

フロントエンド

  • デザインについて

まず、デザインをどうすれば良いかで悩みました!
最終的には、フルスクリーンレイアウトやタイル型レイアウト等に落ち着きました

  • JavaScript(jQuely)全般

スライドメニューやGoogleMapの表示などが大変でした!
これからも勉強を続けて、もっとカッコよく実装できるようになりたいです

バックエンド

  • いいね・フォロー機能のアソシエーション

投稿機能など、初めのうちに学習した機能とは違い、アソシエーションの組み方が独特でした
しかし、一度理解してしまえばあとはスムーズに実装できました

  • GoogleMapsAPI連携

これがとにかく苦労しました
GoogleMapが表示されない、JavaScriptがRubyの値を参照するにはどうするかなど課題がありました
これは、gonというgemを使うことによって解決できました!
最終的に実装できて本当によかったです!

  • RSpecによるテスト

意外と盲点だったのがこちら
書き方が独特で新たに覚えなければいけないことが多いです
こちらは現在進行中で更新中です!

AWS

  • EC2インスタンス内の環境構築

Rails6以降はYarnが必須であることがわかっていなくて、プリコンパイルのところで2日くらい詰まりました。。。
皆さんはお気をつけを!

  • RDS設定

環境構築が終わった後にいざ試しに登録してみるとエラーが。。。
原因は日本語が登録できない設定になっていたことでした
これを直すには、パラメーターグループの設定を変更すれば解決するはずです
【AWS】RDSで作成したMySQLのDBに日本語が保存できないを解決 | Rails on Docker

Docker

  • 環境構築

Dockerはやはりこれに尽きると思います。
dockerファイルの記載やdocker-compose.ymlの設定など覚えることがたくさんありました
あと、Rails6以降はYarnとwebpackerのインストールが必須となります
【Rails6】Docker+Rails6+puma+nginx+mysql【環境構築*初心者必見】

CircleCI

  • 導入とyml設定

まず、GitHubとの連携の仕方がよく分からなくて苦戦しました
原因はymlファイルをgitのmasterにプッシュしてないからでした。。。
【CircleCI】CircleCI 2.0からはじめる個人での簡単なCI導入方法 - githubとの連携まで
今ではなんとか自動テストまでは導入できました
今後は自動デプロイも試していきたいです

参考にした学習教材・記事

とにかく参考になりそうなものは色々と試していきました!
ここでは使ってみてよかったものを紹介していきます

UI/UX

ポートフォリオのデザインに悩んだら読んでみてください!
HTMLやCSSの基礎の他、フルスクリーンレイアウトやタイル型レイアウト、レスポンシブデザインなどとても参考になりました

スライドメニューの作り方や画像のスライドショーなど、実践的に使えるテクニックが紹介されていました
jQueryを使うなら読んでみて損はないかと思います!

Ruby on Rails

AWS

こちらの記事がすごく分かり易かったです!
画像が豊富に使われていて、初めてAWSを使う人でも問題なくできます
手順通りに行うだけで、VPC,EC2,RDS,Route53,ACMなど基本的な環境が構築可能

VPCの構築やEC2の立ち上げ方など、基礎的なことを一通り学ぶことができます
まず最初にこちらを読んでみると良いのではないでしょうか

Docker/docker-compose

まず、学習の手始めとしてこちらを学習しました
Dockerとは何か?というところからdocker-composeの構築の仕方まで学ぶことができます

私はまだ受けていないのですが、こちらの講座がわかりやすいと評判なので紹介させていただきます

CircleCI

今後の課題

  • Vue.jsなどの導入
  • ECSによるコンテナデプロイ
  • CircleCIの自動デプロイ(CDパイプラインの構築)
  • テストコードの充実
  • いいね・フォロー機能のajax化
  • さらなる機能の拡張

など、まだまだできるところはあると感じました!
今後の転職活動の状況と相談しながら引き続き改善していきたいと思います

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

Windows10(WSL2)でRails/Dockerの開発環境を構築したい

まず、Windows10でWSL2を使えるようにする。

公式ドキュメント通りに進める

私はUbuntuを入れました。(Ubuntu 20.04 LTSで大丈夫です。)

Docker EngineとDocker-ComposeをUbuntuにインストールする。

Docker Engineを先に入れてから、Docker-composeを入れる。

理由:On Linux systems, first install the Docker Engine for your OS as described on the Get Docker page, then come back here for instructions on installing Compose on Linux systems.

開発ディレクトリについて(重要)

WSL2 環境において開発ディレクトリをどこに置くかは重要です。

開発ディレクトリを Windowsファイルシステム側(Linuxパス: /mnt/c/...)に置いた場合、ファイル IO が異常に遅く、一部 Docker 環境ではネットワーク通信に不具合が発生するなどの問題が起こります。
そのため、基本的には \wsl$\Ubuntu-20.04\home<ユーザ名>(Linuxパス: /home/<ユーザ名>)など、Linuxファイルシステム側に開発ディレクトリを置く必要があります。
開発ディレクトリがLinuxファイルシステム側に置いてあれば、Dockerプロジェクトも安定・軽快に動かすことができます。(少なくとも今のところは)

Docker EngineをUbuntuに入れる

エラーが出た場合

~$ sudo docker run hello-world
docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?.
See 'docker run --help'.

私はsudo service docker startを使うと動きました。(参考)

Docker-ComposeをUbuntuに入れる

Docker-composeを使用して、Railsをインストールする。

Docker社のクイックスタートを参考にして入れる

エラーが出た場合

W: Failed to fetch http://deb.debian.org/debian/dists/buster/InRelease  Temporary failure resolving 'deb.debian.org'
W: Failed to fetch http://security.debian.org/debian-security/dists/buster/updates/InRelease  Temporary failure resolving 'security.debian.org'
W: Failed to fetch http://deb.debian.org/debian/dists/buster-updates/InRelease  Temporary failure resolving 'deb.debian.org'
W: Some index files failed to download. They have been ignored, or old ones used instead.
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package nodejs
E: Unable to locate package postgresql-client
ERROR: Service 'web' failed to build : The command '/bin/sh -c apt-get update -qq && apt-get install -y nodejs postgresql-client' returned a non-zero code: 100

どうやらコンテナのDNS設定によるものらしい。

私は、この通りに/etc/wsl.confと/etc/resolve.confを編集したら動きました!!

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

rails - devise基礎

deviseの導入・実装手順

初投稿です。deviseを使った簡単なユーザー管理機能実装についてアウトプットもかねて自分用にまとめてみます。


まずは実装の流れになります。

1.Gemインストール
2.devise設定ファイル作成
3.Userモデル作成
4.テーブル作成
5.ビュー作成、編集
6.コントローラーでストロングパラメータ設定


1.Gemインストール

Gemfile,ターミナル
gem 'devise'
bundle install
rails s

2.devise設定ファイル作成

ターミナル
rails g devise:install

3.Userモデル作成

ターミナル
rails g devise user
config/routes.rb
devise_for :users #自動で挿入される

4.テーブル作成

db/migrate/20XXXXXXXXX_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :name,               null: false #カラム追加
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

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

      ## Rememberable
      t.datetime :remember_created_at

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

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

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


      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end
ターミナル
rails db:migrate
rails s

5.ビュー作成

ターミナル
rails g devise:views

6.コントローラーでストロングパラメータ設定

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

  private
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])
  end
end

実装の基本的な部分は以上です。


devise導入時のエラー

rails s ができない <`const_get': uninitialized constant User (NameError)>

rails g devise:installをしていないのにrails g devise userを行っていた

ターミナル
rails destroy model user #同時にマイグレーションファイルも削除しておく
rails g devise:install #ここからやり直し
rails g devise user
rails db:migrate

これで一応解決できました。

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

deviseを利用したログイン機能の実装

目次

①deviseとは
②deviseのインストールの仕方
③モデルの作成方法
④テーブルの作成方法
⑤ビューファイルの作成
⑥deviseで使えるメソッド

①deviseとは

Railsで作成したアプリケーションへ簡単に認証機能を実装することができるgemのひとつ。
こちらを使用すると、ログインやサインアップなどのログイン機能を1から構築することが簡単に実装することができる。

②deviseのインストールの仕方

Gemfileに以下のように記述する

Gemfile
   gem 'devise'

その後に、gemをインストールをする

ターミナル
   bundle install

次に、設定関連に使用するファイルを自動で生成する

ターミナル
   rails g devise:install

③モデルの作成方法

deviseを用いてuserモデルを作成する

ターミナル
   rails g devise user

④テーブルの作成方法

rails g devise userで生成されたマイグレーションファイルが存在するか確認し、確認できたら

ターミナル
   rails db:migrate

⑤ビューファイルの作成

ターミナル
   rails g devise:views

上記のコマンドを実行すると,app/views/deviseの配下に以下のディレクトリが作成される

◯sessions:ログイン画面
◯registrations:ユーザ登録画面とアカウント編集画面
◯confirmations:認証メール再送信するための画面
◯passwords:パスワードを変更するための画面
◯unlocks:アカウント凍結画面
◯shared:Deviseの画面に遷移させるためのリンク

⑥deviseで使えるメソッド

メソッド 意味
before_action :authenticate_user! コントローラーに設定して、ログイン済ユーザーのみにアクセスを許可する
user_signed_in? ユーザーがサインイン済かどうかを判定する
current_user サインインしているユーザーを取得する
user_session ユーザーのセッション情報にアクセスする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オブジェクト思考におけるクラスの概念(Ruby)

クラスとインスタンス

 クラスは、インスタンスを作成する時に「元となる設計図」のことで、インスタンスは、設計図をもとに作成される実物体のことをいいます。

用語 役割 例1 例2
クラス 設計図 車の設計図 たい焼きの型
インスタンス 設計図をもとに作成される実物体 黒い車、赤い車 クリームのたい焼き、あんこのたい焼き

 Rubyは、「もの」を組み立てるように表現してコンピュータに指示するオブジェクト指向プログラミング言語です。「もの」を組み立てる際の設計図が「クラス」で、その設計図から作成された実物体のことを「インスタンス」と呼びます。

オブジェクト指向プログラミング言語

 プログラミングとは、コンピュータが理解できる言葉を並べてプログラムを作ることです。その際に、プログラムの意図したことを順番通りにコンピュータに動作させる指示を出しています。
 プログラムの意図したことを順番通りに表現する代表的な方法は以下の通りです。

プログラムの表現方法 プログラムの流れ
オブジェクト指向プログラミング 「もの」を組み立てる様に表現してコンピュータに指示する
手続き型プログラミング プログラムを上から順番に処理を実行する
関数型プログラミング 関数の組み合わせによってプログラムを組み立てる様に表現する

 このプログラムの表現方法の中で、Rubyは、オブジェクト指向プログラミング言語です。
 物を組み立てコンピュータを動作させるために、物の設計図であるクラスと物の実物体であるオブジェクトが必要です。

ポイント

①Rubyはオブジェクト指向プログラミング言語です。
②オブジェクト指向とは、「もの」を組み立てるように表現してコンピュータに指示するプログラムの表現方法
③物を組み立てプログラムを表現するために、物の設計図であるクラスと設計図から作成される実物体のインスタンスが必要です。

メソッドの特徴を理解しよう

 メソッドとは、複数の処理を1つにまとめて、扱いやすくしたものです。
 メソッドの基本的な書き方は、以下の通りになリます。

def メソッド名
  # 処理
end

# メソッド実行
メソッド名

 例)helloメソッドを定義して実行する

def hello
  puts "hello!"
end

# helloメソッド実行
hello
# => hello!

メソッドの特徴

 メソッドは下記のような特徴があります。
①メソッドに値を渡すことができる
②メソッド内で最後に評価される値を返す

def hello(name)
  puts "#{name},hello!"  ここがメソッド内で最後に評価されている
end

# helloメソッド実行
hello("kaito")
# => kaito,hello!

③メソッド内で定義した変数は、外部からアクセスすることができない

# メソッド内に変数を追加
def hello
  other_name = kaito
  puts "hello!"
end

# メソッド内の変数のather_nameをメソッド外から指定するとエラーが出る
puts other_name
# => NameError: undefind localvariable or method 'other_name' for main:object

クラスの作り方

 「たい焼きのイメージ」と「メソッドの特徴」を踏まえた上でクラスを作成します。

クラス作成

 クラスの定義は、以下のように記述します。

class クラス名
  # メソッドなど
end

 ここで重要なのは、メソッドの時と違いクラス名の最初の文字が大文字ということです。

class Taiyaki

  def メソッド
  end

end

インスタンス作成

 インスタンスは、以下のようにクラスから作成します。

クラス名.new

 たい焼きの型からたい焼きを作り出すには、以下のように記述します。

class Taiyaki

  def メソッド
  end

end

# たい焼きの設計図からたい焼きを作成
taiyaki = Taiyaki.new
# => <Taiyaki:0x00~>

 Taiyaki.newの返り値が、「Taiyaki:0x00~」なので、たい焼きのインスタンス(オブジェクト)が作成されたことがわかります。

initializeメソッドとインスタンス変数

 たい焼きを作ることができましたが、このままではたい焼きの味(taste)や値段(price)がわかりません。たい焼きに情報を持たせるためにインスタンス作成時に、たい焼きの情報を引数に渡します。

渡したいたい焼きの情報 インスタンス作成
あんこ味のたい焼き 250円 Taiyaki.new("あんこ", 250)
カスタード味のたい焼き 300円 Taiyaki.new("カスタード", 300)
抹茶味のたい焼き 330円 Taiyaki.new("抹茶", 330)

 引数で渡された値は、「インスタンス作成時」に実行されるinitializeメソッドを利用して「@のついたインスタンス変数」に代入します。

class Taiyaki

  # インスタンス作成の時に実行される
  def initialize(taste, price)
    # インスタンス変数には@をつける
    @taste = taste
    @price = price
  end

end

# newした際にinitializeメソッドが実行され、引数の値がインスタンス変数に代入される
anko_taiyaki = Taiyaki.new("あんこ", 250)
# => <Taiyaki:0x00~ @taste = "あんこ", @price = 250>

 インスタンスを作成するときに、initializeメソッド内でインスタンス変数に引数で渡した値を代入させることによって、インスタンス変数は、インスタンスごとに値を保持することができます。
 たい焼きを作りときは、それぞれのたい焼きごとに味や値段を変えることができます。

インスタンス作成 インスタンス変数の値の中身
Taiyaki.new("あんこ", 250) @taste="あんこ", @price=250
Taiyaki.new("カスタード", 300) @taste="カスタード", @price=300
Taiyaki.new("抹茶", 330) @taste="抹茶", @price=330

 メソッドの特徴として、メソッドの内で定義した変数はメソッドの外では利用できませんでしたが、インスタンス変数はインスタンス内であればどこでも呼び出すことができます。

インスタンスメソッド

 インスタンスメソッドとは、作成したインスタンスから実行できるメソッドのことです。
 先ほと作成したanko_taiyakiのインスタンスは、initizlizeメソッドでインスタンス変数に「@taste="あんこ", @price=250」を代入したため、この情報を保持してます。

 インスタンスメソッドは、このインスタンスごとに保持している情報を使って処理を書くことができます。

# たい焼きの設計図を作成
class Taiyaki

  # インスタンス作成時に実行される
  def initialize(taste, price)
    @taste = taste
    @price = price
  end

  # 作成したインスタンスから実行できるインスタンスメソッド
  # インスタンスによって、@taste,@priceの値が異なる
  def show_info
    puts "#{@taste}味のたい焼きは#{@price}円です。"
  end

end

# anko_taiyakiのインスタンス作成
anko_taiyaki = Taiyaki.new("あんこ", 250)
# => <Taiyaki:0x00~ @taste="あんこ", @price=250>

# 作成したanko_taiyakiのインスタンスからshow_infoインスタンスメソッドを実行する
anko_taiyaki.show_info
# => あんこ味のたい焼きは250円です。

 show_infoインスタンスメソッドは、作成したインスタンスによって文章の内容が異なります。これはインスタンスごとに保持するデータを変えることができるからです。
 試しにカスタード味のたい焼きを作って確認します。

custard_taiyaki = Taiyaki.new("カスタード", 300)
# <Taiyaki:0x00~ @taste="カスタード", @price=300>

custard_taiyaki.show_info
# カスタード味のたい焼きは300円です。

クラスメソッドの定義

 先程のインスタンスメソッドは、インスタンスから呼び出すことができましたが、その他にもクラスから呼び出すことができるクラスメソッドがあります。
 クラスメソッドとは、インスタンスに依存せずにクラス全体に紐付けられるメソッドのことです。インスタンスの持つデータを使う必要のない処理は、クラスメソッドで定義します。
 クラスメソッドは、2つの定義方法があります。

# クラスメソッドの定義方法1
class クラス名
  def self.クラスメソッド名
  end
end

# クラスメソッドの定義方法2
class クラス名
  # この中で定義するとクラスメソッドと認識されます。
  class << self
    # メソッド名にselfは必要ない
    def test
    end
  end
end

 クラスメソッドは、以下のように呼び出すことができます。

クラス名.クラスメソッド名

クラス変数

 インスタンス変数の他に、クラス変数という@が2つ付く変数があります。
 インスタンス変数が作成された各インスタンスごとに共有される変数だったのに対して、クラス変数は、全てのインスタンスで共有される変数です。
 たい焼きが全部で何個作成されたかわかるクラス変数「@@total_taiyaki_count」と、それを案内するクラスメソッドを定義します。

# たい焼きの設計図を作成
class Taiyaki

  # 全てのインスタンスで共有されるクラス変数
  @@total_taiyaki_count = 0

  # インスタンス作成時に実行される
  def initialize(taste, price)
    @taste = taste
    @price = price

    # インスタンスが作成(new)される毎にカウントアップ
    @total_taiyaki_count += 1
  end

  # インスタンスメソッド
  def show_info
    puts "#{taste}味のたい焼きは#{price}円です。"
  end

  # クラスメソッド
  def self.shoe_all_count
    puts "たい焼きは全部で#{@@total_taiyaki_count}個作成されました。"
  end

end

 クラス変数「@@total_taiyaki_count」によってTaiyakiクラスからインスタンスを作成する度に1ずつ増える為、たい焼きの作成した個数を知ることができます。

# anko_taiyakiのインスタンスを作成(カウントアップされる)
anko_taiyaki = Taiyaki.new("あんこ", 250)
# => <Taiyaki:0x00~ @taste="あんこ", @price=250>

# custard_taiyakiのインスタンスを作成(カウントアップされる)
custard_taiyaki = Taiyaki.new("カスタード", 300)
# => <Taiyaki:0x00~ @taste="カスタード", @price=300>

# たい焼きの個数を調べるためにshow_all_countクラスメソッドを実行する
Taiyaki.show_all_count
# => たい焼きは全部で2個作成されました。

終わりに

 今回は、クラスについて学習しました。railsを用いてポートフォリオサイトを作成することはできましたが、rubyの基礎知識の部分を忘れていることが多く面接の時に苦労したため、今後は基礎知識の部分を重点的に学習していきます。

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

オブジェクト指向におけるクラスの概念(Ruby)

クラスとインスタンス

 クラスは、インスタンスを作成する時に「元となる設計図」のことで、インスタンスは、設計図をもとに作成される実物体のことをいいます。

用語 役割 例1 例2
クラス 設計図 車の設計図 たい焼きの型
インスタンス 設計図をもとに作成される実物体 黒い車、赤い車 クリームのたい焼き、あんこのたい焼き

 Rubyは、「もの」を組み立てるように表現してコンピュータに指示するオブジェクト指向プログラミング言語です。「もの」を組み立てる際の設計図が「クラス」で、その設計図から作成された実物体のことを「インスタンス」と呼びます。

オブジェクト指向プログラミング言語

 プログラミングとは、コンピュータが理解できる言葉を並べてプログラムを作ることです。その際に、プログラムの意図したことを順番通りにコンピュータに動作させる指示を出しています。
 プログラムの意図したことを順番通りに表現する代表的な方法は以下の通りです。

プログラムの表現方法 プログラムの流れ
オブジェクト指向プログラミング 「もの」を組み立てる様に表現してコンピュータに指示する
手続き型プログラミング プログラムを上から順番に処理を実行する
関数型プログラミング 関数の組み合わせによってプログラムを組み立てる様に表現する

 このプログラムの表現方法の中で、Rubyは、オブジェクト指向プログラミング言語です。
 物を組み立てコンピュータを動作させるために、物の設計図であるクラスと物の実物体であるオブジェクトが必要です。

ポイント

①Rubyはオブジェクト指向プログラミング言語です。
②オブジェクト指向とは、「もの」を組み立てるように表現してコンピュータに指示するプログラムの表現方法
③物を組み立てプログラムを表現するために、物の設計図であるクラスと設計図から作成される実物体のインスタンスが必要です。

メソッドの特徴を理解しよう

 メソッドとは、複数の処理を1つにまとめて、扱いやすくしたものです。
 メソッドの基本的な書き方は、以下の通りになリます。

def メソッド名
  # 処理
end

# メソッド実行
メソッド名

 例)helloメソッドを定義して実行する

def hello
  puts "hello!"
end

# helloメソッド実行
hello
# => hello!

メソッドの特徴

 メソッドは下記のような特徴があります。
①メソッドに値を渡すことができる
②メソッド内で最後に評価される値を返す

def hello(name)
  puts "#{name},hello!"  ここがメソッド内で最後に評価されている
end

# helloメソッド実行
hello("kaito")
# => kaito,hello!

③メソッド内で定義した変数は、外部からアクセスすることができない

# メソッド内に変数を追加
def hello
  other_name = kaito
  puts "hello!"
end

# メソッド内の変数のather_nameをメソッド外から指定するとエラーが出る
puts other_name
# => NameError: undefind localvariable or method 'other_name' for main:object

クラスの作り方

 「たい焼きのイメージ」と「メソッドの特徴」を踏まえた上でクラスを作成します。

クラス作成

 クラスの定義は、以下のように記述します。

class クラス名
  # メソッドなど
end

 ここで重要なのは、メソッドの時と違いクラス名の最初の文字が大文字ということです。

class Taiyaki

  def メソッド
  end

end

インスタンス作成

 インスタンスは、以下のようにクラスから作成します。

クラス名.new

 たい焼きの型からたい焼きを作り出すには、以下のように記述します。

class Taiyaki

  def メソッド
  end

end

# たい焼きの設計図からたい焼きを作成
taiyaki = Taiyaki.new
# => <Taiyaki:0x00~>

 Taiyaki.newの返り値が、「Taiyaki:0x00~」なので、たい焼きのインスタンス(オブジェクト)が作成されたことがわかります。

initializeメソッドとインスタンス変数

 たい焼きを作ることができましたが、このままではたい焼きの味(taste)や値段(price)がわかりません。たい焼きに情報を持たせるためにインスタンス作成時に、たい焼きの情報を引数に渡します。

渡したいたい焼きの情報 インスタンス作成
あんこ味のたい焼き 250円 Taiyaki.new("あんこ", 250)
カスタード味のたい焼き 300円 Taiyaki.new("カスタード", 300)
抹茶味のたい焼き 330円 Taiyaki.new("抹茶", 330)

 引数で渡された値は、「インスタンス作成時」に実行されるinitializeメソッドを利用して「@のついたインスタンス変数」に代入します。

class Taiyaki

  # インスタンス作成の時に実行される
  def initialize(taste, price)
    # インスタンス変数には@をつける
    @taste = taste
    @price = price
  end

end

# newした際にinitializeメソッドが実行され、引数の値がインスタンス変数に代入される
anko_taiyaki = Taiyaki.new("あんこ", 250)
# => <Taiyaki:0x00~ @taste = "あんこ", @price = 250>

 インスタンスを作成するときに、initializeメソッド内でインスタンス変数に引数で渡した値を代入させることによって、インスタンス変数は、インスタンスごとに値を保持することができます。
 たい焼きを作りときは、それぞれのたい焼きごとに味や値段を変えることができます。

インスタンス作成 インスタンス変数の値の中身
Taiyaki.new("あんこ", 250) @taste="あんこ", @price=250
Taiyaki.new("カスタード", 300) @taste="カスタード", @price=300
Taiyaki.new("抹茶", 330) @taste="抹茶", @price=330

 メソッドの特徴として、メソッドの内で定義した変数はメソッドの外では利用できませんでしたが、インスタンス変数はインスタンス内であればどこでも呼び出すことができます。

インスタンスメソッド

 インスタンスメソッドとは、作成したインスタンスから実行できるメソッドのことです。
 先ほと作成したanko_taiyakiのインスタンスは、initizlizeメソッドでインスタンス変数に「@taste="あんこ", @price=250」を代入したため、この情報を保持してます。

 インスタンスメソッドは、このインスタンスごとに保持している情報を使って処理を書くことができます。

# たい焼きの設計図を作成
class Taiyaki

  # インスタンス作成時に実行される
  def initialize(taste, price)
    @taste = taste
    @price = price
  end

  # 作成したインスタンスから実行できるインスタンスメソッド
  # インスタンスによって、@taste,@priceの値が異なる
  def show_info
    puts "#{@taste}味のたい焼きは#{@price}円です。"
  end

end

# anko_taiyakiのインスタンス作成
anko_taiyaki = Taiyaki.new("あんこ", 250)
# => <Taiyaki:0x00~ @taste="あんこ", @price=250>

# 作成したanko_taiyakiのインスタンスからshow_infoインスタンスメソッドを実行する
anko_taiyaki.show_info
# => あんこ味のたい焼きは250円です。

 show_infoインスタンスメソッドは、作成したインスタンスによって文章の内容が異なります。これはインスタンスごとに保持するデータを変えることができるからです。
 試しにカスタード味のたい焼きを作って確認します。

custard_taiyaki = Taiyaki.new("カスタード", 300)
# <Taiyaki:0x00~ @taste="カスタード", @price=300>

custard_taiyaki.show_info
# カスタード味のたい焼きは300円です。

クラスメソッドの定義

 先程のインスタンスメソッドは、インスタンスから呼び出すことができましたが、その他にもクラスから呼び出すことができるクラスメソッドがあります。
 クラスメソッドとは、インスタンスに依存せずにクラス全体に紐付けられるメソッドのことです。インスタンスの持つデータを使う必要のない処理は、クラスメソッドで定義します。
 クラスメソッドは、2つの定義方法があります。

# クラスメソッドの定義方法1
class クラス名
  def self.クラスメソッド名
  end
end

# クラスメソッドの定義方法2
class クラス名
  # この中で定義するとクラスメソッドと認識されます。
  class << self
    # メソッド名にselfは必要ない
    def test
    end
  end
end

 クラスメソッドは、以下のように呼び出すことができます。

クラス名.クラスメソッド名

クラス変数

 インスタンス変数の他に、クラス変数という@が2つ付く変数があります。
 インスタンス変数が作成された各インスタンスごとに共有される変数だったのに対して、クラス変数は、全てのインスタンスで共有される変数です。
 たい焼きが全部で何個作成されたかわかるクラス変数「@@total_taiyaki_count」と、それを案内するクラスメソッドを定義します。

# たい焼きの設計図を作成
class Taiyaki

  # 全てのインスタンスで共有されるクラス変数
  @@total_taiyaki_count = 0

  # インスタンス作成時に実行される
  def initialize(taste, price)
    @taste = taste
    @price = price

    # インスタンスが作成(new)される毎にカウントアップ
    @total_taiyaki_count += 1
  end

  # インスタンスメソッド
  def show_info
    puts "#{taste}味のたい焼きは#{price}円です。"
  end

  # クラスメソッド
  def self.shoe_all_count
    puts "たい焼きは全部で#{@@total_taiyaki_count}個作成されました。"
  end

end

 クラス変数「@@total_taiyaki_count」によってTaiyakiクラスからインスタンスを作成する度に1ずつ増える為、たい焼きの作成した個数を知ることができます。

# anko_taiyakiのインスタンスを作成(カウントアップされる)
anko_taiyaki = Taiyaki.new("あんこ", 250)
# => <Taiyaki:0x00~ @taste="あんこ", @price=250>

# custard_taiyakiのインスタンスを作成(カウントアップされる)
custard_taiyaki = Taiyaki.new("カスタード", 300)
# => <Taiyaki:0x00~ @taste="カスタード", @price=300>

# たい焼きの個数を調べるためにshow_all_countクラスメソッドを実行する
Taiyaki.show_all_count
# => たい焼きは全部で2個作成されました。

終わりに

 今回は、クラスについて学習しました。railsを用いてポートフォリオサイトを作成することはできましたが、rubyの基礎知識の部分を忘れていることが多く面接の時に苦労したため、今後は基礎知識の部分を重点的に学習していきます。

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

開発環境でRailsのログをFluentdでElasticsearchに送ってKibanaで見るための環境をDockerで作る

目的

fluentdやkibanaの使い方を知らなかったので、勉強のために開発環境にDockerでfluentdやkibanaを触れる環境を作ってみました。
この記事のゴールは、fluentdを通してElasticsearchにログを送りKibanaでRailsのログを見ることです。

記事の最後まで進めると、Kibanaでこんな風にRailsのログが表示されます。
スクリーンショット 2021-01-27 18.37.57.png

概要

104429333-d7f65f80-55c8-11eb-9f20-81ea36226fcb.png

フォルダ構成

トップにあるDockerfileはRails用です。
dockerフォルダにはElasticsearch、fluentd、Kibana、MySQL、NginxのDockerfileをおいています。

$ ls
Dockerfile docker-compose.yml docker/ entrypoint.sh
$ tree -L 2 docker
docker
├── elasticsearch
│   └── Dockerfile
├── fluentd
│   ├── Dockerfile
│   └── fluent.conf
├── kibana
│   └── Dockerfile
├── mysql
│   └── Dockerfile
└── nginx
    ├── Dockerfile
    └── nginx.conf

バージョン

対象 バージョン
ruby 2.7.2
rails 6.1
mysql 5.7
nginx 1.19.6
elasticsearch 7.10.1
kibana 7.10.1
fluentd 1.12-1
docker 20.10.2

Rails環境作成

まずはRailsを動かすためにファイルを作っていきます。DBの設定もここで書いていきます。

1. まずは以下のコマンドで必要なファイルを作ります

touch {Dockerfile,Gemfile,Gemfile.lock,entrypoint.sh,docker- 
 compose.yml,.dockerignore}

2. Dockerfileの中身を書いていきます

手順1で作ったDockerfileに以下のコードを書いてください

FROM ruby:2.7-slim-buster

RUN apt-get update -qq && \
    apt-get install -y --no-install-recommends \
    vim locales build-essential \
    libpq-dev libmariadb-dev curl npm && \
    npm install -g yarn n && \
    n 15.6

ENV APP_ROOT /app
WORKDIR $APP_ROOT

COPY Gemfile Gemfile.lock $APP_ROOT/

RUN gem update --system && gem install bundler:2.1.4

RUN RAILS_ENV=development bundle install && yarn install --frozen-lockfile


COPY . $APP_ROOT/
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

3. Gemfileの作成

手順1で作ったGemfileに以下を記述

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.2'

gem 'rails', '~>6'

4. entrypoint.sh

手順1で作ったentrypoint.shに以下を記述

#!/bin/bash
set -e
rm -f /app/tmp/pids/server.pid
exec "$@"

5. dockerフォルダを作ってそこにMySQLのDockerfileを作成する

docker/mysqlフォルダ作成し

mkdir -p docker/mysql

mysql用のDockerfileを作成し

touch docker/mysql/Dockerfile

docker/mysql/Dockerfileには以下を記述します

FROM mysql:5.7

6. docker-compose.ymlを作成

version: "3.7"
services:
  db:
    build: ./docker/mysql/
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      TZ: Asia/Tokyo
    expose:
      - 3306
    volumes:
      # 初期データを投入するSQLが格納されているフォルダ
      - ./docker/mysql/mysql_init:/docker-entrypoint-initdb.d
      # 永続化するときにマウントするフォルダ
      - ./docker/mysql/mysql_data:/var/lib/mysql
  app:
    build: .
    command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
    environment:
      DATABASE_HOST: db
      DATABASE_PORT: 3306
      DATABASE_USER: root
      RAILS_ENV: development
    expose:
      - 3000
    port:
      - 3000:3000
    depends_on:
      - db
    #ポート待受していないコンテナがすぐ終了してしまうのを防ぐ
    tty: true
    # コンテナの標準に入力をオープンにする
    stdin_open: true
    volumes:
      - .:/app

7. .dockerignoreファイルに以下を記述

tmp/
!tmp/pids/
node_modules/
vendor/bundle/

8. Dockerでrails newを実行

docker-compose run --no-deps app rails new . --force --database=mysql

これの実行が完了するまで数分の時間がかかるので気長に待ちましょう...

9. DBの作成

先程のコマンドの実行が完了したらconfig/database.ymlというファイルが作られています。そのファイルのhostを以下のように編集します。
localhostからdbという名前に変えてください。

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
- host: localhost
+ host: db

そしたらrailsをDockerの立ち上げて

docker-compose up --build

別のターミナルでDBを作成するコマンドを実行します。

$ docker-compose run app rails db:create
Starting rails-monitoring_test_db_1 ... done
Created database 'app_development'
Created database 'app_test'

これが完了したら、http://localhost:3000 にアクセスしてみましょう。

この画面が出たら起動成功です!

スクリーンショット 2021-01-27 16.50.15.png

Nginxを作成

続いてNginxの環境を作って行きます。

1. Nginxに関する設定を置くフォルダを作成

mkdir -p docker/nginx/

2. Nginx用のDockerfileと設定ファイルを作成

touch docker/nginx/{Dockerfile,nginx.conf}

3. NginxのDockerfileを書きます

docker/nginx/Dockerfileに以下を記述

FROM nginx:1.19.6
# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*

# Nginxの設定ファイルをコンテナにコピー
COPY ./nginx.conf /etc/nginx/

4. docker/nginx/nginx.confに設定を書きます

user  nginx;
worker_processes  auto;

events {
    worker_connections  1024;
}

http {
    upstream app {
        server unix:///app/tmp/sockets/puma.sock;
    }

    log_format ltsv 'time:$time_iso8601\t'
                    'remote_addr:$remote_addr\t'
                    'request_method:$request_method\t'
                    'request_length:$request_length\t'
                    'request_uri:$request_uri\t'
                    'https:$https\t'
                    'uri:$uri\t'
                    'query_string:$query_string\t'
                    'status:$status\t'
                    'bytes_sent:$bytes_sent\t'
                    'body_bytes_sent:$body_bytes_sent\t'
                    'referer:$http_referer\t'
                    'useragent:$http_user_agent\t'
                    'forwardedfor:$http_x_forwarded_for\t'
                    'request_time:$request_time\t'
                    'upstream_response_time:$upstream_response_time\t'
                    'host:$host';

    server {
        listen 80;
        server_name localhost;

        root /app/public;
        try_files  $uri/index.html $uri @app;

        access_log /var/log/nginx/access.log ltsv;
        error_log /var/log/nginx/error.log;

        location @app {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_pass http://app;
        }
    }
}

5. config/puma.rbを編集

rails newをしたときにconfig/puma.rbというファイルが作成されています。
puma.rbファイルの一番下に以下のコードを記述します。

# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart

# ↓これを記述
bind "unix://#{Rails.root}/tmp/sockets/puma.sock"

またpuma.rbの以下の行をコメントアウトします

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
# ↓この行をコメントアウト
- port ENV.fetch("PORT") { 3000 }
+ # port ENV.fetch("PORT") { 3000 }

6. docker-compose.ymlを編集

docker-compose.ymlのapp部分について編集していきます。
1つは、書いてあったここの部分の -b '0.0.0.0'を削除します。

- command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
+ command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s"

2つめは、volumesに - tmp-data:/app/tmp/socketsを追加です。

    volumes:
      - .:/app
      - tmp-data:/app/tmp/sockets

3つめは、portsの指定を削除です

    ports:
      - 3000:3000

続いては、nginxについての記述を追加します

  nginx:
    build: ./docker/nginx/
    # Nginxをforegroundで動かすため、daemonをoff
    command: nginx -g 'daemon off;'
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
      - tmp-data:/app/tmp/sockets
    ports:
      - 80:80
    links:
      - app

volumes:
    tmp-data:

上記のコードを追加した結果、docker-compose.yml 全体は今こうなっています

version: "3.7"
services:
  db:
    build: ./docker/mysql/
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      TZ: Asia/Tokyo
    expose:
      - 3306
    volumes:
      # 初期データを投入するSQLが格納されているフォルダ
      - ./docker/mysql/mysql_init:/docker-entrypoint-initdb.d
      # 永続化するときにマウントするフォルダ
      - ./docker/mysql/mysql_data:/var/lib/mysql
  app:
    build: .
    command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s"
    environment:
      DATABASE_HOST: db
      DATABASE_PORT: 3306
      DATABASE_USER: root
      RAILS_ENV: development
    expose:
      - 3000
    depends_on:
      - db
    #ポート待受していないコンテナがすぐ終了してしまうのを防ぐ
    tty: true
    # コンテナの標準に入力をオープンにする
    stdin_open: true
    volumes:
      - .:/app
      - tmp-data:/app/tmp/sockets

  nginx:
    build: ./docker/nginx/
    # Nginxをforegroundで動かすため、daemonをoff
    command: nginx -g 'daemon off;'
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
      - tmp-data:/app/tmp/sockets
    ports:
      - 80:80
    links:
      - app

volumes:
    tmp-data:

7. Nginx込みでdocker-compose.ymlを立ち上げてみる

docker-compose up --build

起動が確認できたら http:localhost/ にアクセスしてみましょう。:3000 なしのURLにアクセスできたら成功です!

スクリーンショット 2021-01-27 16.50.15.png

Elasticsearchの作成

1. docker/elasticsearch/Dockerfileの作成

mkdir docker/elasticsearch/
touch docker/elasticsearch/Dockerfile

docker/elasticsearch/Dockerfileには以下の記述だけです

FROM elasticsearch:7.10.1

2. elasticsearchをdocker-compose.ymlに追加

  elasticsearch:
    build: ./docker/elasticsearch/
    environment:
      - discovery.type=single-node
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    ports:
      - 9200:9200
    volumes:
      - ./docker/elasticsearch/data:/usr/share/elasticsearch/data

3. Dockerを立ち上げて動作確認

docker-compose up

起動が確認できたら http://localhost:9200/ にアクセスしてみましょう。こんな画面が出たら成功です!
スクリーンショット 2021-01-27 17.46.25.png

fluentdの作成

1. docker/fluentd/Dockerfileを作成します

mkdir -p docker/fluentd/
touch docker/fluentd/{Dockerfile,fluent.conf}

docker/fluentd/Dockerfileには以下を記述します。

FROM fluent/fluentd:v1.12-1

USER root
RUN gem install fluent-plugin-elasticsearch

2. docker/fluentd/fluent.confを作成します

以下を記述すればOKです。

<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<filter nginx>
  @type parser
  key_name log
  <parse>
    @type ltsv
  </parse>
</filter>

<match nginx>
  @type elasticsearch
  host elasticsearch
  buffer_type memory
  port 9200
  index_name fluentd
  type_name nginx
  logstash_format true
  logstash_prefix nginx.access
</match>

<filter rails>
  @type parser
  key_name messages
  <parse>
    @type json
  </parse>
</filter>

<match rails>
  @type elasticsearch
  host elasticsearch
  buffer_type memory
  port 9200
  type_name rails
  logstash_format true
  logstash_prefix rails.access
</match>

3. docker-compose.ymlにfluentdを追加

  fluentd:
    build: ./docker/fluentd/
    volumes:
      - ./docker/fluentd/fluent.conf:/fluentd/etc/fluent.conf
    ports:
      - 24224:24224
    depends_on:
      - elasticsearch

続けて既存のdocker-compose.ymlにも編集を加えます

まずはappのdepends_onの箇所に - fluentd を加えます。

  app:
    depends_on:
      - db
      - fluentd # これを追加

次にnginxも編集します。

  nginx:
    build: ./docker/nginx/
    # Nginxをforegroundで動かすため、daemonをoff
    command: nginx -g 'daemon off;'
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
      - tmp-data:/app/tmp/sockets
    ports:
      - 80:80
    links:
      - app
      # ↓ここから下を追加
      - fluentd
    logging: # ロギング(ログ保存)ドライバをfluentdが行う
      driver: fluentd
      options:
        tag: nginx
        # 初期接続をブロックしない
        fluentd-async-connect: "true"

fluentdの設定はこれで完了です。次のKibanaの設定で最後になります!

Kibanaの作成

1. docker/kibana/Dockerfileを作成します

mkdir -p docker/kibana/
touch docker/kibana/Dockerfile

docker/kibana/Dockerfileには以下を記述します。

FROM kibana:7.10.1

2. docker-compose.ymlにkibanaを追加します

  kibana:
    build: ./docker/kibana/
    ports:
      - 5601:5601
    depends_on:
      - elasticsearch

これで完成です!

上記までの記述でdocker-compose.ymlは以下のようになりました

version: "3.7"
services:
  db:
    build: ./docker/mysql/
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      TZ: Asia/Tokyo
    expose:
      - 3306
    volumes:
      # 初期データを投入するSQLが格納されているフォルダ
      - ./docker/mysql/mysql_init:/docker-entrypoint-initdb.d
      # 永続化するときにマウントするフォルダ
      - ./docker/mysql/mysql_data:/var/lib/mysql

  app:
    build: .
    command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s"
    environment:
      DATABASE_HOST: db
      DATABASE_PORT: 3306
      DATABASE_USER: root
      RAILS_ENV: development
    expose:
      - 3000
    depends_on:
      - db
      - fluentd
    #ポート待受していないコンテナがすぐ終了してしまうのを防ぐ
    tty: true
    # コンテナの標準に入力をオープンにする
    stdin_open: true
    volumes:
      - .:/app
      - tmp-data:/app/tmp/sockets

  nginx:
    build: ./docker/nginx/
    # Nginxをforegroundで動かすため、daemonをoff
    command: nginx -g 'daemon off;'
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
      - tmp-data:/app/tmp/sockets
    ports:
      - 80:80
    links:
      - app
      - fluentd
    logging:
      # ロギング(ログ保存)ドライバをfluentdが行う
      driver: fluentd
      options:
        tag: nginx
        # 初期接続をブロックしない
        fluentd-async-connect: "true"

  elasticsearch:
    build: ./docker/elasticsearch/
    environment:
      - discovery.type=single-node
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    ports:
      - 9200:9200
    volumes:
      - ./docker/elasticsearch/data:/usr/share/elasticsearch/data

  fluentd:
    build: ./docker/fluentd/
    volumes:
      - ./docker/fluentd/fluent.conf:/fluentd/etc/fluent.conf
    ports:
      - 24224:24224
    depends_on:
      - elasticsearch

  kibana:
    build: ./docker/kibana/
    ports:
      - 5601:5601
    depends_on:
      - elasticsearch

volumes:
    tmp-data:

ここまでできたら動作確認をしてみましょう。

docker-compose up

Kibanaは http://localhost:5601/app/home/ にアクセスして以下のような画面が表示されたら成功です!

スクリーンショット 2021-01-27 17.44.05.png

KibanaでIndexPatternを作成する

RailsのログをKibanaで見れるようにするためにもう少しだけRailsに手を加えます。

1. gemを追加

Gemfileに以下の2つを追加してください

# Railsのログをfluentdに送る
gem 'act-fluent-logger-rails'
gem 'lograge'

2. config/application.rbに設定追加

  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.1

    # ↓これを追加
    # Fluentdのログ設定
    config.log_level = :info
    config.logger = ActFluentLoggerRails::Logger.new
    config.lograge.enabled = true
    config.lograge.formatter = Lograge::Formatters::Json.new
    # ↑ここまで
  end

3. config/fluent-logger.ymlファイルを作成

touch config/fluent-logger.yml

config/fluent-logger.ymlファイルに以下のコードを追記します。

development:
  fluent_host:   fluentd
  fluent_port:   24224
  tag:           'rails'
  messages_type: 'string'

test:
  fluent_host:   '127.0.0.1'
  fluent_port:   24224
  tag:           'rails'
  messages_type: 'string'

production:
  fluent_host:   '127.0.0.1'
  fluent_port:   24224
  tag:           'rails'
  messages_type: 'string'

またRailsのいろいろなログを見るためにscaffoldでCRUD処理を追加しておきます。

docker-compose run app rails g scaffold User name:string
docker-compose run app rails db:migrate

これでRails側の設定は完了です。

再度 docker-compose up --buildをして起動しましょう。

http://localhost/users このページにアクセスしてScaffoleで作られたページが見れたら成功です。

スクリーンショット 2021-01-27 18.30.00.png

IndexPatternを作成

1. kibanaにアクセス

docker-compose up --build

これでKibanaが起動したら
http://localhost:5601/app/management/kibana/indexPatterns/create
にアクセスします。

先程のScaffoldで作られたページ(http://localhost/users )にアクセスをすると、Railsが動くようになり、その結果Kibanaでraisl.access-**というIndex patternを作れるようになります。

スクリーンショット 2021-01-27 18.31.13.png

2. Index pattern nameというテキストフォームに rails.access-*を入力してNext spepに進みます

スクリーンショット 2021-01-27 18.34.11.png

3. Time fieldの値を @timestampにして「Create index pattern」ボタンを押して完成です

スクリーンショット 2021-01-27 18.34.25.png

4. IndexPatterの作成が終わったらログを実際に見てみましょう

http://localhost:5601/app/discover/ にアクセスすると、以下のように直近15分のログが確認できます。

スクリーンショット 2021-01-27 18.37.57.png

これでKibanaでRailsのログを見るための環境をDockerで作ることに成功しました。

おわりに

これでRails×MySQL×Nginx×Fluentd×Elaasticsearch×Kibbanaの環境ができました。

fluentdやKibanaを触ってみることが目的でしたが、Dockerの勉強にもなって楽しかったです。まだ触りたてなので、もっと適切な記述方法などがあればコメントで優しく教えていただけたら幸いです。

参考:https://chulip.org/entry/2019/08/18/233205, https://qiita.com/zgmf_mbfp03/items/0697cc827efa89e5d93e

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

ruby3.0 rails6.1 postgresql + redisなdocker環境を構築する

ruby:latest使ってるので
当然最新rubyになりますが、、、笑

redisはおまけ,
自分はsidekiqに使うので入れておきました。
いらなかったら消してください。

ディレクトリ構造

app
├─ Dockerfile
├─ Gemfile  
├─ Gemfile.lock
└─ docker-compose.yml

各ファイルの中身

Dockerfile
FROM ruby:latest

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
  apt-get update -qq && apt-get install -y \
  nodejs \
  yarn \
  imagemagick \
  build-essential \
  libpq-dev \
  postgresql-client

WORKDIR /app

COPY Gemfile Gemfile.lock /app/

RUN bundle install

docker-compose.yml
version: '3'

volumes:
  db-data:
  redis:
  bundle:
  node_modules:

services:
  web:
    build: .
    command: >
      bash -c "rm -f tmp/pids/server.pid &&
               bundle exec rails s -p 3000 -b '0.0.0.0'"
    ports:
      - '3000:3000'
    volumes:
      - '.:/app'
      - 'bundle:/usr/local/bundle:cached'
      - 'node_modules:/app/node_modules'
      - '/app/vendor'
      - '/app/tmp'
      - '/app/log'
      - '/app/.git'
    environment:
      - 'DATABASE_PASSWORD=postgres'
    tty: true
    stdin_open: true
    depends_on:
      - db
      - redis
    links:
      - db

  db:
    image: postgres
    environment:
      - 'POSTGRES_USER=postgres'
      - 'POSTGRES_PASSWORD=postgres'
    volumes:
      - 'db-data:/var/lib/postgresql/data'

  redis:
    image: redis:latest
    command: redis-server
    ports:
      - 6379:6379
    volumes:
      - redis:/data

node_modules,vendor,tem,log,.gitはup高速化のためvolumeを作成しました。

Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6.1'

Gemfile.lock
# 何も書かないでね

アプリ立ち上げ

appディレクトリにて以下のコマンドでapp立ち上げ

rspec使うので skip-testしてますがそこら辺はお好みで!
--forceは強制上書き、
--no-depsはリンクしたサービスを起動しない設定。
--webpackerはwebpacker:installをやってくれます。

docker-compose run web rails new . --force --no-deps --database=postgresql --skip-test --webpacker

webpackerがインストールできました!
となれば成功です。

次にdatabase.ymlを修正しましょう。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  user: postgres
  port: 5432
  password: <%= ENV.fetch("DATABASE_PASSWORD") %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: product_register_development

test:
  <<: *default
  database: product_register_test

production:
  <<: *default
  database: product_register_production
  username: product_register
  password: <%= ENV['PRODUCT_REGISTER_DATABASE_PASSWORD'] %>

修正したら、db:createを実行

docker-compose run web rake db:create

createできました!
となったら

docker-compose up

でcontainerとserver立ち上げて成功です!

docker-compose exec web bash

でコンテナ入って確認してみましょう。
※僕はtest_appという名前でアプリを作成しました。

スクリーンショット 2021-01-27 19.30.14.png

CMDとかENTRYPOINTとかcommand:とかvolume:とかcontainer_name:とか好きにカスタマイズしてください。

日々精進。

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

【rails】忘れがち!!localhostサーバーのプロセスが切れない時!!

今回はターミナルでローカルサーバーを起動した時に出るトラブルを対処した時のお話です。

たまになるのですが忘れるのでメモメモ!!

ターミナル
rails s

とした時に!!

ターミナル
Run rails server --help` for more startup options

と出た時のお話ですね(´∇`)

結論的にはkillコマンドなのですが、毎回その番号がどこにあったかを忘れてしまいます(アルツ?)

どこだったかな?とゴソゴソしていましたが見つけましたw

tmp>pids>server.pid
<ここに出てきるプロセス番号!!>

このプロセス番号にkillコマンド!!

ターミナル
kill -9 プロセス番号

これでrails sをすると再起動ですね(´∇`)

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

git push -u origin main の実行でエラー

Winodows 1p Pro (64bit)
Vagrant 2.2.14
Ubuntu 16.04.6 LTS
Docker 18.09.7
ruby:ruby 2.7.2p137
GitHubにファイルをプッシュしたら以下のエラーが発生。
git push -u origin main
`````

Warning: Permanently added the RSA host key for IP address '52.69.186.44' to the list of known hosts.
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists

そこで、以下のコマンドを実行した。
````

root@vagrant:/home/vagrant/rails_docker#  sudo ls -al ~/.ssh
total 20
drwx------ 2 root root 4096 Jan 27 08:30 .
drwx------ 8 root root 4096 Jan 27 07:13 ..
-rw------- 1 root root 1679 Jan 26 13:27 id_rsa
-rw-r--r-- 1 root root  394 Jan 26 13:27 id_rsa.pub
-rw-r--r-- 1 root root 1768 Jan 27 08:34 known_hosts

.sshに実行権限を与えた。
````

root@vagrant:/home/vagrant/rails_docker# sudo chmod 700 ~/.ssh

そして再度 git push -u origin mainを実行
````

root@vagrant:/home/vagrant/rails_docker# git push -u origin main
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights

エラーが変わった。
ググって
gitでPlease make sure you have the correct access rights and the repository exists. が出た時の対処法
という記事を発見。これを参考にして再度、プッシュして成功しました。

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

Developers Summit 2021に開発責任者 柴戸の登壇が決定!

日本最大級のデベロッパーの祭典 DevSumi2021

2021年2月18日(木)・19(金)の2日間で開催が予定されている国内最大級のITエンジニア向けイベント「Developers Summit 2021」に当社の開発責任者柴戸が登壇いたします。また、リンクアンドモチベーションとしてはこの度シルバースポンサーとして参加させていただきます!

スクリーンショット 2021-01-26 18.27.57.png

登壇内容

19-B-8 02/19(金) 16:25 ~ 17:05

ARR20億円を3年で達成したエンジニア組織が実現した3つのDeveloper eXperience~訪れる壁と突破方法~

Developers Summit 2021では、リンクアンドモチベーション初のエンジニアとして入社した柴戸より、社員エンジニア1名から組織を拡大していくにあたり、ぶつかった壁やその乗り越え方を、挑戦と葛藤のリアルエピソードを交えながらお話しします。

現在、国内初の組織改善クラウド「モチベーションクラウドシリーズ」は、2016年にリリース後、ARR(年間経常収益)20億円を3年で達成し成長を続けています。開発組織の内製化に舵を切った2018年から、どのような変化があったのか…ご興味のある方は、是非お申し込みください!

▼お申込み・詳細はコチラ
Developers Summit 2021公式ページ

登壇者紹介

柴戸 純也

株式会社リンクアンドモチベーション
ビジネスデザインユニット 開発責任者

大手IT企業を経て、フリーランスとして技術力を磨いた後、前職のアドテク系ベンチャー企業で執行役員(Vp of Engineering )を勤め、企業を上場へと導く。2018年に「良い会社の定義を変える」というミッションに共感し、リンクアンドモチベーションに入社。現在は、モチベーションクラウドシリーズの開発責任者を務めると同時に、グループ全体のDXを牽引。テクノロジーの力で「第二の創業」を推進している。`

? 開発責任者 柴戸の関連記事
大手企業などを中心に5,000社超のデータを基にした自社プロダクトを自らの手で。エンジニア中途一号、柴戸に迫る。

【キャディ社とのCTO対談】 人事領域のプロフェッショナル組織がエンジニアリング組織を作るとどうなるか

「Developers Summit 2021」開催概要

 日時:2/18(木)9:50~18:20予定
    2/19(金)9:50~18:15予定
 会場:オンライン
 参加費:無料(事前登録制)
 URL:https://event.shoeisha.jp/devsumi/20210218

Developers Summitは、翔泳社主催で行われる日本最大級のソフトウェアデベロッパーカンファレンスの一つです。各企業で活躍されている方々のセミナーを通して、日本のITエンジニアが活気づくことを目指している「エンジニアの祭典」となりますので、私たちリンクアンドモチベーションもカンファレンスを盛り上げて行きたいと思います!

エンジニアの皆さま、オンライン会場でお会いしましょう!

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

結合テストで画像を添付する

参考にした記事

結合テストで画像を添付する方法

オリジナルアプリの新規投稿の結合テストコードを実装しようとしたときに添付のやり方がわからなかったので調べました。

books_spec.rb
 image_path = Rails.root.join('public/images/test.png')
 attach_file('book[image]',image_path, make_visible: true)

image_pathという変数にテスト用の画像を添付します。相対パスで指定をするみたいです。
attach_fileメソッドはアップロードのinput要素にテスト用画像を添付することができます。
第1引数:アップロードするinput
要素のname属性の値
第2引数:アップロードする画像のパス
第3引数:オプション(make_visible: trueで一時的に表示)

添付したテスト用画像が表示されているかの確認方法

添付した画像が表示されているかの確認方法です。

books_spec.rb
 expect(page).to have_selector("img[src$='test.png']")

have_selectorで要素があるか判断をします。

あとは必要な他のコードを記述して問題なく実行できたのでOKでした。
ほぼ参考にさせていただいた記事の記述でいけました。

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

Rails 非同期通信 メッセージ機能の実装

メッセージの非同期通信の実装

メッセージの非同期にあたり大きく分けて二つの機能を実装しました、

一つは非同期通信のメッセージ送信です。

二つ目はメッセージの受信(自動更新機能)です。

今回はJavaScriptのみのコードを公開しております。途中に参考にした記事などを掲載しております。そちらを参照していただければと思います。

その前に非同期通信とは?

コンピュータ間でデータ送信と受信のタイミングを合わせずに行う通信手段である。片方がオンライン状態であれば、データの送受信を行うことができる。
反対に同期通信というものがある。
同期通信とは、送信側も受信側もオンラインである必要があり、片方が接続してれば通信を行える非同期通信とは大きく違う点である。

それを踏まえて一つ目のメッセージの送信について、

$(document).on('turbolinks:load', function(){
    function buildHTML(message) {
      var content = message.content ? `${ message.content }` : "";
      var img = message.image ? `<img src= ${ message.image }>` : "";
      var html = `<div class= "message" data-message-id=${message.id}>
                    <div class="upper-message">
                      <p class="message-user">
                        ${message.user_name}
                      </p>
                      <p class="message-date">
                        ${message.date}
                      </p>
                    </div>
                    <p class="lower-message">
                      <div class="message-content">
                      ${content}
                      </div>
                    </p>
                  </div>`
    return html;
    }
    $('#new_message').on('submit', function(e){
      e.preventDefault();
      var message = new FormData(this);
      var url = (window.location.href);
      $.ajax({
        url: url,
        type: 'POST',
        data: message,
        dataType: 'json',
        processData: false,
        contentType: false
      })
      .done(function(data){
        var html = buildHTML(data);
        $('.messages').append(html);
        $('#message_content').val('');
        scrollBottom();

      })
      .fail(function(data){
        alert('エラーが発生したためメッセージは送信できませんでした。');
      })
      .always(function(data){
        $('.form-submit').prop('disabled', false);
      })
    })

こちらに関するコードは下記のURLを参考にして実装いたしました、この方の記事を読んだ方が効率がいいので参考にしていただければと思います。
(引用https://qiita.com/mmmasuke/items/36365bcdf30eaea65250)

二つ目のメッセージの受信(自動更新機能)について

    var reloadMessages = function(){
      var href = 'api/messages#index {:format=>"json"}'
      var last_message_id = $('.message:last').data('message-id');
      $.ajax({
        url: href,
        type: 'GET',
        data: {id: last_message_id},
        dataType: 'json',
      })
      .done(function(messages) {
        if (messages.length !== 0) {
          var insertHTML = '';
          $.each(messages, function(i, message) {
            insertHTML += buildHTML(message)
          });
          $('.messages').append(insertHTML);
          $('.messages').animate({ scrollTop: $('.messages')[0].scrollHeight});
        }
      })
      .fail(function(){
        alert("自動更新に失敗しました")
      });
    };
  if (document.location.href.match(/\/rooms\/\d+\/messages/)){
    setInterval(reloadMessages, 7000)};
});

  function scrollBottom(){
    var target = $('.message').last();
    var position = target.offset().top + $('.messages').scrollTop();
    $('.messages').animate({
      scrollTop: position
    }, 300, 'swing');
  }

こちらのコードは
(引用https://qiita.com/AK4747471/items/cc0ba52b6ed34f0b4b8c)
こちらの記事を参考に実装いたしました。

ただの感想

実装にあたり、自身が送信のみで非同期通信を実装できたと勘違いしておりました。メッセージの非同期通信には自動更新は必須です。

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

【クラスとインスタンスの比喩】

お初にお目にかかります。

ここでは私自身が咀嚼した知識を吐き出してまいります。
青々しく酸っぱいのであまり参考にしないでください。
たで、同じように解釈や比喩について悩んでいる方の一助になれたら幸いです。

2021年1月22日

1、クラス
 クラスとは、個々にデータを持たせるための雛形である。具体的な実体を持っておらず、クラスに共通する属性やメソッドを用いてインスタンス(後述)を作成する。
 例えば、街を行き交う人に「紙の上に三角形を書いてください」と注文する。その際に各人が頭に思い描く「三角形」が、いわば設計図であるクラスと呼べるだろう。人々がどのような三角形をイメージしていても、一般的に共通する特徴はある。線が三本である、直線で描かれている、などだ。
 また、紙上へ出力するための手段がある。鉛筆を使う人もいる一方で、絵筆で描いたりする人もいる。
 この場合特徴は「属性」へ、手段は「メソッド」としてプログラミング上で扱われる。



2、インスタンス
 インスタンスとは、クラスを用いて作成され、それぞれが個別のデータを持つものである。クラスによって作成されたインスタンスは、前述の例を用いて表すならば「人々が実際に書いた三角形」であると言えよう。
 概念から生み出された三角形は、個々人によって様々である。鉛筆を用いて細い小さな一本の線で描いた者もいれば、太いマジックペンで豪快な線を描く者もいる。この場合の描かれた三角形は様々な特徴を持ち、姿は異なる。
 こうして描かれた三角形は、概念上の「三角形」、すなわちクラスから生み出され、様々なメソッドや属性を用いることでインスタンスとして表出する。

2021年1月27日

1、クラス
クラスは、インスタンスを生成するために定義される。それ自体に実体はない。
例えるならば、たいやきというインスタンスを作り出すための「たい焼き器」であると言える。たい焼きの種類はどうあれ、「鯛」の形を成し、熱によって加工される工程は共通のルールとして定義される。


2、インスタンス
インスタンスはクラスから生成され、個々に異なる値を持つものである。
上記の例示を引用するならば、クラスから作り出された「たいやき」である。
素材によってスタンダードなたいやき、白いたいやき、また、中身もあんこやクリームといったように、様々な特徴を持ったインスタンスが作り出される。
この場合の素材は「属性」、見た目や味といった特徴は「値」と言い換えられる

ここまで読んでいただけた奇特な貴方に感謝申し上げます。
また、なにか解釈違いや見当違い等ございましたら、ぜひともご教授いただけますと幸いです。

鍵谷

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

Ruby on Railsでアプリを作ってみよう⑦

投稿機能を作ってみよう

投稿機能は2段階に分けて解説していきます。
少し難しくなってきますが頑張ってください。
まずは投稿画面(new)の処理を作成していきましょう。

図1 遷移図

まずはルーティングを設定していきましょう

newアクションの行き先を指定します。

config/routes.rb
Rails.application.routes.draw do
  get 'posts', to: 'posts#index'
  get 'posts/new', to: 'posts#new' #newアクションのルーティング設定
end

rails routesコマンドを実行してルーティングを確認しましょう
image.png
newアクションのルーティングが設定されました。

次にコントローラーのアクションを定義しましょう。
ページを表示するだけなので中身は必要ありません。

app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end

  def new  #newアクションを定義
  end
end

次はビューファイルを作っていきましょう。

ヘルパーメソッド

Railsではフォームを作る時にヘルパーメソッドを使い作っていきます。
主にビューでHTMLタグを出現させたりテキストを加工するために使用するメソッドで複数あります。

ヘルパーメソッドを使う利点は主に以下の2つになります。
①パスの指定やRubyの埋め込みなどの記述がシンプルになる
②セキュリティ上の問題を解消するため

今回使う2つのメソッドについて紹介します。

ヘルパーメソッド 用途
form_with フォームの実装
link_to リンクの実装

form_withメソッド

フォームを実装するためのヘルパーメソッドでHTMLのformタグの代わりに使用できます。

<%= form_with url: "/posts", method: :post, local: true do |form| %>
  <%= form.text_field :comment %>
  <%= form.submit '投稿する' %>
<% end %>

form_with~doまでの記述はオプションと呼ばれるもので設定が書かれています。

オプション名 説明
url 情報を送るパスを指定
method リクエストのHTTPメソッドを指定(初期値は:postなのでpostの場合は省略可)
local リモート送信を無効にするかどうかを指定する。trueで無効になる

do~endまでの記述はビューファイルに表示されるものを表していて
記述することで部品を追加できます。

フォーム部品 用途
text_field 1行のテキストボックス
password_field パスワード入力ボックス(テキストを*などに置き換えて表示)
check_box チェックボックス(複数選択可
radio_button ラジオボタン(複数の中から1つのみ選択)
submit 送信ボタン

この中ではsubmitをのぞいて引数にはname属性に当たる名前が入ります。
name属性の値がデータを扱う時のキーになります。

エディタを開きapp/views/postsディレクトリにnew.html.erbファイルを作ってください。
そして以下のコードを貼り付けてください。

app/views/posts/new.html.erb
<h1>新規投稿ページ</h1>
<%= form_with url: "/posts", method: :post, local: true do |form| %>
  <%= form.text_field :comment %>
  <%= form.submit '投稿する' %>
<% end %>

image.png

では次はトップページから投稿ページにアクセスする処理を作っていきます

link_toメソッド

リンクを作成するためのヘルパーメソッドでHTMLのaタグの代わりに使用できます。

<%= link_to 'リンクに表示する文字', 'パス', method: :HTTPメソッド %>

image.png

リンクに表示する文字は今回は新規投稿
今回表示したいのはnewの画面なので post#newの隣の/posts/newというパスを入力します。
そしてHTTPメソッドはVerbのところのgetになります。

<%= link_to '新規投稿', '/posts/new', method: :GET %>

このコードをトップページのビューであるapp/views/posts/index.html.erbに記入しましょう。
image.png

そしてhttp://localhost:3000/postsにアクセスし

image.png

新規投稿のリンクをクリックすると以下のページに遷移すれば成功です

image.png

次回はデータを作って保存する処理をやっていきましょう。

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

【Rails】プルダウン検索編 ransackを導入して検索機能を作成する

はじめに

保存したデータを項目事に分けて表示することで、欲しいデータのみ手に入れたい。
そんな時はransackです。

目次

  1. ランサック導入
  2. ルーティング設定
  3. モデル
  4. コントローラー
  5. ビュー
  6. 重複の解消
  7. 検索結果ページを作る

開発環境

ruby 2.6.5
Rails 6.0.3.4
ransack 2.3.2

1. ランサック導入

Ginfile
gem 'ransack'
ターミナル
bundle install

2. ルーティング設定

今回はdemosコントローラーを使用し、アクション名はsearchするので、get 'demos/search'を記述します。

routes.rb
  root to: demos#index'
  resources :demos
  get 'demos/search' #ここransakuのルーティング

3. モデル

今回はdemoモデルとします。マイグレーションファイルを編集します。

db/2020XXXXXXXXX_create_demos.rb
class CreateDemos < ActiveRecord::Migration[6.0]
  def change
    create_table :demos do |t|
      t.string     :sample_name
      t.string     :sample_kana
      t.string     :sample_email
      t.string     :sample_tel
      t.string     :sample_product_name
      t.string     :price
      t.integer    :num_id
      t.datetime   :start_time
      t.integer    :sample_id,             null: false

      t.timestamps
    end
  end
end

今回は上記の項目を保存します。

ターミナル
rails db:migrate

サンプルデータは自分で作成するか、「seed」を使います。
seedの使い方は下記の記事を参考にしてみてください。

【Rails】seedとFakerでランダムなサンプルデータを作成
https://qiita.com/AKI3/items/189574314ca94e8f43c6

4. コントローラー

今回コントローラーはdemosという名前です。

app/controlers/demos_controller.rb
class DemosController < ApplicationController
  before_action :search_demo, only: [:index, :search]

  def index
    @demos = Demo.all
  end

  def search
    @results = @d.result
  end

  private

  def search_demo
    @d = Demo.ransack(params[:q])  # 検索オブジェクトを生成
  end
end

検索パラメーターのキーを「:q」とします。この「:q」とは「query(質問する)」のイニシャルのことです。

次に、:qを使って、demosテーブルから商品情報を探しています。そして、「@d」という名前の検索オブジェクトを生成しています。
この処理を行うメソッド名を「search_demo」としています。

最後にこの@dに対して「.result」とすることで、検索結果を取得しています。
これは、検索条件に該当した商品が@dに格納されているので、その格納されている値を表示する役割があります。

5. ビュー

ビューに検索するバーを作成します。

app/views/demos/index.html.erb
    <div class="search-data">
      <h3>商品検索</h3>
      <%= search_form_for @d, url: demos_search_path do |f| %>
        <%= f.label :sample_product_name_eq, '商品検索' %>
        <%= f.collection_select :sample_product_name_eq, @demo, :sample_product_name, :sample_product_name,  include_blank: '指定なし' %>
        <%= f.submit '検索' %>
      <% end %>
    </div>

まず、search_form_forの引数に「@d」を渡すことで検索フォームを生成します。
「_eq」は条件検索を行うための記述です。
続いて、<%= f.collection_select 第一引数, 第二引数, 第三引数, 第四引数, 第五引数, オプション %>の順で並んでいます。

できたらリロードしてプルダウンを確認します。

ここを自分の言葉に編集

1 2 3
第一引数(メソッド名) :name_eq ・カラム名・name属性やid属性を決める
第二引数(オブジェクト) @demo 配列データを指定する
第三引数(value) name 表示する際に参照するDBのカラム名
第四引数(name) name 実際に表示されるカラム名
オプション include_blank 何も選択していない時に表示される内容

6. 重複の解消

このままだとこのように商品名が重複しています。
これを解消する為にコントローラーに以下を追記します。

app/controlers/demos_controller.rb
#省略
  def index
    @demos = Demo.all
    set_demo_column #←追記
  end

#省略
private
  def set_demo_column
    @demo_date = Demo.select("sample_product_name").distinct  # 重複なくnameカラムのデータを取り出す
  end

html.erbも@demo_dateに変更します。

app/views/demos/index.html.erb
<%= f.collection_select :start_time_eq, @demo_date, :start_time, :start_time,  include_blank: '指定なし' %>

7. 検索結果ページを作る

最後に検索ページを作成します。

app/views/demos/search.html.erb
<h1>検索結果</h1>
<% if @results.length !=0 %>
  <% @results.each do |result| %>
    <td>
    <br>
    <li>
      <%= result.sample_name %>
      <%= result.sample_kana %>
      <%= result.sample_email %>
      <%= result.sample_tel %>
      <%= result.sample_product_name %>
      <%= result.price %>
      <%= result.num_id %>
      <%= result.start_time %>
      <%= result.created_at %>
    </li>
  <% end %>
<% else %>
  該当する商品はありません
<% end %>
<br>
<%= link_to 'トップページへ戻る', root_path %>

表示されれば完成です!

まとめ

本記事では、RANSACKを導入してプルダウン検索機能を作成する方法を紹介しました。
今後別の検索機能も実装していきます。

最後に

同じ様に悩んでる方々の助けになればと思い、記事を投稿しております。
それでは、また次回お会いしましょう〜

参考

【公式】
https://github.com/activerecord-hackery/ransack

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

トランザクション中にrescueするとロールバックしないので注意!

トランザクション中のrescueはロールバックを発生させない

動画による説明

[Ruby on Rails] トランザクション中のrescueには気をつけて

トランザクション中のrescue

このようにすると、create!で発生した例外をキャッチして、exec_transactionの返り値としてfalseを返すことができます。

def exec_transaction
  ApplicationRecord.transaction do
    User.create!(name: 'Duplicate')
    User.create!(name: 'Duplicate')
  rescue ActiveRecord::RecordInvalid
    false
  end
end

Userモデルは下記のようにバリデーションが設定してあります。

class User < ApplicationRecord
  validates :name, uniqueness: true
end

このコードを実行すると、二回目のcreate!でエラーが発生しますが、ロールバック処理が実行されません

Rollbackが起きる仕組み

transactionメソッドに与えられたブロックは、最終的にwithin_new_transactionメソッドの中で実行されます。
ここで、与えられたブロックで発生したすべての例外をキャッチして、ロールバックを発生させたあと、例外をもう一度送出します。

activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L313-L318

この動作によって、transactionで例外が発生すると、ロールバックが暗黙的に実行されて、例外の送出も行われます。

Exceptionをキャッチするとロールバックしない

上述の通り、発生した例外をトリガーにしてロールバックが発生します。したがって、先に示したコードのようにブロックの中で例外をキャッチしてしまうと、ロールバックが起きません。

ロールバックしつつ例外をキャッチしたい場合

方法は二通り。明示的にロールバックするか、トランザクションの外側で例外をキャッチする。

明示的にロールバック

こちらの動画で紹介されている方法と同じ考え方です。
https://www.youtube.com/watch?v=jFBvEQhApKQ

ActiveRecord::Rollbackを送出してロールバックを行い、全て正常終了したかどうかを返り値とします。

def exec_transaction
  success = true
  ApplicationRecord.transaction do
    success &= User.create(name: 'Duplicate')
    success &= User.create(name: 'Duplicate')
    unless success do
       raise ActiveRecord::Rollback
    end
  end

  success
end

トランザクションの外側で例外をキャッチする

ロールバックが発生したら例外は再度送出される、という性質を利用します。

def exec_transaction
  ApplicationRecord.transaction do
    User.create!(name: 'Duplicate')
    User.create!(name: 'Duplicate')
  end
rescue ActiveRecord::RecordInvalid
  false
end

そもそもの例外処理の設計

こういう例外処理がある事自体が良くない可能性もあります。もちろん、すべてが悪いということではないです。
エラー処理の設計については、下記の資料で非常に丁寧にまとめられています。

Railsアプリケーションにおけるエラー処理(例外設計)の考え方
プロを目指す人のための例外処理(再)入門 / #rubykansai 2018-01-13

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

Rubyのハッシュとシンボル、キーの関係について、わかりやすく解説する

曖昧な理解になってしまいがちな「ハッシュ」「シンボル」について、解説していきます。
Rubyを勉強し始めた時は全然わからなかった。。

この記事を通して
・ハッシュとキーの関係がわかる
・シンボルの使い方がわかる
ようになれば幸いです。

ハッシュは、「関連付いた名前をつけて、データを格納したいとき」に使う

数学のテストがあって、データが点数順に並んでいるとします。

 score = [56,43,55,78,79,66,73,90,45,65,43,25]

ここで、taro君の点数を知ろうとしたとき「score」というデータだけではわかりませんよね。「name」という、データに関連付いた名前が必要になってきます。

ここで、ハッシュの登場です。
ハッシュとは、「関連付いた名前をつけて、データを格納したいとき」に使うことができます。

{ :name => score }    #今回の例であれば { :taro => 56 }

このような形でハッシュを作ることができます。これでtaro君の点数を知ることができるようになりました。

データに関連付いた名前のことを「キー」と呼ぶ

「:taro」の部分をキーと呼びます。キー(データに関連付いた名前)とデータのセットであるハッシュを格納するわけですね。

キーは、文字列、時間、数字など、任意のオブジェクトを使うことできますが、「シンボル」と呼ばれる、コロンではじまる識別子をよく使います。(例 => :taro)

またキーとデータの間の区切りにコロンを使えば、シンボル名の頭のコロンを省略することができます。

# ①文字列
"taro" => 56
# ②シンボル
:taro => 56
# ③一番良く使う表記
taro : 56

①、②、③は同じ意味になります

シンボルは「何かしらの名前を表す存在だよ」ということを示しています。

「:taro」になったり、「taro:」になったりして、コロンに惑わされると思うのですが、①、②、③が同じであることがわかれば安心かと思います。

まとめ

以上、ハッシュとシンボル、キーの関係について、解説していきました。

・「ハッシュ」・・・「関連付いた名前をつけて、データを格納したいとき」に使う
・「キー」・・・データに関連付いた名前
・「シンボル」・・・コロンではじまる識別子を使ったキー

この3点をおさえていきましょう。


この記事の説明がわかりやすかった!ここ間違ってるよ!次こんな記事を書いて欲しい!などあればコメント、DMよろしくお願いします。LGTMもぜひ。

Twitterもやってますので、フォローしていただけたらうれしいです。
卓球、心理学、哲学、Webサービス、好きな音楽、カメラ、登山、ランニング、読んだ本などなんでもつぶやいてます。

[https://twitter.com/atsushi101011]

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

Railsチュートリアルの1.5.2でRubyのバージョンがHerokuに対応していないと言われた

IDEはRailsチュートリアルに則りAWS Cloud9のものを使用。
Gemfileの内容は以下のとおり。

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.3'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '6.0.3'
# Use Puma as the app server
gem 'puma', '4.3.6'
# Use SCSS for stylesheets
gem 'sass-rails', '5.1.0'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '4.0.7'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '5.2.0'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '2.9.1'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '1.4.5', require: false

group :development, :test do
  # Use sqlite3 as the database for Active Record
  gem 'sqlite3', '1.4.1'
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '4.0.1'
  gem 'listen', '3.1.5'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring', '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '3.28.0'
  gem 'selenium-webdriver', '3.142.4'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers', '4.1.2'
end

group :production do
  gem 'pg', '1.1.4'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]'

これで以下のコマンドを実行した。

$ git push heroku master

すると以下のように表示された

...

remote:  !
remote:  !     The Ruby version you are trying to install does not exist on this stack.
remote:  !     
remote:  !     You are trying to install ruby-2.6.3 on heroku-20.
remote:  !     
remote:  !     Ruby ruby-2.6.3 is present on the following stacks:
remote:  !     
remote:  !     - cedar-14
remote:  !     - heroku-16
remote:  !     - heroku-18
remote:  !     
remote:  !     Heroku recommends you use the latest supported Ruby version listed here:
remote:  !     https://devcenter.heroku.com/articles/ruby-support#supported-runtimes
remote:  !     
remote:  !     For more information on syntax for declaring a Ruby version see:
remote:  !     https://devcenter.heroku.com/articles/ruby-versions
remote:  !

...

どうやらRalisチュートリアルで使っているRubyのバージョンがHerokuに対応していないので、ここに載っているバージョンを使ってほしいということらしい
https://devcenter.heroku.com/articles/ruby-support#supported-runtimes

早速以下のようにGemfileを書き換えた。
要するにrubyのバージョンを変えただけ。

Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '6.0.3'
# Use Puma as the app server
gem 'puma', '4.3.6'
# Use SCSS for stylesheets
gem 'sass-rails', '5.1.0'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '4.0.7'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '5.2.0'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '2.9.1'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '1.4.5', require: false

group :development, :test do
  # Use sqlite3 as the database for Active Record
  gem 'sqlite3', '1.4.1'
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '4.0.1'
  gem 'listen', '3.1.5'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring', '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '3.28.0'
  gem 'selenium-webdriver', '3.142.4'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers', '4.1.2'
end

group :production do
  gem 'pg', '1.1.4'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

あとは下記のコマンドを実行してデプロイすればOK

$ bundle update
$ git commit -a -m "RubyのバージョンをHerokuに対応している2.6.6に変更"
$ git push heroku master

Railsチュートリアルの1.3.1ではGemfileのRubyのバージョンを2.6.3としていたが、どこかで変更しているのを私が見落としたのだろうか?

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

【Rails】Railsのencrypted_passwordを自作する

前提

ローカル環境でのテスト用などに、手動でユーザーを作成時のパスワードを生成する。
rails consoleを利用。

作業手順

encrypted_passwordの自作

ターミナル
password = '12345678'
User.new(password: password).encrypted_password

rails cで上記を実行すると、encrypted_passwordが生成される。
この例ではusersテーブルのencrypted_passwordカラムにコピペすると、
'12345678'がパスワードとなる。

確認

ターミナル
user = User.find(1)
user.valid_password?('12345678')

上記のようにrails cから操作すると、
users.id=1のレコードのパスワードが'12345678'であるか確認できる。

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

form objectで検索フォームを実装する。(複数単語、or 検索)

何をしたか

form objectで検索フォームを実装しました。
「赤い りんご」のように、空白で区切られた検索ワードが入力された時には、空白で区切って複数単語のOR検索にしました。

実装環境は以下の通りです。

  • Rails 5.2.3
  • Ruby 2.6.0

まずは1単語の検索フォームを作る

今回作るのは、投稿postの検索フォームです。

まずはシンプルに、1単語の検索フォーム(フォームオブジェクトではないもの)から作り始めました。

実際に書いたコードは以下の通りです。(装飾等に使用したclassは省いています。以下同。)

/controllers/posts_controller.rb
def search
  @posts = Post.where('body LIKE ?', "%#{params[:body]}%")
end
/views/posts/index_html.slim
= form_with url: search_posts_path, method: :get, local: true do |f|
 = f.search_field :body
 = f.submit

 # ページ下方で = render @posts をして、検索結果を呼び出しています

非常にシンプルですね:relaxed:

この辺の基本的なコードの読み解き方は、以下の記事にまとめておりますので、よろしければご覧ください。

Railsの検索メソッドの基礎(whereでLIKE句を使う)

form objectにする

では、こちらのコードをform objectにしていきます。

/controllers/posts_controller.rb
def search
  @posts = SearchForm.new(search_post_params)
end

# 中略

def search_post_params
  params.fetch(:q, '').permit(:body) # 解説します
end
/views/posts/index_html.slim
= form_with form_with model: search_form, url: search_posts_path, scope: :q, method: :get, local: true do |f|
 = f.search_field :body  # ↑上記、解説します 
 = f.submit
/forms/search_form.rb
class SearchForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :body, :string

  def search
    Post.includes(:user, :images).where('body LIKE ?', "%#{body}%")
  end
end

form objectにすることで、検索ロジックをフォームオブジェクトに移動できました:relaxed:

解説:scope: :qfetch(:q, '')について

ところで、viewにあったscope: :qが謎ですよね。(↓この部分です)

= form_with form_with model: search_form, url: search_posts_path, scope: :q, method: :get, local: true do |f|

これは、「送るパラメーターをどのハッシュでまとめるのか」を指定している部分で、

scope: :qがない時のparams

Processing by PostsController#search as HTML
  Parameters: {"utf8"=>"✓", "body"=>"りんご", "commit"=>"SEARCH"}
Unpermitted parameters: :utf8, :commit

scope: :qが ある時のparams

Processing by PostsController#search as HTML
  Parameters: {"utf8"=>"✓", "q"=>{"body"=>"りんご"}, "commit"=>"SEARCH"}

のように、scope: :qの記述によって、検索単語を"q"=>{ ... }の中にまとめることができます。

キーワードを入力するフォールドが一つの検索フォームだとほとんど意味がありませんが、入力するフィールド数が2つ、3つにになってくると、

"q"=>{ "フィールド名"=>"検索単語", "フィールド名"=>"検索単語", ... }

の形でparamsがまとまってくれるので、便利です。:relaxed:

また、先ほどはcontrollerstrong paramaterに以下のように設定していましたが...

controller
def search_post_params
  params.fetch(:q, '').permit(:body)
end

これによって、paramsの中から、"q"=>{ ... }の部分だけを取り出しています。

fetchの詳しい説明はこちら
instance method Hash#fetch

複数単語検索にする

それでは、これを複数単語のor検索に変えていきます。

色々やり方はありましたが、今回はこちらの記事と、そのコメントを参考に、以下のように実装しました。

/forms/search_form.rb
class SearchForm

  # 変わっていないので中略

  def search
    keywords = body.split(/[[:blank:]]+/)

    @posts = Post.none

    keywords.each do |keyword|
      @posts = @posts.or(Post.where('body LIKE ?', "%#{keyword}%"))
    end

    return @posts
  end
end

何点かピックアップして解説します。

  • .split(/[[:blank:]]+/)...検索ワードを空白で複数単語に分け、配列に入れます。
  • Post.none...空のActive Recordを作ります。空の配列を作る[]のActive Record版です。
  • @posts.or(...)...詳細下述します。

解説:@posts.or(...)について

この部分については、記述が複雑になるのでピックアップします。Railsorメソッドは、データベースへの複数検索のorを実行するメソッドです。

例えば、以下のコードで

Fruit.where(name: 'バナナ').or(Fruit.where(name: 'グレープ'))

以下のようなSQLが発行されます。(サンプルのコードはこちらからお借りしました)

Fruit Load (1.5ms) SELECT `fruits`.* FROM `fruits` WHERE (`fruits`.`name` = 'バナナ' OR `fruits`.`name` = 'グレープ')

実装に用いたコードでは、このように書くことで、

@posts = Post.none

keywords.each do |keyword|
  @posts = @posts.or(Post.where('body LIKE ?', "%#{keyword}%"))
end

return @posts

keywordの数だけ

Post.where(body: 'keyword').or(Post.where(body: 'keyword').or(Post.where(...)))

を繰り返し、複数語検索を実現しています。

実際にはリファクタリングを繰り返し、上記orのロジックは消えてしまったのですが、面白い書き方だったので記事にしてとっておくことにしました:relaxed:

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

「Undifined method 'upload' for nil:NilClass」エラーの対処

概要

Active Storageを利用する際に、「Undifined method 'upload' for nil:NilClass」エラーが出る。

状況

  1. rails version -5.2.2.4でアプリケーションを作成
  2. ActiveStorageをインストール
  3. Model, Controller, Viewファイル作成
  4. ブラウザでファイルのアップデートを実行すると、概要のエラーが発生

解決策

https://qiita.com/NaokiIshimura/items/c8aa4d55cb653ff0501a

こちらの記事と全く同じ症状であることが発覚。
同様の対処法で解決した。

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