- 投稿日:2021-01-27T23:49:57+09:00
【環境構築】Docker rails 備忘録
Docker使ってrailsでアプリを作成した際の備忘録
手順1
ファイル作成
既にDockerをインストールしているので
使用しているディレクトリの配下に任意の名前でディレクトリを作成し
以下作成したディレクトリにファイル準備。
・Gemfile
・Gemfile.lock
・Dockerfile
・docker-compose.yml
※ターミナルでのファイル作成コマンド:touch XXXXXX(※XXXに作成するファイル名)手順2
作成したファイルの内容を編集
Gemfilesource 'https://rubygems.org' gem 'rails', 5.2.2DockerfileFROM 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 . /sampledocker-compose.ymlversion: '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
- 投稿日:2021-01-27T23:17:31+09:00
【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
- 投稿日:2021-01-27T23:09:49+09:00
ActiveModel::Serializer のインストール方法
ActiveModel::Serializer ( Gem ) のインストールの仕方を簡単に書いておきます!
ActiveModel::Serializer ( Gem ) のインストール その①
Gemfileに以下のコードを記述する。Gemfilegem 'active_model_serializers'その後、ターミナル上で bundle install を実行。
terminalbundle installActiveModel::Serializer ( Gem ) のインストール その②
ターミナル上で以下のコマンドを実行し、
Gemfile にgem 'active_model_serializers'を追記。terminalecho "gem 'devise_token_auth'" >> Gemfileその後、ターミナル上で bundle install。
terminalbundle installまとめ
どちらの方法でもインストール可能です。
本日、Qiita に初投稿してみました!とても簡単な内容かもしれませんが、自分が検索したときにパッと出てこなかったのと、Markdown の書き方の勉強を兼ねて投稿してみました!
- 投稿日:2021-01-27T23:09:49+09:00
ActiveModel::Serializer ( Gem ) のインストール方法
ActiveModel::Serializer ( Gem ) のインストールの仕方を簡単に書いておきます!
ActiveModel::Serializer ( Gem ) のインストール その①
Gemfileに以下のコードを記述する。Gemfilegem 'active_model_serializers'その後、ターミナル上で bundle install を実行。
terminalbundle installActiveModel::Serializer ( Gem ) のインストール その②
ターミナル上で以下のコマンドを実行し、
Gemfile にgem 'active_model_serializers'を追記。terminalecho "gem 'devise_token_auth'" >> Gemfileその後、ターミナル上で bundle install。
terminalbundle installまとめ
どちらの方法でもインストール可能です。
本日、Qiita に初投稿してみました!とても簡単な内容かもしれませんが、自分が検索したときにパッと出てこなかったのと、Markdown の書き方の勉強を兼ねて投稿してみました!
- 投稿日:2021-01-27T22:38:36+09:00
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
主な使用イメージ
トップページからゲストログイン(閲覧用)をクリック
「レシピを見てみる」または「材料を探してみる」をクリックし、一覧画面へ遷移します
ログインが完了したら、レシピまたは材料の一覧画面の右下から新規投稿ができます
必要な項目を入力して、投稿するをクリック
材料投稿画面にて、郵便番号を入力すると自動で住所が入力されます
材料投稿画面で入力した住所の経度・緯度を自動で取得して、GoogleMapに表示されます
投稿詳細画面でいいねをすることができます
使用技術
- フロントエンド
- 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
インフラ構成図
VPCの構築、EC2へのデプロイ、RDSの導入、ELBの設定、ドメイン取得からのRoute53の設定、ACMでのSSL証明書の取得など基本的な部分は導入できました
今後は、S3や自動デプロイを導入していきたいです!機能一覧
機能 概要 ユーザー管理機能 新規登録・ログイン・ログアウトできます 簡単ログイン機能 トップ画面のゲストログイン(閲覧用)をクリックすることで、簡単にログインできます 投稿機能 レシピまたは材料を画像を5枚までつけて投稿できます 投稿詳細表示機能 各投稿画面が詳細ページで見ることができます 投稿編集・削除機能 投稿者本人のみ投稿編集・削除できます ユーザー一覧表示機能 登録したユーザーの一覧を見ることができます ユーザー情報編集機能 ログイン中のユーザーでアカウント本人であればプロフィール編集できます フォロー機能 ユーザー一覧画面から各ユーザーのフォローできます フォロー一覧表示機能 フォローしているまたはフォローされているユーザーを見ることができます いいね機能 投稿詳細ページからいいねすることできます いいね一覧機能 いいねした投稿の一覧を見ることができます コメント機能 投稿詳細ページから非同期通信でコメントできます 住所自動入力機能 材料投稿画面にて、郵便番号を入力するだけで住所が自動で入力されます マップ表示機能 材料投稿画面で入力した住所の経度・緯度を自動で取得し、マップで表示することができます レスポンシブデザイン スマートフォン向けの表示に対応 工夫した点
とにかく、単なるレシピ投稿アプリにならないように下記を頑張って実装しました!
- 料理に使った材料の投稿機能
- 住所自動入力機能
- GoogleMapsAPI連携
レシピを投稿したユーザーが、使った材料をどこで買ったかも投稿できるようにしました
通販でいいんじゃ。。。というツッコミはなしでお願いします笑
実際にお店で色々見たりする楽しみだってありますから!今後、新しい機能を思いついたら実装してみたいとおもいます
DB設計
各テーブルについて
テーブル名 説明 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 DockerDocker
- 環境構築
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
- いいね機能
- フォロー機能
- 住所自動入力機能
- GoogleMapsAPI連携
- Rspec
AWS
こちらの記事がすごく分かり易かったです!
画像が豊富に使われていて、初めてAWSを使う人でも問題なくできます
手順通りに行うだけで、VPC,EC2,RDS,Route53,ACMなど基本的な環境が構築可能VPCの構築やEC2の立ち上げ方など、基礎的なことを一通り学ぶことができます
まず最初にこちらを読んでみると良いのではないでしょうかDocker/docker-compose
- 【Qiita】いまさらだけどDockerに入門したので分かりやすくまとめてみた
- 入門 Docker
- 【書籍】Docker/Kubernetes 実践コンテナ開発入門(3章のdocker-composeまで読みました)
まず、学習の手始めとしてこちらを学習しました
Dockerとは何か?というところからdocker-composeの構築の仕方まで学ぶことができます私はまだ受けていないのですが、こちらの講座がわかりやすいと評判なので紹介させていただきます
CircleCI
今後の課題
- Vue.jsなどの導入
- ECSによるコンテナデプロイ
- CircleCIの自動デプロイ(CDパイプラインの構築)
- テストコードの充実
- いいね・フォロー機能のajax化
- さらなる機能の拡張
など、まだまだできるところはあると感じました!
今後の転職活動の状況と相談しながら引き続き改善していきたいと思います
- 投稿日:2021-01-27T22:13:04+09:00
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を編集したら動きました!!
- 投稿日:2021-01-27T21:59:20+09:00
rails - devise基礎
deviseの導入・実装手順
初投稿です。deviseを使った簡単なユーザー管理機能実装についてアウトプットもかねて自分用にまとめてみます。
まずは実装の流れになります。
1.Gemインストール
2.devise設定ファイル作成
3.Userモデル作成
4.テーブル作成
5.ビュー作成、編集
6.コントローラーでストロングパラメータ設定
1.Gemインストール
Gemfile,ターミナルgem 'devise' bundle install rails s2.devise設定ファイル作成
ターミナルrails g devise:install3.Userモデル作成
ターミナルrails g devise userconfig/routes.rbdevise_for :users #自動で挿入される4.テーブル作成
db/migrate/20XXXXXXXXX_devise_create_users.rbclass 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 s5.ビュー作成
ターミナルrails g devise:views6.コントローラーでストロングパラメータ設定
app/controllers/application_controller.rbclass 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これで一応解決できました。
- 投稿日:2021-01-27T21:33:11+09:00
deviseを利用したログイン機能の実装
目次
①deviseとは
②deviseのインストールの仕方
③モデルの作成方法
④テーブルの作成方法
⑤ビューファイルの作成
⑥deviseで使えるメソッド①deviseとは
Railsで作成したアプリケーションへ簡単に認証機能を実装することができるgemのひとつ。
こちらを使用すると、ログインやサインアップなどのログイン機能を1から構築することが簡単に実装することができる。②deviseのインストールの仕方
Gemfileに以下のように記述する
Gemfilegem '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 ユーザーのセッション情報にアクセスする
- 投稿日:2021-01-27T21:08:40+09:00
オブジェクト思考におけるクラスの概念(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の基礎知識の部分を忘れていることが多く面接の時に苦労したため、今後は基礎知識の部分を重点的に学習していきます。
- 投稿日:2021-01-27T21:08:40+09:00
オブジェクト指向におけるクラスの概念(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の基礎知識の部分を忘れていることが多く面接の時に苦労したため、今後は基礎知識の部分を重点的に学習していきます。
- 投稿日:2021-01-27T20:22:34+09:00
開発環境でRailsのログをFluentdでElasticsearchに送ってKibanaで見るための環境をDockerで作る
目的
fluentdやkibanaの使い方を知らなかったので、勉強のために開発環境にDockerでfluentdやkibanaを触れる環境を作ってみました。
この記事のゴールは、fluentdを通してElasticsearchにログを送りKibanaでRailsのログを見ることです。記事の最後まで進めると、Kibanaでこんな風にRailsのログが表示されます。
概要
フォルダ構成
トップにある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 30003. 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/mysqlmysql用のDockerfileを作成し
touch docker/mysql/Dockerfiledocker/mysql/Dockerfileには以下を記述します
FROM mysql:5.76. 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: - .:/app7. .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 にアクセスしてみましょう。
この画面が出たら起動成功です!
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/sockets3つめは、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にアクセスできたら成功です!Elasticsearchの作成
1. docker/elasticsearch/Dockerfileの作成
mkdir docker/elasticsearch/touch docker/elasticsearch/Dockerfile
docker/elasticsearch/Dockerfileには以下の記述だけですFROM elasticsearch:7.10.12. 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/data3. Dockerを立ち上げて動作確認
docker-compose up起動が確認できたら http://localhost:9200/ にアクセスしてみましょう。こんな画面が出たら成功です!
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-elasticsearch2. 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.12. 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 upKibanaは http://localhost:5601/app/home/ にアクセスして以下のような画面が表示されたら成功です!
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 # ↑ここまで end3. 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で作られたページが見れたら成功です。
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を作れるようになります。2. Index pattern nameというテキストフォームに
rails.access-*を入力してNext spepに進みます3. Time fieldの値を
@timestampにして「Create index pattern」ボタンを押して完成です4. IndexPatterの作成が終わったらログを実際に見てみましょう
http://localhost:5601/app/discover/ にアクセスすると、以下のように直近15分のログが確認できます。
これで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
- 投稿日:2021-01-27T19:53:05+09:00
ruby3.0 rails6.1 postgresql + redisなdocker環境を構築する
ruby:latest使ってるので
当然最新rubyになりますが、、、笑redisはおまけ,
自分はsidekiqに使うので入れておきました。
いらなかったら消してください。ディレクトリ構造
app ├─ Dockerfile ├─ Gemfile ├─ Gemfile.lock └─ docker-compose.yml各ファイルの中身
DockerfileFROM 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 installdocker-compose.ymlversion: '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:/datanode_modules,vendor,tem,log,.gitはup高速化のためvolumeを作成しました。
Gemfilesource '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 --webpackerwebpackerがインストールできました!
となれば成功です。次にdatabase.ymlを修正しましょう。
database.ymldefault: &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:createcreateできました!
となったらdocker-compose upでcontainerとserver立ち上げて成功です!
docker-compose exec web bashでコンテナ入って確認してみましょう。
※僕はtest_appという名前でアプリを作成しました。CMDとかENTRYPOINTとかcommand:とかvolume:とかcontainer_name:とか好きにカスタマイズしてください。
日々精進。
- 投稿日:2021-01-27T18:09:29+09:00
【rails】忘れがち!!localhostサーバーのプロセスが切れない時!!
今回はターミナルでローカルサーバーを起動した時に出るトラブルを対処した時のお話です。
たまになるのですが忘れるのでメモメモ!!
ターミナルrails sとした時に!!
ターミナルRun rails server --help` for more startup optionsと出た時のお話ですね(´∇`)
結論的にはkillコマンドなのですが、毎回その番号がどこにあったかを忘れてしまいます(アルツ?)
どこだったかな?とゴソゴソしていましたが見つけましたw
tmp>pids>server.pid<ここに出てきるプロセス番号!!>このプロセス番号にkillコマンド!!
ターミナルkill -9 プロセス番号これでrails sをすると再起動ですね(´∇`)
- 投稿日:2021-01-27T18:05:27+09:00
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. が出た時の対処法
という記事を発見。これを参考にして再度、プッシュして成功しました。
- 投稿日:2021-01-27T15:20:08+09:00
Developers Summit 2021に開発責任者 柴戸の登壇が決定!
日本最大級のデベロッパーの祭典 DevSumi2021
2021年2月18日(木)・19(金)の2日間で開催が予定されている国内最大級のITエンジニア向けイベント「Developers Summit 2021」に当社の開発責任者柴戸が登壇いたします。また、リンクアンドモチベーションとしてはこの度シルバースポンサーとして参加させていただきます!
登壇内容
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/20210218Developers Summitは、翔泳社主催で行われる日本最大級のソフトウェアデベロッパーカンファレンスの一つです。各企業で活躍されている方々のセミナーを通して、日本のITエンジニアが活気づくことを目指している「エンジニアの祭典」となりますので、私たちリンクアンドモチベーションもカンファレンスを盛り上げて行きたいと思います!
エンジニアの皆さま、オンライン会場でお会いしましょう!
- 投稿日:2021-01-27T14:23:12+09:00
結合テストで画像を添付する
参考にした記事
結合テストで画像を添付する方法
オリジナルアプリの新規投稿の結合テストコードを実装しようとしたときに添付のやり方がわからなかったので調べました。
books_spec.rbimage_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.rbexpect(page).to have_selector("img[src$='test.png']")have_selectorで要素があるか判断をします。
あとは必要な他のコードを記述して問題なく実行できたのでOKでした。
ほぼ参考にさせていただいた記事の記述でいけました。
- 投稿日:2021-01-27T13:56:09+09:00
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)
こちらの記事を参考に実装いたしました。ただの感想
実装にあたり、自身が送信のみで非同期通信を実装できたと勘違いしておりました。メッセージの非同期通信には自動更新は必須です。
- 投稿日:2021-01-27T12:51:50+09:00
【クラスとインスタンスの比喩】
お初にお目にかかります。
ここでは私自身が咀嚼した知識を吐き出してまいります。
青々しく酸っぱいのであまり参考にしないでください。
たで、同じように解釈や比喩について悩んでいる方の一助になれたら幸いです。2021年1月22日
1、クラス クラスとは、個々にデータを持たせるための雛形である。具体的な実体を持っておらず、クラスに共通する属性やメソッドを用いてインスタンス(後述)を作成する。 例えば、街を行き交う人に「紙の上に三角形を書いてください」と注文する。その際に各人が頭に思い描く「三角形」が、いわば設計図であるクラスと呼べるだろう。人々がどのような三角形をイメージしていても、一般的に共通する特徴はある。線が三本である、直線で描かれている、などだ。 また、紙上へ出力するための手段がある。鉛筆を使う人もいる一方で、絵筆で描いたりする人もいる。 この場合特徴は「属性」へ、手段は「メソッド」としてプログラミング上で扱われる。 2、インスタンス インスタンスとは、クラスを用いて作成され、それぞれが個別のデータを持つものである。クラスによって作成されたインスタンスは、前述の例を用いて表すならば「人々が実際に書いた三角形」であると言えよう。 概念から生み出された三角形は、個々人によって様々である。鉛筆を用いて細い小さな一本の線で描いた者もいれば、太いマジックペンで豪快な線を描く者もいる。この場合の描かれた三角形は様々な特徴を持ち、姿は異なる。 こうして描かれた三角形は、概念上の「三角形」、すなわちクラスから生み出され、様々なメソッドや属性を用いることでインスタンスとして表出する。2021年1月27日
1、クラス クラスは、インスタンスを生成するために定義される。それ自体に実体はない。 例えるならば、たいやきというインスタンスを作り出すための「たい焼き器」であると言える。たい焼きの種類はどうあれ、「鯛」の形を成し、熱によって加工される工程は共通のルールとして定義される。 2、インスタンス インスタンスはクラスから生成され、個々に異なる値を持つものである。 上記の例示を引用するならば、クラスから作り出された「たいやき」である。 素材によってスタンダードなたいやき、白いたいやき、また、中身もあんこやクリームといったように、様々な特徴を持ったインスタンスが作り出される。 この場合の素材は「属性」、見た目や味といった特徴は「値」と言い換えられるここまで読んでいただけた奇特な貴方に感謝申し上げます。
また、なにか解釈違いや見当違い等ございましたら、ぜひともご教授いただけますと幸いです。鍵谷
- 投稿日:2021-01-27T11:09:07+09:00
Ruby on Railsでアプリを作ってみよう⑦
投稿機能を作ってみよう
投稿機能は2段階に分けて解説していきます。
少し難しくなってきますが頑張ってください。
まずは投稿画面(new)の処理を作成していきましょう。図1 遷移図
まずはルーティングを設定していきましょう
newアクションの行き先を指定します。
config/routes.rbRails.application.routes.draw do get 'posts', to: 'posts#index' get 'posts/new', to: 'posts#new' #newアクションのルーティング設定 endrails routesコマンドを実行してルーティングを確認しましょう
newアクションのルーティングが設定されました。次にコントローラーのアクションを定義しましょう。
ページを表示するだけなので中身は必要ありません。app/controllers/posts_controller.rbclass 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 %>では次はトップページから投稿ページにアクセスする処理を作っていきます
link_toメソッド
リンクを作成するためのヘルパーメソッドでHTMLのaタグの代わりに使用できます。
<%= link_to 'リンクに表示する文字', 'パス', method: :HTTPメソッド %>リンクに表示する文字は今回は新規投稿
今回表示したいのはnewの画面なので post#newの隣の/posts/newというパスを入力します。
そしてHTTPメソッドはVerbのところのgetになります。<%= link_to '新規投稿', '/posts/new', method: :GET %>このコードをトップページのビューであるapp/views/posts/index.html.erbに記入しましょう。
そしてhttp://localhost:3000/postsにアクセスし
新規投稿のリンクをクリックすると以下のページに遷移すれば成功です
次回はデータを作って保存する処理をやっていきましょう。
- 投稿日:2021-01-27T09:58:13+09:00
【Rails】プルダウン検索編 ransackを導入して検索機能を作成する
はじめに
保存したデータを項目事に分けて表示することで、欲しいデータのみ手に入れたい。
そんな時はransackです。目次
- ランサック導入
- ルーティング設定
- モデル
- コントローラー
- ビュー
- 重複の解消
- 検索結果ページを作る
開発環境
ruby 2.6.5
Rails 6.0.3.4
ransack 2.3.21. ランサック導入
Ginfilegem 'ransack'ターミナルbundle install2. ルーティング設定
今回は
demosコントローラーを使用し、アクション名はsearchするので、get 'demos/search'を記述します。routes.rbroot to: demos#index' resources :demos get 'demos/search' #ここransakuのルーティング3. モデル
今回は
demoモデルとします。マイグレーションファイルを編集します。db/2020XXXXXXXXX_create_demos.rbclass 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/189574314ca94e8f43c64. コントローラー
今回コントローラーは
demosという名前です。app/controlers/demos_controller.rbclass 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カラムのデータを取り出す endhtml.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を導入してプルダウン検索機能を作成する方法を紹介しました。
今後別の検索機能も実装していきます。最後に
同じ様に悩んでる方々の助けになればと思い、記事を投稿しております。
それでは、また次回お会いしましょう〜参考
- 投稿日:2021-01-27T09:48:15+09:00
トランザクション中に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 endUserモデルは下記のようにバリデーションが設定してあります。
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=jFBvEQhApKQActiveRecord::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
- 投稿日:2021-01-27T08:17:47+09:00
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サービス、好きな音楽、カメラ、登山、ランニング、読んだ本などなんでもつぶやいてます。
- 投稿日:2021-01-27T02:38:58+09:00
Railsチュートリアルの1.5.2でRubyのバージョンがHerokuに対応していないと言われた
IDEはRailsチュートリアルに則りAWS Cloud9のものを使用。
Gemfileの内容は以下のとおり。Gemfilesource '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のバージョンを変えただけ。Gemfilesource '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 masterRailsチュートリアルの1.3.1ではGemfileのRubyのバージョンを2.6.3としていたが、どこかで変更しているのを私が見落としたのだろうか?
- 投稿日:2021-01-27T01:55:51+09:00
【Rails】Railsのencrypted_passwordを自作する
前提
ローカル環境でのテスト用などに、手動でユーザーを作成時のパスワードを生成する。
rails consoleを利用。作業手順
encrypted_passwordの自作
ターミナルpassword = '12345678' User.new(password: password).encrypted_passwordrails cで上記を実行すると、encrypted_passwordが生成される。
この例ではusersテーブルのencrypted_passwordカラムにコピペすると、
'12345678'がパスワードとなる。確認
ターミナルuser = User.find(1) user.valid_password?('12345678')上記のようにrails cから操作すると、
users.id=1のレコードのパスワードが'12345678'であるか確認できる。
- 投稿日:2021-01-27T00:07:52+09:00
form objectで検索フォームを実装する。(複数単語、or 検索)
何をしたか
form objectで検索フォームを実装しました。
「赤い りんご」のように、空白で区切られた検索ワードが入力された時には、空白で区切って複数単語のOR検索にしました。実装環境は以下の通りです。
Rails 5.2.3Ruby 2.6.0まずは1単語の検索フォームを作る
今回作るのは、投稿
postの検索フォームです。まずはシンプルに、1単語の検索フォーム(フォームオブジェクトではないもの)から作り始めました。
実際に書いたコードは以下の通りです。(装飾等に使用したclassは省いています。以下同。)
/controllers/posts_controller.rbdef 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 をして、検索結果を呼び出しています非常にシンプルですね
この辺の基本的なコードの読み解き方は、以下の記事にまとめておりますので、よろしければご覧ください。
Railsの検索メソッドの基礎(whereでLIKE句を使う)
form objectにする
では、こちらのコードを
form objectにしていきます。/controllers/posts_controller.rbdef 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.rbclass SearchForm include ActiveModel::Model include ActiveModel::Attributes attribute :body, :string def search Post.includes(:user, :images).where('body LIKE ?', "%#{body}%") end end
form objectにすることで、検索ロジックをフォームオブジェクトに移動できました解説:
scope: :qとfetch(: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がない時のparamsProcessing by PostsController#search as HTML Parameters: {"utf8"=>"✓", "body"=>"りんご", "commit"=>"SEARCH"} Unpermitted parameters: :utf8, :commit▼
scope: :qが ある時のparamsProcessing by PostsController#search as HTML Parameters: {"utf8"=>"✓", "q"=>{"body"=>"りんご"}, "commit"=>"SEARCH"}のように、
scope: :qの記述によって、検索単語を"q"=>{ ... }の中にまとめることができます。キーワードを入力するフォールドが一つの検索フォームだとほとんど意味がありませんが、入力するフィールド数が2つ、3つにになってくると、
"q"=>{ "フィールド名"=>"検索単語", "フィールド名"=>"検索単語", ... }の形で
paramsがまとまってくれるので、便利です。また、先ほどは
controllerのstrong paramaterに以下のように設定していましたが...controllerdef search_post_params params.fetch(:q, '').permit(:body) endこれによって、
paramsの中から、"q"=>{ ... }の部分だけを取り出しています。▼
fetchの詳しい説明はこちら
instance method Hash#fetch複数単語検索にする
それでは、これを複数単語のor検索に変えていきます。
色々やり方はありましたが、今回はこちらの記事と、そのコメントを参考に、以下のように実装しました。
/forms/search_form.rbclass 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(...)についてこの部分については、記述が複雑になるのでピックアップします。
Railsのorメソッドは、データベースへの複数検索の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のロジックは消えてしまったのですが、面白い書き方だったので記事にしてとっておくことにしました
- 投稿日:2021-01-27T00:03:39+09:00
「Undifined method 'upload' for nil:NilClass」エラーの対処
概要
Active Storageを利用する際に、「Undifined method 'upload' for nil:NilClass」エラーが出る。
状況
- rails version -5.2.2.4でアプリケーションを作成
- ActiveStorageをインストール
- Model, Controller, Viewファイル作成
- ブラウザでファイルのアップデートを実行すると、概要のエラーが発生
解決策
https://qiita.com/NaokiIshimura/items/c8aa4d55cb653ff0501a
こちらの記事と全く同じ症状であることが発覚。
同様の対処法で解決した。
























