- 投稿日:2020-10-10T23:42:36+09:00
【RSpec】ヘルパーメソッドが使われたviewファイルをテストしたらActionView::Template::Error:が出た。。
個人開発のアプリに通知機能を実装し、その後GitにpushしてCircleCIのテスト結果を確認したら突然大量のテストが失敗していて少し困ったので、その解決方法をここで共有させて頂きます。
エラー内容
Failure/Error: -if unchecked_notifications.any? ActionView::Template::Error: undefined local variable or method `unchecked_notifications' for #<#<Class:0x0000555732134140>:0x00005557326f7f78>通知が来ている場合と来ていない場合で表示するviewを変えるために、
unchecked_notifications
という、ヘルパーメソッドを定義しました。しかし、RSpec実行時にはこのヘルパーメソッドが読み込まれておらずエラーが出てテストに失敗してしまったようです。notifications_helper.rbmodule NotificationsHelper def unchecked_notifications @notifications = current_user.passive_notifications.where(checked: false) end end解決策
解決策は簡単です。
テストを実行するファイルに、モジュールをインクルードするれば解決するはずです。↓例post_spec.rbrequire 'rails_helper' #ヘルパーメソッドをインクルード include NotificationsHelper RSpec.describe '投稿機能', type: :system do #テスト処理 end最後まで読んでいただきありがとうございます!
日々学習したことをアウトプットしてます!ご指摘などあればコメントいただけますと幸いです!
- 投稿日:2020-10-10T23:35:01+09:00
検索機能の実装
はじめに
個人アプリの作成にて、投稿した記事の内容を検索で表示出来るように検索機能の実装を行う。
ルーティングの設定
今回は:idを指定してページに遷移しないので、collectionを使用してルーティングを設定します。
routes.rbresources :posts do get :search, on: :collection resource :likes, only: [:create, :destroy] -いいね機能実装 endモデルの設定
LIKE句
LIKE句は、文字列の検索をすることができる。
whereメソッドと一緒に使う。
実行例 詳細 where('title LIKE(?)', "a%") aから始まるタイトル where('title LIKE(?)', "%b") bで終わるタイトル where('title LIKE(?)', "%c%") cが含まれるタイトル where('title LIKE(?)', "d_") dで始まる2文字のタイトル where('title LIKE(?)', "_e") eで終わる2文字のタイトル app/models/post.rbdef self.search(search) return Post.all unless search Post.where('body LIKE(?)', "%#{search}%") endコントローラーにsearchアクションを定義
post_controller.rbdef search @posts = Post.search(params[:keyword]) endviewの実装
renderメソッドを使って、部分テンプレートを行っているものとします。
{ post: post } の右側のpostはeachメソッド内の変数としてのpostでpostのインスタンスを示しています。左側のpostは部分テンプレート内での変数の名前を表しています。search.html.erb<% @posts.each do |post| %> <%= render partial: "post", locals: { post: post } %> <% end %>おわりに
比較的簡単に検索機能の実装は完了しました。
他にも機能を実装し、出来ることを増やしていこうと思います。
最後まで見ていただきありがとうございます
- 投稿日:2020-10-10T22:36:32+09:00
Mixed Content(混在コンテンツ)エラーの解決方法
railsでアプリケーションを開発。本番環境(heroku)にデプロイしたがなんか見た目が違う...となりました。
解決はしましたが、新しい発見があったため記録として残しておきます!問題発生の経緯
デプロイをしたがビューが崩れている
↓
アセットファイルをコンパイルしてない!
↓
rails assets:precompile RAILS_ENV=production
を実行
↓
再度デプロイ。「これで大丈夫なはず!」
↓
直ってませんでした......。原因
結論から言うとコンパイルの問題ではなく外部ページがHTTPのページだったことが原因でした。(コンソールにちゃんと
This request has been blocked; the content must be served over HTTPS.
と書かれていましたが確認しておらず反省です。)
httpsで読み込まれているのに対して、httpのものを読み込もうとするとエラーになるとは知らず新しい発見でした!解決策
httpsであれば問題ないとのことだったのでhttpで読み込まれていたパスをhttpsに変えてみたが対応していませんでした...
今回はリセットCSSを外部から読み込んで使おうと思っていたため、外部から読み込むのではなくreset.cssとしてファイルを作成することで解決しました。参考記事
https://qiita.com/Kouch/items/5089fb36694c6e467a3a
https://www.transnet.ne.jp/2016/02/28/rails%e5%88%9d%e5%ad%a6%e8%80%85%e3%81%8c%e3%81%a4%e3%81%be%e3%81%9a%e3%81%8dcolnr%e3%80%8c%e3%82%a2%e3%82%bb%e3%83%83%e3%83%88%e3%83%91%e3%82%a4%e3%83%97%e3%83%a9%e3%82%a4%e3%83%b3/
- 投稿日:2020-10-10T22:33:23+09:00
Rails datetime型からdate型のマイグレーションの書き方
「このカラム
datetime
じゃなくてdate
でよさそう」日付をあつかうから
datetime
にしてたけど、よくよく考えてみると「時間」は必要ないよねーとなったとき、date
に変換したくなりますよね。今回のゴール
- Bookモデルは発売日
released_at(datetime)
をもっている- マイグレーションをして
released_on(date)
にしたい環境
- Rails 6.0.3
- psql (PostgreSQL) 12.4
changeをつかったdatetime→dateの変換
def change rename_column :books, :released_at, :released_on change_column :books, :released_on, 'date USING CAST(released_on AS date)' end
db:rollback
できますか?これでよしよし、
git commi..
、と、ちょっとまってください!マイグレーションは巻き戻る(rollback)ことを考慮しておきましょう。
前述のマイグレーションをrollbackできるかを確かめてみましょう。
$ rails db:rollback == ..... : reverting ======== rails aborted! StandardError: An error has occurred, this and all later migrations canceled: This migration uses change_column, which is not automatically reversible. To make the migration reversible you can either: 1. Define #up and #down methods in place of the #change method. 2. Use the #reversible method to define reversible behavior.なにがエラーになったのか
change_column
でロールバックしたとき、以前の型情報がわからないから元に戻せないから
'date USING CAST(released_on AS date)'
で型を変換できたものの、「変換前の型の情報」が抜け落ちてしまったため、ロールバックに失敗してしまいました。これを解決するためのひとつの方法としてup/downをつかった方法に書きかえてみましょう。
up/downをつかった方法
def up rename_column :books, :published_at, :release_on change_column :books, :release_on, 'date USING CAST(release_on AS date)' end def down rename_column :books, :release_on, :published_at change_column :books, :published_at, 'timestamp USING CAST(published_at AS timestamp)' endこうすることによって、
db:migrate
ではup
のdatetime -> date
が実行されるdb:rollback
ではdown
のdate -> datetime
が実行されるおまけ:PostgreSQLで
datetime USING CAST
はエラーになるchange_column :books, :released_at, 'datetime USING CAST(released_at AS datetime)'PG::UndefinedObject: ERROR: type "datetime" does not exist LINE 1: ...released_at" TYPE datetime USING CAST(published_at AS datetime)
Rails世界の
datetime
は、PostgreSQL世界ではtimestamp
になるという話でした
- 投稿日:2020-10-10T21:48:33+09:00
【Ruby】ハッシュテーブルの順番を逆にしたい
はじめに
たとえば、
reputation = {very_bad: '最悪', bad: '悪い', good: '良い', very_good: '最高'}
みたいなハッシュテーブルがあったとして、その順番を{very_good: '最高', good: '良い', bad: '悪い', very_bad: '最悪'}
のように後ろから並べたい。やり方
pry(main)> reputation.to_a.reverse.to_h #=> {very_good: '最高', good: '良い', bad: '悪い', very_bad: '最悪'}これだけです。
一応説明すると、
reverse
っていう配列の順番を逆にするメソッドを使いたいんだけどreputation
はhashなので使えない。なので一度to_a
でhashを配列に変換してreverse
した後、そのままだと配列のままなのでto_h
で元の戻す、ということです。あとがき
「gemの
enum_help
を使ってラジオボタンの表示は日本語表記に、内部ではint型で処理したい。評価が低い方から数字も低い方を割り当てたい、しかしラジオボタンの表示は評価が高い方から表示したい。」という場面で必要でした。
普通enum使うときってvalueが数字になるはずだから並べ替えやすいんですが、enum_help使うと上に書いたようなhashを渡すことになるので「どうやって並べ替えるんだ…」ってなりました。記事にすることもないような気がしますが、僕のような初心者だと
to_s
やto_i
は使っても、to_a
とかは使う機会が少なくて詰まることがあると思うので少しでも誰かの役に立てば。
- 投稿日:2020-10-10T21:44:07+09:00
【0からDockerに挑戦】③DockerとDocker-composeを使ってRuby on Rails・MYSQLの開発環境をつくる (part2)
DockerとDocker-composeを使ってRuby on Rails・MYSQLの開発環境をつくる
前回の続きになります。もし見てなければ先にそちらをみていただけたらと思います。
【前回の記事】
【0からDockerに挑戦】②DockerとDocker-composeを使ってRuby on Rails・MYSQLの開発環境をつくる (part1)ちなみに前回までで下記手順②まで終了しています。なので今回は③からの手順になります。
①ローカルPC上に必要なディレクトリ・ファイルをつくる
②そのうちDockerfile、docker-compose.yml、Gemfileに必要な記述を書く
③docker-composeをビルド・実行させる
④コンテナ上でrails newを実行し必要なファイルを作成する。
⑤コンテナ上でデータを作成・起動するそれでは早速再開しましょう!!!
③docker-composeをビルド・実行させる
前回までで必要なファイルの記述が完了しましたので、さっそくターミナルでdocker-composeを実行しましょう。
まずはターミナルでDockerファイルのある場所(今回の場合は/Desktop/test-product)まで移動します。
そしたら下記コマンドを実行します。ターミナルuser@user test-product % docker-compose up -d
そうすると2~3分程度で、さきほど用意したdocker-compose.ymlとDockerfileを基にコンテナが立ち上がります。もし記述に誤りがある場合は途中で処理が止まりますので、エラー文をみてファイルを修正しましょう。ちなみに「-d」はディタッチオプションといい、「-d」をつけることでバックグラウンドで実行することができます。
ちなみに下記のように、成功した後に「docker-compose ps」とターミナルで入力するとちゃんとコンテナができていることが確認できます。
ターミナルuser@user test-product % docker-compose ps Name Command State Ports --------------------------------------------------------------------------- test-product_db_1 docker-entrypoint.sh mysqld Up 3306/tcp test-product_web_1 irb Up 0.0.0.0:3000->3000/tcp
④コンテナ上でrails newを実行し必要なファイルを作成する。
では出来上がったコンテナの中に入り、railsに必要なファイルを生成します。コンテナの中を操作するには下記のコマンドを入力します。
ターミナルuser@user test-product % docker-compose exec web bash root@f206189dc19b:/test-product#簡単に説明するとコマンドは、「exec(実行する)」、「web(コンテナの名前)」、「bash(場所はコンテナ内のシェル)」という意味です。つまりコンテナ内に入りbashを起動するということになります。「root@・・・・」と表示されていたらOKです。
そしてコンテナの中に入れたので「rails new」を実行し、必要なファイルを実行しましょう。
ターミナルroot@f206189dc19b:/test-product# rails new . --force --database=mysql --skip-bundlenewのあとの「.」はカレントディレクトリを示します。「--force」はファイルを無理やり上書きすることを示します。rails newを実行するとGemfileも作らるのですが、今回ファイル自体はすでに作ってあるので--forceをつけることでGemfileを改めて作ります。(railsのバージョンは最初に指定したものになります)。「--database=mysql」はデータベースの指定です。そして最後の「--skip-bundle 」はコンテナ立ち上げ時にbundleをしたので、スキップを指定しました。これで必要なファイルは揃うのですが、1つ問題があります。それはdatabase.ymlの設定です。(おそらくファイルの生成には問題ないがbundle install時にエラーが起こる)。生成したファイルのconfig/database.ymlを開き、docker-compose.ymlの内容にあわせて下記のように編集しましょう。
ターミナル・ ・ ・ (省略) default: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: <%= ENV.fetch("DATABASE_PASSWORD")%> host: db ・ ・変更箇所はusernameとpasswordとhost名です。これで設定は問題ありません。
⑤コンテナ上でデータを作成・起動する
最後に「rails db:create」と「rails s」すれば終わりと行きたいところですが、このまま実行するとエラーがでます。理由はrails6.0から標準装備されたwebpackerがインストールされていないからです(rails5.0であればそのままでもエラー起きません)。なのでwebpackerをインストールしましょう。
ターミナルroot@f206189dc19b:/test-product# rails webpacker:install問題なくインストールできるはずです。(もしエラーがおこるのであればDockerfileのyarnのインストールの指定の仕方を見直してください。前の記事に書いてあります)。これで準備ができたので「rails db:create」をしましょう成功するです。そして最後に下記のように[rails s」でサーバーを立ち上げましょう。ここでもしかしたらyarnに関するエラーがでるかもしれません。エラーが出た場合は「yarn upgrade」で解決できるはずです。
ターミナルroot@f206189dc19b:/test-product# rails s -b 0.0.0.0ローカルの環境では自分のホスト内で完結していたため、特に指定なしで問題ありませんでした。しかしコンテナの場合、自分のPCからコンテナで立ち上げたサーバーへのアクセスは外部からのアクセス扱いになるので、指定が必要です。今回は0.0.0.0を指定して外部からのアクセスを許可しています。
【参照】
RailsアプリケーションをLAN内に公開するこれでブラウザにlocalhost:3000と記載すればrailsの初期画面が表示されるはずです。
まとめ
以上でdockerによる開発環境構築完了です。このあとはいつもどおりgitとrailsを用いいた開発になります。もし「バージョンを変えたい」や「環境系のエラー起きた」などがあっても、Dockerfileをいじり、新しくコンテナを立ち上げればすぐに対応できるでしょう(私も開発に取り組んでdokcerの凄さを実感しました)。dockerはまだまだ奥深く、勉強することがたくさんありそうですので、引き続き私もdockerの勉強がんばります。
しばらくあとになりますが、AWS環境(本番環境)でdockerを使ってみたり、dockerとCircleCIを用いてCICDパイプラインを作るという記事も書く予定です。【前回までの記事】
【0からDockerに挑戦】①Dockerの概要と用語
【0からDockerに挑戦】②DockerとDocker-composeを使ってRuby on Rails・MYSQLの開発環境をつくる (part1)参考になった教材
「米国AI開発者がゼロから教えるDocker講座」
https://www.udemy.com/course/aidocker/
→初心者がDockerを学ぶならこれ!!!めちゃくちゃわかりやすい!!!linuxの基礎からDockerでの環境構築を網羅しています。
今回の環境構築もこの教材をベースにしています(もちろんrailsのバージョンとかdbなど自分なりに変えているので同じではないです)
もしDocker全然わかないという人はこの講座をぜひ買ってみてください。「いまさらだけどDockerに入門したので分かりやすくまとめてみた」
https://qiita.com/gold-kou/items/44860fbda1a34a001fc1
→すごく細かく網羅しているのでとてもわからなくなったらよく見ています。リファラル的によくみさせていただいてます。「Docker/Kubernetes 実践コンテナ開発入門 --著 山田 明憲」
→実践的な書籍、あまりにも初学者だと少しむずかしいかも。 → 基礎覚え得たあとに見返したら非常に良かったです!「たった1日で基本が身に付く! Docker/Kubernetes超入門 --著」
→初学者が書籍で学びたい場合はこれがとっかかりやすい。
- 投稿日:2020-10-10T20:38:31+09:00
Ruby on Rails 変数、定数まとめ
Ruby on Rails 変数、定数まとめ
Railsにおいてよく使う変数、定数についてまとめました。
前提条件
- 変数
- 変更可能
- 定数
- 定数不可
変数
ローカル変数
- メソッドやブロック内でのみ有効
- 変数名
- 小文字のスネークケース
user_name = 'jon'
Railsでの活用
- View
- View でローカル変数を扱いたい場合は、Viewファイル内で定義する。 [users/show.html.erb]
<% hoge = 'huga' %> <div><%= hoge %><div>
- Controller
- Viewに変数の値をわたす必要がなかったり、Contrller内で変数の値を共有する必要がない場合は、ローカル変数に定義する
def create article = Article.find( params[:id]) return render_404 if article.blank? endインスタンス変数
- 同じコントローラー内で使える変数
- 変数名
- 先頭に
@
を付けるRails での活用
- Controller Action内で定義したインスタンス変数を View へわたす。
def show @user = User.find(params[:id]) end[users/show.html.erb]
<div>ユーザーID:<%= @user.id %><div>※ローカル変数をわたすことはできない
def show user = User.find(params[:id]) end[users/show.html.erb]
<div>ユーザーID:<%= user.id %><div>-> 参照不可
undefined local variable or method 'user'
グローバル変数
- 全てのContrller使える
- プログラムのどこからでも、変更、参照が可能
- 変数名
- 先頭に
@
を付ける定数
クラス内で定義する
定数名
- 大文字、スネークケース
Rails での活用
- [config/initializers/constants.rb]に共通の定数を定義する
- 全ての Controller, View で参照することができる [config/initializers/constants.rb]
MAX_SIZE = 10
- 各 Model で使用する定数を定義するには、model ファイルのクラス内に定義する。 [user.rb]
class User OFFICIAL_ID = 100 endController や View で参照する場合は、
User::OFFICIAL_ID
と書く
- 投稿日:2020-10-10T20:04:05+09:00
[Rails5.2] Docker内のMysql 5.7の絵文字対応(文字コードをutf8mb4に変更する)
はじめに
Ruby on Railsでポートフォリオを作成しているRails初学者です。
今回はタイトルの通り、tableに絵文字情報を保存するためのmysqlをutf8mb4にした際の覚書です。Youtube Data APIから動画タイトルを保存しようとしたらエラーが出た。
ActiveRecord::StatementInvalid in VideosController#refresh Mysql2::Error: Incorrect string value: '\xF0\x9F\x94\xB5\xE8\x87...'絵文字が入っているタイトルがいくつかあり、それが影響でエラーが出たようです。
mysql上のdbの文字コードを見るとuft8になっており、これを変更しなければいけない様子・・・
ひとまず色々なコードを参考にして以下の通りに実施しました。環境
Docker for mac を使ってコンテナ内でRailsを開発しています。
ruby 2.4.5 mysql 5.7.31 Ruby on rails 5.0.7.2流れ
(準備:dumpする)
1. my.cnfを変更する
2. mysqlをリスタートする
3. ActiveRecordにオプションを追加
4. database.ymlの変更
5. docker-compose.ymlの変更し、コンテナの再起動
6. db:migrate:resetを実行
7. dumpデータのrestore準備:dumpする
そもそもこの作業を行うまでdumpという言葉を知らなかったのですが、dumpとはDBのテーブル内の情報をSQLの形で出力することだそうです。(ちなみに最後に行うrestoreは、dumpした情報をDBに投入すること)
今回データベース設定を変更し、各テーブルの文字コードをutf8mb4にするので、一旦データをファイルとして落としておいて、設定変更後に投入するという形をとりります。
dump/restoreを簡単に行うために以下のgemを使用します。
Gemfilegem 'yaml_db'今回使用するのはyaml_dbという、ダンプでyaml形式ファイルを出力してくれるgemです。
GitHub - yamldb/yaml_dbdumpするには以下のコマンドをすればOK
Terminalbundle exec rails db:data:dumpコマンド実行後、
db/data.yml
にファイルが出力されています。
こんな感じで、基本的にはテーブル名、カラム情報、レコードの順番で出力されます。data.ymlvideos: columns: - id - name - url - upload_at - created_at - updated_at records: - - 1 - "【衝撃】ボーナス支給額をクイズで決める会社" - https://www.youtube.com/watch?v=42ofwfioMFM - 2020-10-09 09:00:00.000000000 Z - 2020-10-10 08:07:38.000000000 Z - 2020-10-10 08:07:38.000000000 Z :以下で準備はOKです。
1. my.cnfを変更する
my.cnfファイルを以下のように設定します。
記載されている内容をきちんと理解したかったため、1行ずつ見ていきました。my.cnf[mysql] default-character-set=utf8mb4 #文字コードの設定 [mysqld] #mysqld Mysqlサーバの設定 character-set-server = utf8mb4 #文字コードの設定 skip-character-set-client-handshake #client側で指定した文字コードを無視するためのもの collation-server = utf8mb4_general_ci #ソート順の指定 init-connect = SET NAMES utf8mb4 #クライアントからserverへの送信に使用される文字セット指定各項目について、細かく見ていきます。
[mysql]と[mysqld]
mysqldとは、MySQLサーバとも呼ばれる、mysqlでの各種操作を受け持っているメインプログラムのこと。
mysql側の操作は必ずこのmysqldよりされるため、ここに各種設定が必要。
skip-character-set-client-handshake
character-set-client-handshakeとは、クライアント側の文字コードをMySQL側に反映する行為。これをスキップすることで、uft8mb4への設定ができる。
collation-server = utf8mb4_general_ci
collationは、照合順序:ソート順のこと。_で区切られた各単語でそれぞれ設定。
詳細は以下を参照
【MySQL】照合順序とは?
init-connect = SET NAMES utf8mb4
クライアントからサーバへの送信に使用される文字コードの指定
詳細は以下を参照
10.1.4 接続文字セットおよび照合順序2. mysqlをリスタートする
ひとまず、この設定を反映するためにmysqlをリスタートします。
Docker内のコンテナに入り以下のコマンドを実行します。もともとの設定を一応確認しておきます
terminalmysql>status; :(中略) Server characterset: utf8 Db characterset: utf8 Client characterset: utf8 Conn. characterset: utf8 :utf8になっています。
というわけで、一旦リスタートして、もう一度みてみます。terminalmysql>service mysql restartterminalmysql>status :(中略) Server characterset: utf8mb4 Db characterset: utf8mb4 Client characterset: utf8mb4 Conn. characterset: utf8mb4 :設定できました!
3. ActiveRecordにオプションを追加
次に、ActiveRecordのcreate_tableが実行される際に、utf8mb4を使って登録するよう設定していきます。
新しく
config/initializers/utf8mb4.rb
を作成し、以下のコードを記載。
config/initializersにファイルを投入すると、Railsが起動する前に初期設定として読み込まれるようになります。config/initializers/utf8mb4.rb#optionを設定するmodule module Utf8mb4 def create_table(table_name, options = {}) table_options = options.merge(options: 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC') super(table_name, table_options) do |td| yield td if block_given? end end end ActiveSupport.on_load :active_record do module ActiveRecord::ConnectionAdapters class AbstractMysqlAdapter #最初にmodule utf8mb4を実行し、その後既存のメソッドを実行(super) prepend Utf8mb4 end end end4. database.ymlの変更
以下の内容を追加ないし変更します
config/database.ymlcharset: utf8mb4 encoding: utf8mb4 collation: utf8mb4_general_ci5. docker-compose.ymlの変更
Dockerの設定ファイルも以下のように変更します。
docker-compose.yml: db: image: mysql:5.7 command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci #utf8mb4をセット container_name: コンテナ名 volumes: - ./my.cnf:/etc/mysql/conf.d/my.cnf #my.cnfを読み込むよう設定 :この状態で、一度コンテナを再起動します。
6. db:migrate:resetを実行
ここまでのDBへの設定を反映させるため、DBをリセット→migrateします。
7. dumpデータのrestore
最後に、冒頭でdumpしたデータをもう一度投入すれば完了です。
以下のコマンドでrestoreできます。terminalrails db:data:load以上で設定は完了です!
終わりに
色々と設定してきましたが、dumpなどの機能を知ることができたので、いい勉強になりました。
全面的に参考にさせていただいたサイト様
大変ありがとうございます・・・!
・MySQLのencodingをutf8からutf8mb4に変更して寿司ビール問題に対応する
・RailsのDBバックアップ(gem:yaml_db)
・Rails5で絵文字を保存する utf8mb4 (docker)
- 投稿日:2020-10-10T19:47:06+09:00
DockerでのRailsアプリケーション構築
概要
Dockerの勉強会の際、作成したリポジトリがありそのまま埋めてるの勿体無いと思ったので記事にしました。
どなたかの参考になればいいなぁと思ってます。https://github.com/yodev21/docker_tutorial_rails
作業手順
ファイル作成
下記ファイルを作成
Dockerfile docker-compose.yml Gemfile Gemfile.lock# イメージ名にRuby(Ver2.6.5)の実行環境のイメージを指定 FROM ruby:2.6.5 # パッケージのリストを更新しrailsの環境構築に必要なパッケージをインストール RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs # プロジェクト用のディレクトリを作成 RUN mkdir /myapp # ワーキングディレクトリに設定 WORKDIR /myapp # プロジェクトのディレクトリにコピー COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock # bundle install実行 RUN bundle install # ビルドコンテキストの内容を全てmyappにコピー COPY . /myappdocker-compose.ymlversion: '3' services: db: # postgresのイメージを取得 image: postgres environment: POSTGRES_USER: 'postgresql' POSTGRES_PASSWORD: 'postgresql-pass' restart: always volumes: - pgdatavol:/var/lib/postgresql/data web: # Dockerfileからイメージをビルドして使用 build: . # コンテナ起動時に実行 command: bundle exec rails s -p 3000 -b '0.0.0.0' # カレントディレクトリを/myappにバインドマウント volumes: - .:/myapp # 3000で公開して、コンテナの3000へ転送 ports: - "3000:3000" # Webサービスを起動する前にdbサービスを起動 depends_on: - db # データ永続化のためにpgdatabolのvolumeを作成し、postgresqlのデータ領域をマウント volumes: pgdatavol:source 'https://rubygems.org' gem 'rails', '5.2.4.2'Gemfile.lockrailsアプリケーション作成
docker-compose run web rails new . --force --database=postgresqlrailsプロジェクトに使用するデータベースの設定ファイルを修正
database.ymldefault: &default adapter: postgresql encoding: unicode # -------- 追加 -------- host: db username: postgresql password: postgresql-pass # -------- ここまで --------デタッチモード(バックグラウンド)で起動
docker-compose up -dbundle installが反映されない場合の対応
docker-compose build --no-cacheデータベース作成コマンド
docker-compose run web rails db:createScaffoldにて簡易的なアプリケーション作成
docker-compose run web bin/rails g scaffold User name:stringdocker-compose run web bin/rails db:migrateコンテナの停止
docker-compose stopコンテナ削除
docker-compose downコンテナ内に移動
docker-compose run web bashGo言語を使用してみる
ディレクトリ移動
cd doc/golang/FROM golang:1.9 RUN mkdir /echo COPY main.go /echo CMD ["go", "run", "/echo/main.go"]イメージのビルド
docker image build -t example/echo:latest .イメージの確認
docker image lsコンテナの起動
docker container run -d -p 9000:8080 example/echo:latestGETリクエストの確認
curl http://localhost:9000
- 投稿日:2020-10-10T19:05:22+09:00
投稿者のみアクセスできるようにする記述
開発環境
Ruby 2.6.5
Rails 6.0.3.3問題点
投稿者のみレビューの削除ができるように実装したいが、NoMethodErrorが出てしまう
原因
当初、条件式を以下のように記述をしておりました。
<% if current_user.id == @review.user_id %>こちらは現在ログインしているユーザーと投稿者が同じ場合、条件式がTrueとなり処理を実行するという記述です。
しかし、こちらの記述で読み込むとエラーが発生しておりました。確認したところ正しくは以下のような記述でした。
<% if user_signed_in? && current_user.id == @review.user_id %>こちらの記述でログインの有無を確認することにより正常に処理を行えるようになりました。
初歩的な箇所ですが、改めて気付いたので、備忘録として投稿いたしました。
- 投稿日:2020-10-10T16:18:35+09:00
アソシエーション(多対多)!!
アソシエーションって何??
簡単に説明するとモデルを利用したテーブル同士の関連付けのことですね。
テーブル同士で関連付けておき、一方のモデルからもう一方のモデルに
アクセスできるようにするためということですね。*今回は、「多対多」のアソシエーションの説明になります。
関連付けを行う理由
Railsでは、「関連付け(アソシエーション: association)」とは2つの
Active Recordモデル同士の繋がりのことですね。2つモデルの間には関連付けを行なう必要がありますが、
その理由を知っていますか?それはですね、関連付けを行う事でコードの共通操作をより
シンプルで簡単にできるからなんです。例:アソシエーション(多対多)
今回は、usersテーブルとroomsテーブルで説明したいと思います。
まずは、テーブルごとに、他テーブルとの関係性を考えましょう。
・ユーザーがどのチャットルームに属しているかを管理する
・チャットルームにどのユーザーが存在するかを管理する今回の下記の画像になります。
ここで注目すべきポイントは、usersテーブルとroomsテーブルの関係性が、
今回のDB設計では「多対多」という関係性が存在していますね。
「多対多」は、関連するテーブルのidをお互いが複数持っている関係性のことです。今回の場合は、「一人のユーザーは複数のチャットルームに所属する」
「一つのチャットルームには複数ユーザーが所属する」という場合の関係性ですね。しかしですね、このテーブルの関係性を示すために、外部キーへ複数の値を
保存できれば簡単なのですが、1つのカラムに対して複数の値を保存することはできません。よく考えられる方法としては、関連するidが増えるごとにカラムを増やすという
パターンがあります。
例としてわかりやすいように下記に画像を載せておきますね。画像を見てもらえるとわかるのですが、カラムを不安と
無駄なカラムが生じていますね。このようなDB設計は、関連するレコードが増えるほどカラムが増えるため、
良くない設計とされているものですね。これを解決するために使用するのが、中間テーブルです。
中間テーブル
では、中間テーブルって何って思われると思うので簡単に説明します。
中間テーブルとは、その名の通り2つのテーブルの中間にあるテーブルのことです。今回で言うと、usersテーブルとroomsテーブルの間に中間テーブルを作成します。
中間テーブルは、「多対多」の関係にある2つのテーブルの間に挟まって、2つの組み合わせパターンだけをレコードとして保存することですね。
また、2つのモデルのみでは「多対多」のアソシエーションを
組むことはできません。
そのため、中間テーブルを利用して「多対多」の関係を定義します。まだこれだけの説明ではイメージがあまりできてないかもしれないので、
次はSNSに例えて説明します。
次の説明からはテーブル名を変えていきますので、先ほどのテーブル名とは異なります。わかりにくかったら申し訳ないです。。タグ付き写真投稿アプリを例に中間テーブルの役割を確認しましょう
『Instagram』などの、一つの写真に複数のタグ付けができる写真投稿アプリをイメージしてみて下さい。
『Instagram』のようにタグ付け機能を実現するには3つのテーブルが
必要です。タグを保存するテーブル、写真を保存するテーブル、
そして「どの写真にどのタグが登録されているか」を保存するテーブルです。このうち写真やタグを保存するテーブルの名前はtagsテーブル、photosテーブルで良いでしょう。「どの写真にどのタグが ~」のテーブルは、photos_tagsテーブルとします。関係する2つのテーブルをアンダーバーで繋いだ名前です。
それぞれのテーブルに保存されているレコードの関係性は以下のようになります。
上記の画像の見てもらうとわかるのですが、中間テーブルには、「どの写真とタグが関連づいているか」という情報が記録されています。一つのレコードには「photo_id × tag_id」の組み合わせが記録され、すべての写真とタグの組み合わせの数だけ、レコードが蓄積されていきます。例えば、10個の写真にそれぞれ、異なるタグが3つずつ付いている場合、これらの関係性を表すための中間テーブルには30個のレコードが生成されることになります。
ここまでが、中間テーブルを用いて情報をDBに保存することの説明です。ここからは、中間テーブルを用いてアソシエーションを設定する方法を確認します。
中間テーブルを経由して「多対多」のテーブルへアソシエーションを組むには、これまで使用してきたhas_manyメソッドに、throughオプションを記述する必要があります。
throughオプションの説明を次にさせて頂きます。throughオプション
has_manyメソッドのthroughオプションは、モデルに多対多の関連を定義するときに利用します。
throughという名前のとおり、「〜を経由する」という意味です。
もっとわかりやすく参考画像を載せておきます。throughオプションを使用し、多対多のアソシエーションを定義する場合は、それぞれのモデルに以下のような記述をします。
models/photo.rbclass Photo < ApplicationRecord has_many :photos_tags has_many :tags, through: :photos_tags endmodels/tag.rbclass Tag < ApplicationRecord has_many :photos_tags has_many :photos, through: :photos_tags endmodels/photos_tag.rbclass PhotosTag < ApplicationRecord belongs_to :photo belongs_to :tag end多対多の関係にある2つのテーブルのモデルでは、has_manyメソッドによる「1対多」のアソシエーションを互いに定義するのと合わせて、
throughオプションによって経由する中間テーブルを指定します。一方、中間テーブルのモデルでは、belongs_toメソッドで多対多の関係にある2つのテーブルを指定します。
以上が、「多対多」のアソシエーションを定義する方法です。
まとめ
・「多対多」のアソシエーションには中間テーブルを用いること
・「多対多」の関係にある2つのテーブルのモデルでは、has_manyメソッド
による「1対多」のアソシエーションを互いに定義するのと合わせて、
throughオプションを使用して経由する中間テーブルを指定すること今回の説明は、長くなってしまって説明がわかりにくくなっていたら、
すいません。。
私なりに頑張ってまとめたので参考になれば嬉しいです。以上。
- 投稿日:2020-10-10T16:08:06+09:00
RailsとVueの一連の流れについてのメモ書き
RailsとVueに関して今まで流れがわからなかったのですが、
色々いじっていたらようやく流れが理解できてきたので見返せるようにメモ書きをします。
初学者なので理解が間違っている可能性が非常に高いため参考程度に!
一般的な正解ではなく、あくまで私の理解と納得解です。環境
MacOS Mojave
Ruby 2.6.4
Rails 6.0.3.3
Vue 2.6.12
Webpack 4.44.2
yarn 1.22.5
Docker 2.3.0.5
VScode
Vuetify
Vue-router
Vue-Storeブラウザからroutes.rbまで
この記事では、以下のURLを使います。
http://localhost:3000/
http://localhost:3000/userはじめに、WebブラウザからURLを入力します。
http://localhost:3000/routes.rbRails.application.routes.draw do root to: 'home#index' get "/user", to: 'home#index' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html endブラウザからRailsサーバへURLが送られます。
http://localhost:3000/のURLを指定しているため
routes.rbの"root to: 'home#index'"の読み込み先が
参照されます。ルートからコントローラー
homeコントローラーのindexの関数が呼び出されます。
home_controller.rbclass HomeController < ApplicationController def index end endちなみに正しいか分かりませんがroutes.rbも参照されるコントローラーも
毎回インスタンスが作成されている認識です。
だからルート間やコントローラ間で関連性がないのです。本題に戻りますが、indexの関数が呼び出されます。
今回何も無いのでindexにマッチするVeiwファイルが参照されますが、
index関数内に@home = Home.allのようなモデルのインスタンスを作成する
プログラムがあった場合、モデルインスタンスが作成されて、
必要あればモデルがデータベースから情報を取得して@homeインスタンスに格納する
ステップがあるはずですが、今回は使用しません。流れとして覚えておきます。コントローラーからVeiwファイル(erbファイル)
コントローラーのindex関数が対応するファイルを参照します。
veiw/home/index.html.erbを参照します。ここでは以下の設定が投入されていますが、
これは、app/javascript/pack内のhello.vueファイルを参照することを意味します。index.html.erb<%= javascript_pack_tag 'hello_vue' %> <%= stylesheet_pack_tag 'hello_vue' %>Veiwファイル(erbファイル)からhello.vue(jsファイル)
Vueアプリケーションを使用するための根幹のファイルですね。
拡張子がVueの各ファイルはオブジェクトでまとまってこのファイルに
結合されるイメージです。render: h => h(App)で分かりますがapp.vueのテンプレートを
呼ぶことで app.vueの内容が表示されます。このファイルはVueの各機能を使えるようにしてくれるものでして
このファイル自体にプロントの内容が記入されているわけではないです。
裏側で管理してくれる感じのファイルです。vuetify, router, store,を定義してVueで使えるようにしています。
親コンポーネントファイルはAppとしています。
SPAを実現しているのはrouterで
データを格納するのがvuexで
UIのフレームワークがvuetifyです。hello_vue.jsimport Vue from 'vue'; import App from '../src/components/app/app'; import Vuetify from 'vuetify'; import router from '../src/router'; import store from "../src/vuex/index" import 'vuetify/dist/vuetify.min.css'; Vue.use(Vuetify); const vuetify = new Vuetify(); document.addEventListener('DOMContentLoaded', () => { const app = new Vue({ vuetify, router, store, render: h => h(App) }).$mount() document.body.appendChild(app.$el) console.log(app) })次に見ていくのはAppファイルです。
hello.vue(jsファイル)からapp.vue(vueコンポーネント)
すごくシンプルにしていますが、
このファイルが親コンポーネントでこのファイルがブラウザに表示されます。
しかし<router-view></router-view>がいることで
hello_vueでroute機能を持たせているためrouteが待ったを掛けます。app.vue<template> <div> <router-view></router-view> </div> </template>app.vue(vueコンポーネント)からrouter.js(URLによってコンポーネントを決める)
route機能を持つとURL情報に従って指定のコンポーネントを返すようになります。
router.jsimport Vue from 'vue'; import VueRouter from 'vue-router'; import Home from "../src/components/home/Home" import User from "../src/components/user/User" Vue.use(VueRouter); export default new VueRouter({ mode: 'history', routes: [{ path: '/', component: Home }, { path: '/user', component: User }] });ブラウザでhttp://localhost:3000/
を打ちましたので{ path: '/', component: Home }によりHomeのコンポーネントを返すように決まりました。
はじめのほうのroutes.rbにてroot to: 'home#index'のルート情報と連動しているのでURL情報をvueでも取得できています。
router.js(URLによってコンポーネントを決める)からHome.vue(子コンポーネント)
VueのルートにてURL情報から表示するコンポーネントを決めたので
ここでの対象コンポーネントであるHome.vueを見ていきます。Home.vue<template> <div> <p>Home</p> <p>{{ store}}</p> </div> </template> <script> export default { computed: { store(){ return this.$store.state.store } } } </script>templateは実際にブラウザに表示される部分でscriptはjsの処理の部分ですね。
template内の<p>{{ store }}</p>を表示したいのでscript処理内を見ていきます。
computed: { store(){ return this.$store.state.store } }と書いてありますが、これは、Vuexを定義しているファイルの
state.storeのデータを持ってきてと言っていますのでvuexを定義しているファイルを見ていきます。Home.vue(子コンポーネント)からindex.js(vuexを定義したファイル)
index.jsimport Vuex from 'vuex'; import Vue from 'vue'; Vue.use(Vuex); export default new Vuex.Store({ state: { store: "Store" } });ファイル名が分かりにくくすいませんがvuexを定義したファイルです。
store:"Store""Store"をstoreで定義しているので、どのコンポーネントからでもstoreと書けば"Store"を呼び出せます。
index.js(vuexを定義したファイル)からHome.vue(子コンポーネント)
再びHome.vueへ戻ります。
<p>{{ store }}</p>を表示したいのでscript処理内を見ていきます。
computed: { store(){ return this.$store.state.store } }scriptでvuexで"Store"を持ってきて、
{{ store }}
に入れて表示するという流れです。Home.vue(子コンポーネント)からapp.vue(vueコンポーネント)
RouteでURLのコンポーネント(Home)が決まったと思います。
それをapp.vueにあった、に代入します。app.vue<template> <div> <router-view></router-view> </div> </template>下の
<router-view></router-view>が
Home.vue<template> <div> <p>Home</p> <p>{{ store}}</p> </div> </template> <script> export default { computed: { store(){ return this.$store.state.store } } } </script>に代入されます。
app.vue(vueコンポーネント)からhello_vue.js
はじめの管理ファイルに戻ってきます。
app.vueファイルが全てオブジェクトになってhello_vue.jsに渡ってきます。hello_vue.jsimport Vue from 'vue'; import App from '../src/components/app/app'; import Vuetify from 'vuetify'; import router from '../src/router'; import store from "../src/vuex/index" import 'vuetify/dist/vuetify.min.css'; Vue.use(Vuetify); const vuetify = new Vuetify(); document.addEventListener('DOMContentLoaded', () => { const app = new Vue({ vuetify, router, store, render: h => h(App) }).$mount() document.body.appendChild(app.$el) console.log(app) })以下のレンダー関数により
render: h => h(App)
全てのVueファイルがオブジェクトになって返されます。
hello_vue.jsからVeiwファイル(erbファイル)
やっとRails側に戻ってきます。
全てのVueファイルがindex.html.erbに結合されます。index.html.erb<%= javascript_pack_tag 'hello_vue' %> <%= stylesheet_pack_tag 'hello_vue' %>Veiwファイル(erbファイル)からコントローラー
ちょっと曖昧なのですが、最後にVeiwファイルをまとめてコントローラーがブラウザに返します。
ブラウザにhttp://localhost:3000/を打ち込んだら
Home.vueに記載されているHomeとStoreが返されました。同じように別のURLを打ったら?
別のURLで試してみます。
http://localhost:3000/user
上記のURLをブラウザで打ち込みます。get "/user", to: 'home#index'が使用されます。
コントローラーは同じで、erbも同じですので割愛します。routes.rbRails.application.routes.draw do root to: 'home#index' get "/user", to: 'home#index' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html endrouter.jsimport Vue from 'vue'; import VueRouter from 'vue-router'; import Home from "../src/components/home/Home" import User from "../src/components/user/User" Vue.use(VueRouter); export default new VueRouter({ mode: 'history', routes: [{ path: '/', component: Home }, { path: '/user', component: User }] });vue側のrouter.jsで以下のURLとコンポーネントが使用されます。
今回はUserコンポーネントです。{ path: '/user', component: User }Userコンポーネントは以下のようにシンプルです。
あとは同じようにrouteがapp.vueに結合してくれてhello_vueで表示してくれます。
後は同じ手順です。User.vue<template> <div> <p>User</p> </div> </template>Userが表示されました。
因みに、routerを使っているのになぜSPAにならないのか
(/と/user)で2回サーバにアクセスしに行っているのかですが、
ブラウザに直接URLを打ち込んでいるからです。Home.vueやUser.vueで各コンポーネントに接続できるようにプログラムすれば
SPAを実現できます。
- 投稿日:2020-10-10T15:47:40+09:00
[Docker]dockerでRails5.2環境構築
前提
dockerとdocker-composeインストール済
1. fileを用意
Gemfile, Gemfile.lock, Dockerfile, Docker-compose.ymlを~/Desktop/product-register下におく。
2. Dockerfile
FROM ruby:2.5 RUN apt-get update && apt-get install -y \ build-essential \ libpg-dev \ nodejs \ postgresql-client \ yarn WORKDIR /product-register COPY Gemfile Gemfile.lock /product-register/ RUN bundle install3. Docker-compose.yml
version: '3' services: web: build: . // imageとってくる場合は image: イメージ名 ports: - '3000:3000' volumes: - '.:/product-register' tty: true stdin_open: true注意点: インデントやスペースがシビア
4. コンテナを作ってRailsアプリを作成
$ docker-compose up -d $ docker-compose exec web bash :/product-register# rails new . --force -database=postgresql -skip-bundle // gemfileが更新されてしまう $ docker-compose down // gemfileを更新するためにコンテナを一度落とす $ docker-compose up --build -d 新しくdockerfileをbuildしたimageを使うために--buildをつける $ docker-compose exec web bash //再度入る :/product-register# rails s -b 0.0.0.0 // dbの設定がまだなのでエラーが出る5. DBの設定
1. 作成されたアプリのconfig/database.ymlの修正
default: &default adapter: postgresql host: db //不思議だがこれでdbサービスを認識してくれる user: postgres port: 5432 password: <%= ENV.fetch("DATABASE_PASSWORD") %>2. docker-compose.yml ファイルのdb部分の追記
version: '3' volumes: db-data: // docker volumeを作っている services: web: build: . ports: - '3000:3000' volumes: - '.:/app' environment: - 'DATABASE_PASSWORD=postgres' tty: true stdin_open: true depends_on: - db links: - db db: image: postgres volumes: - 'db-data:/var/lib/postgresql/data' //上で作ったdockervolumeをマウントしてる environment: - 'POSTGRES_PASSWORD=postgres' //最近必要になったらしい
- 投稿日:2020-10-10T15:21:48+09:00
部分一致検索機能の実装をRansuckを使用せず行う
部分一致検索機能の実装をRansuckを使用せず行ったのでまとめていく。
今回は名前、メールアドレスを入力するとそれに部分一致で該当するユーザーの情報を取得できるという簡単な機能を作る。例 名前フォームに「の」 メールアドレスフォームに「 」で検索 ↓ 野比 のび太 nobinobi@example.com が出力するみたいな感じ。にしたい。結論からということでまずはコード載せますね。
routes.rbRails.application.routes.draw do root 'users#index' resources :users endindex.html<h1>ドラえもんキャラを部分一致検索してみよう</h1> <%= form_with(url: 'users', local: true, method: :get) do |f| %> <span class="small">検索対象: 名前 メールアドレス</span> <div> <%= f.label :name, '名前' %> <%= f.text_field :name , value: @search_params[:name] %> </div> <div> <%= f.label :email, 'メールアドレス' %> <%= f.text_field :email, value: @search_params[:email] %> </div> <%= f.submit '検索', class: 'btn btn-default' %> <%end%> <h1>検索結果</h1> <% @users.each do |user| %> <%= user.name %> <%= user.email %> <% end %>users_controller.rbclass UsersController < ApplicationController def index @search_params = search_params @users = User.search(search_params) end private def search_params params.permit( :name, :email ) end enduser.rbclass User < ApplicationRecord scope :search, -> (search_params) do return if search_params.blank? name_like(search_params[:name] ) .email_like(search_params[:email]) end scope :name_like, -> (name){where("name LIKE ?" ,"%#{name}%")} scope :email_like, -> (email){where("email LIKE ?", "%#{email}%")} endschema.rbActiveRecord::Schema.define(version: 2020_10_10_011424) do create_table "users", force: :cascade do |t| t.string "name" t.string "email" t.datetime "created_at", null: false t.datetime "updated_at", null: false end endseeds.rbUser.create!(name: "野比 のび太",email: "nobinobi@example.com") User.create!(name: "剛田 武",email: "soulmate@example.com") User.create!(name: "骨川 スネ夫",email: "kanemoti@example.com") User.create!(name: "ドラえもん",email: "dora@example.com") User.create!(name: "ドラミちゃん",email: "dorami@example.com") User.create!(name: "源 しずか",email: "piano@example.com")一個一個説明していきます。
まずviewの説明からindex.html<h1>ドラえもんキャラを部分一致検索してみよう</h1> <%= form_with(url: 'users', local: true, method: :get) do |f| %> <span class="small">検索対象: 名前 メールアドレス</span> <div> <%= f.label :name, '名前' %> <%= f.text_field :name , value: @search_params[:name] %> </div> <div> <%= f.label :email, 'メールアドレス' %> <%= f.text_field :email, value: @search_params[:email] %> </div> <%= f.submit '検索', class: 'btn btn-default' %> <%end%> <h1>検索結果</h1> <% @users.each do |user| %> <%= user.name %> <%= user.email %> <% end %>まずここなのですが
<%= form_with(scope: :search ,url: 'users', local: true, method: :get) do |f| %>検索フォームはform_withを使用しています。form_tag と form_forとの違いは下記URLにわかりやすく説明されています。
https://qiita.com/hmmrjn/items/24f3b8eade206ace17e
(以前は関連するモデルがなかったときは form_tag 関連するモデルがある時は form_forを使用していましたがrails5.1で非推奨となっています。具体例として挙げるとUser情報を使いたいだけ(検索時)などはform_tag
User情報を登録したい→登録されるデータベースが存在している時はform_forを使用していた。)
rails5.1以降より推奨されているform_withはそこら辺の処理をうまい感じに切り分けて実行してくれます。またデフォルトで非同期通信のAjaxが実装されてます。今回はJSは使用しない実装なので、リモートフォームを無効にするため local: true と指定し、HTML形式でデータをmethod: get にて通信しています。続いてコントローラーを見ていきましょう。
users_controller.rbclass UsersController < ApplicationController def index @search_params = search_params @users = User.search(search_params) end private def search_params params.permit( :name, :email ) end endここでのみそは User.search(search_params) です。こちらのsearchメソッドなのですがUsermodelの方に部分一致検索機能をscopeしています。送られた値に対しsearchでnameとemailに部分一致処理をし返します。続いてmodelを参照してください。
user.rbclass User < ApplicationRecord scope :search, -> (search_params) do return if search_params.blank? name_like(search_params[:name] ) .email_like(search_params[:email]) end scope :name_like, -> (name){where("name LIKE ?" ,"%#{name}%")} scope :email_like, -> (email){where("email LIKE ?", "%#{email}%")} endsearchスコープはこちらに飛んで一つ一つ部分処理をした後コントローラに返されます。
return if search_params.blank?
を入れておく事で仮に何も値がはいっていなくても処理をそこで止めてコントローラに返します。
検索対象が今回のように:name :emailと複数である場合はparamsを繰り返し処理とし一つ一つ検索を返すようにします。今回は以上となります。
もしここは違うなどありましたどんどん指摘しください。
- 投稿日:2020-10-10T15:21:48+09:00
部分一致検索機能の実装をransuckを使用せず行う
部分一致検索機能の実装をransuckを使用せず行ったのでまとめていく。
今回は名前、メールアドレスを入力するとそれに部分一致で該当するユーザーの情報を取得できるという簡単な機能を作る。例 名前フォームに「の」 メールアドレスフォームに「 」で検索 ↓ 野比 のび太 nobinobi@example.com が出力するみたいな感じ。にしたい。結論からということでまずはコード載せますね。
routes.rbRails.application.routes.draw do root 'users#index' resources :users endindex.html<h1>ドラえもんキャラを部分一致検索してみよう</h1> <%= form_with(url: 'users', local: true, method: :get) do |f| %> <span class="small">検索対象: 名前 メールアドレス</span> <div> <%= f.label :name, '名前' %> <%= f.text_field :name , value: @search_params[:name] %> </div> <div> <%= f.label :email, 'メールアドレス' %> <%= f.text_field :email, value: @search_params[:email] %> </div> <%= f.submit '検索', class: 'btn btn-default' %> <%end%> <h1>検索結果</h1> <% @users.each do |user| %> <%= user.name %> <%= user.email %> <% end %>users_controller.rbclass UsersController < ApplicationController def index @search_params = search_params @users = User.search(search_params) end private def search_params params.permit( :name, :email ) end enduser.rbclass User < ApplicationRecord scope :search, -> (search_params) do return if search_params.blank? name_like(search_params[:name] ) .email_like(search_params[:email]) end scope :name_like, -> (name){where("name LIKE ?" ,"%#{name}%")} scope :email_like, -> (email){where("email LIKE ?", "%#{email}%")} endschema.rbActiveRecord::Schema.define(version: 2020_10_10_011424) do create_table "users", force: :cascade do |t| t.string "name" t.string "email" t.datetime "created_at", null: false t.datetime "updated_at", null: false end endseeds.rbUser.create!(name: "野比 のび太",email: "nobinobi@example.com") User.create!(name: "剛田 武",email: "soulmate@example.com") User.create!(name: "骨川 スネ夫",email: "kanemoti@example.com") User.create!(name: "ドラえもん",email: "dora@example.com") User.create!(name: "ドラミちゃん",email: "dorami@example.com") User.create!(name: "源 しずか",email: "piano@example.com")一個一個説明していきます。
まずviewの説明からindex.html<h1>ドラえもんキャラを部分一致検索してみよう</h1> <%= form_with(url: 'users', local: true, method: :get) do |f| %> <span class="small">検索対象: 名前 メールアドレス</span> <div> <%= f.label :name, '名前' %> <%= f.text_field :name , value: @search_params[:name] %> </div> <div> <%= f.label :email, 'メールアドレス' %> <%= f.text_field :email, value: @search_params[:email] %> </div> <%= f.submit '検索', class: 'btn btn-default' %> <%end%> <h1>検索結果</h1> <% @users.each do |user| %> <%= user.name %> <%= user.email %> <% end %>まずここなのですが
<%= form_with(scope: :search ,url: 'users', local: true, method: :get) do |f| %>検索フォームはform_withを使用しています。form_tag と form_forとの違いは下記URLにわかりやすく説明されています。
https://qiita.com/hmmrjn/items/24f3b8eade206ace17e
(以前は関連するモデルがなかったときは form_tag 関連するモデルがある時は form_forを使用していましたがrails5.1で非推奨となっています。具体例として挙げるとUser情報を使いたいだけ(検索時)などはform_tag
User情報を登録したい→登録されるデータベースが存在している時はform_forを使用していた。)
rails5.1以降より推奨されているform_withはそこら辺の処理をうまい感じに切り分けて実行してくれます。またデフォルトで非同期通信のAjaxが実装されてます。今回はJSは使用しない実装なので、リモートフォームを無効にするため local: true と指定し、HTML形式でデータをmethod: get にて通信しています。続いてコントローラーを見ていきましょう。
users_controller.rbclass UsersController < ApplicationController def index @search_params = search_params @users = User.search(search_params) end private def search_params params.permit( :name, :email ) end endここでのみそは User.search(search_params) です。こちらのsearchメソッドなのですがUsermodelの方に部分一致検索機能をscopeしています。送られた値に対しsearchでnameとemailに部分一致処理をし返します。続いてmodelを参照してください。
user.rbclass User < ApplicationRecord scope :search, -> (search_params) do return if search_params.blank? name_like(search_params[:name] ) .email_like(search_params[:email]) end scope :name_like, -> (name){where("name LIKE ?" ,"%#{name}%")} scope :email_like, -> (email){where("email LIKE ?", "%#{email}%")} endsearchスコープはこちらに飛んで一つ一つ部分処理をした後コントローラに返されます。
return if search_params.blank?
を入れておく事で仮に何も値がはいっていなくても処理をそこで止めてコントローラに返します。
検索対象が今回のように:name :emailと複数である場合はparamsを繰り返し処理とし一つ一つ検索を返すようにします。今回は以上となります。
もしここは違うなどありましたどんどん指摘しください。
- 投稿日:2020-10-10T15:12:41+09:00
Heroku+ActiveStorage+Amazon S3
1 AWSコンソール → サービス → ストレージ → S3でパケット作成
項目 入力・選択 パケット名 ex: my-rails-app-first-bucket リージョン 東京 アクセス権限 チェック欄全てoff 上記以外 デフォルト 2 アクセスキーを作る
下記参照
https://tech-blog.s-yoshiki.com/entry/1353 必要なGemのインストール
gem "aws-sdk-s3", require: false #追記4 S3へのアクセスキーを入力
$ EDITOR=vim rails credentials:edit-vi/vim操作-
iキーで編集開始、escで編集終了 、 ZZで保存して終了
後から入力を確認したいときは$ rails credentials:show5 Herokuでの画像の保存先をAmazon S3に変更
config/storage.ymlamazon: # 以下3行はそのまま service: S3 access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> # 以下2行は変える region: ap-northeast-1 #東京 bucket: my-rails-app-first-bucket #自分で作成したS3のバケットの名前config/environmentas/production.rbconfig.active_storage.service = :amazon #amazonに変更6 最後にHerokuでやること
$ heroku buildpacks:add -i 1 https://github.com/heroku/heroku-buildpack-activestorage-preview $ git add . $ git commit -m "added s3 to production" $ git push heroku masterRailsプロジェックトのマスターキー(config/master.keyの中身)をコピーします
$ heroku config:set RAILS_MASTER_KEY=マスターキーをここに貼り付け $ heroku run rails db:migrate $ heroku open
- 投稿日:2020-10-10T14:54:59+09:00
Trailblazerでcreateを試してみる
はじめに
抽象レイヤーを提供してくれるGem,Trailblazerの存在を知り勉強がてらにCreateを試してみた。
Trailblazerの概要については他の記事でわかりやすく説明していただいてるので割愛とする。こちらの記事がとてもわかりやすかったです。
TrailBlazer概要まとめてみた
環境
ruby '2.7.2'
'rails', '~> 6.0.3'
template engine: hamlCreate
Trailblazerのディレクトリ構成
. └── todo ├── cell │ ├── index.rb │ └── new.rb ├── contract │ └── form.rb ├── operation │ ├── create.rb │ └── index.rb └── view ├── form.haml ├── index.haml └── new.hamlOperation
module Todo::Operation class Create < Trailblazer::Operation class Present < Trailblazer::Operation step Model(Todo, :new) step Contract::Build( constant: Todo::Contract::Form ) end step Subprocess(Present) # present classのstep呼び出し step Contract::Validate(key: :todo) # contractを使ってバリデーション step Contract::Persist() # モデルに保存する end endContract
module Todo::Contract class Form < Reform::Form include Reform::Form::ActiveModel property :title property :description property :completed validates :title, presence: true validates :description, length: { minimum: 2 } # overrideもできる # def title # super.capitalize # end end endCell
module Todo::Cell class New < Trailblazer::Cell include ActionView::RecordIdentifier include ActionView::Helpers::FormOptionsHelper include SimpleForm::ActionViewExtensions::FormHelper end endView
= simple_form_for(model, html: {novalidate: true}) do |f| = f.input :title, placeholder: "Title" %br = f.input :description, as: :text, placeholder: "Description" %br = f.submit 'Submit' = link_to 'Back', todos_path確認
今回試したプロジェクト
参考記事
- 投稿日:2020-10-10T14:15:11+09:00
JQueryでscrollイベントが発火しない問題を解決しました
- 投稿日:2020-10-10T14:14:08+09:00
【Rails】deviseのviewやcontrollerの編集とカスタマイズ方法
はじめに
deviseのviewとcontrollerを編集、カスタマイズする方法
deviseを導入した後、見た目が味気なく英語ばかりのviewを整えたい。
deviseのcontrollerに編集を加えてカスタマイズする必要がある。
そんな時の方法を簡単に紹介します。目次
1.前提
2.devise.rbの設定ファイルを変更
3.contorollerの生成・カスタマイズ
4.viewファイルの生成・編集
5.まとめ開発環境
ruby 2.6.5
rails 6.0.0
devise 4.7.31.前提
devise導入済み
model生成済み
ルーティングは随所で設定実装
それでは実装します。
2.devise.rbの設定ファイルを変更
config/initializers/devise.rbを開きます。config.scoped_viewsを有効にします。
config/initializers/devise.rb# ==> Scopes configuration # Turn scoped views on. Before rendering "sessions/new", it will first check for # "users/sessions/new". It's turned off by default because it's slower if you # are using only default views. config.scoped_views = true #←デフォルトはfalse#Railsサーバを再起動します。
「gemを入れた時」などと同様で再起動が必要です。これを怠るとソースが反映されず「エラーは起きずないが、思い通りにならないという」原因を突き止めるのが難しい状況に陥ります。3.controllerファイルの生成・編集
deviseのcontrollerを生成します。
$ rails g devise:controllers usersこの時の公文は
rails g devise:controllers モデル名
です。
今回はdeviseモデルをuserにしているので(大体userにしますが)その名前を使います。
app > controllers > users >
の下にファイルができます。
例えば、ユーザー登録関連のcontrollerをカスタマイズしたければ、regisrations_controller.rbファイルを編集します。ここで注意が必要なのが、ルーティングの設定です。
ルーティングで記述している[コントローラー名]#[アクション名]
と実際の
[コントローラー]#[アクション]が違うと、当たり前ですがうまく行きません。
なので、rails routes 等でしっかり確認する事をおすすめします。4.viewファイルの生成・編集
deviseのviewファイルを生成・編集します。
$ rails g devise:viewsapp > views > devise >
devise直下にフォルダができます。例えば登録画面を編集したければ、regisrationsのディレクトリにあるファイルを編集します。
ここで注意するべきなのは、viewのファイル名です。
deviseではrenderやredirect_toで遷移先を指定しなければ、コントローラーの処理後メソッド名に遷移する様になってます。
なので、それぞれの名前を合わせる必要があります。5.まとめ
contorollerの生成・カスタマイズとviewファイルの生成・編集を紹介しました。
deviseのcontrollerを使うと、デフォルトで持ってる機能を自動で使えると言うメリットがあります。
しかし、その反面に間違った記述をしてても、裏でdeviseが動いてくれるので、思い通りの挙動にならずエラーも出ないという状況にも陥ります。
その為にも、ルーティング、コントローラー、ビューがどうの様に辿ってきてるか注意する事をおすすめします。最後に
私はプログラミング初学者ですが、自分と同じ様にエンジニアを目指す方々の助けになればと思い、記事を投稿しております。
それではまた次回お会いしましょう〜
- 投稿日:2020-10-10T13:50:08+09:00
【買付代行サービス個人開発 - No.010】会社の切替画面、会社の詳細画面、会社切替後は注文画面に行くようにする
概要
会社の切替画面、会社の詳細画面、会社切替後は注文画面に行くようにする
Figmaに近づけるToDoリスト
- 会社の切替画面修正
- 注文画面修正
- 会社の詳細画面修正
ToDoリスト詳細
ロゴ追加
app/assets/images/svg/sample.svg
追加会社の切替画面修正
app/views/orgs/index.html.slim= render(::Layout::NavbarComponent.new(org: @org, tab: false)) .py-10.px-8 header .max-w-7xl.mx-auto.px-4.sm:px-6.lg:px-8 h1.text-3xl.font-bold.leading-tight.text-gray-900 | 所属会社一覧 main .max-w-7xl.mx-auto.sm:px-6.lg:px-8 .px-4.py-8.sm:px-0 .border-4.border-dashed.border-gray-200.rounded-lg.h-96 .bg-white.shadow.sm:rounded-md.my-16.m-auto(class="w-3/4") ul - @orgs.each do |org| / TODO:Sassで場合分けできるようにする - border_t = org == @orgs.first ? '' : 'border-t border-gray-200' li(class=border_t) = link_to orders_ordering_org_sides_path, class: 'block hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out' do .flex.items-baseline.px-4.py-4.sm:px-6 .min-w-0.flex-1.flex.items-baseline .min-w-0.flex-1.px-4.md:grid.md:grid-cols-3.md:gap-4 div.align-middle .text-xl.font-medium.text-indigo-600.truncate.leading-10 = org.name div .text-xs.text-gray-500 | 会社タイプ .text-lg.leading-5.font-medium.text-gray-900.truncate / TODO: ENUM化する | 買付会社 div .text-xs.text-gray-500 | オーナー .text-lg.leading-5.font-medium.text-gray-900.truncate / TODO: Userモデルの紐付けをしたら、呼び出すようにすする | 山田 太郎 div i.fas.fa-sign-in-alt.fa-lg.text-gray-600
- 注文画面修正
app/views/orders/ordering_org_sides/index.html.slim.bg-gray-100 = render(::Layout::NavbarComponent.new(org: @org, tab: true, active: :order_ordering_org_sides)) / ...略
- 会社の詳細画面修正
app/views/orgs/show.html.slim= render(::Layout::NavbarComponent.new(org: @org, tab: true)) .bg-white.shadow.overflow-hidden.m-auto.mt-16.sm:rounded-lg(class="w-1/2") .px-4.py-5.border-b.border-gray-200.sm:px-6 h3.text-lg.leading-6.font-medium.text-gray-900 | 会社詳細 p.mt-1.max-w-2xl.text-sm.leading-5.text-gray-500 | 会社の詳細を説明します .px-4.py-5.sm:p-0 dl .sm:grid.sm:grid-cols-3.sm:gap-4.sm:px-6.sm:py-5 dt.text-sm.leading-5.font-medium.text-gray-500 | 会社名 dd.mt-1.text-sm.leading-5.text-gray-900.sm:mt-0.sm:col-span-2 = @org.name .mt-8.sm:mt-0.sm:grid.sm:grid-cols-3.sm:gap-4.sm:border-t.sm:border-gray-200.sm:px-6.sm:py-5 dt.text-sm.leading-5.font-medium.text-gray-500 | 会社タイプ dd.mt-1.text-sm.leading-5.text-gray-900.sm:mt-0.sm:col-span-2 / TODO:enum化する | 買付会社 .mt-8.sm:mt-0.sm:grid.sm:grid-cols-3.sm:gap-4.sm:border-t.sm:border-gray-200.sm:px-6.sm:py-5 dt.text-sm.leading-5.font-medium.text-gray-500 | オーナー dd.mt-1.text-sm.leading-5.text-gray-900.sm:mt-0.sm:col-span-2 | 山田太郎動作確認
準備
bin/rails db:migrate:reset bin/rails db:reset受入基準
- デザインがFigmaぽくなっている
- 画面遷移が下図のようになっている。会社の切替後は注文一覧画面に行くようになっている # ドロップダウンのJSは後日実装予定
- 投稿日:2020-10-10T13:38:56+09:00
時刻表記にlメソッドを使う
経緯
店舗の運営時間を表示したくて調べていたところstrftimeというものを使うことで時刻の表記を指定できることがわかった。
view<%= @laundry.open_time.strftime('%-H:%M')%> 〜 <%= @laundry.close_time.strftime('%-H:%M')%>これで
07:00 ~ 23:00
といった表記ができるようになった。
しかしどうもstrftime('%H:%M')
が2回繰り返されていることが気になり他の方法を調べていたところ、lメソッド(エルメソッド)を発見。上記の書き方からlメソッドでの書き方に変更をした過程を記録しておく。lメソッドを使うまでの流れ
1 タイムゾーンを設定(strftimeを使用した時点で設定は済んでいたが今後見返せるように記述しておく)
lメソッドを使用する前にタイムゾーンを設定して日本時間で表示されるようにする必要がある(設定をしていない場合はUTC(世界協定時)
になっており日本時間とは9時間の差がある)
config/application.rb
に以下のように記述を行う。config/application.rbconfig.time_zone = 'Tokyo'2 デフォルトの言語をjaに変更、ymlファイルの作成
1と同じファイル内に以下のように記述をすることで言語を日本語に設定する。config/application.rbconfig.i18n.default_locale = :jaファイルは
config/locales
にja.yml
という名前で作成する。(default_locale
の後に続く国名(日本の場合はja)とconfig/localesの〇〇.yml
)の〇〇を一致させることでファイルを読み込んでくれる)3
ja.yml
にフォーマットを作成するja.ymlja: time: formats: default: '%-H:%M'
default:
の後の記述を変えることで好みの時間表記にすることができる。4 ビューファイルに3で設定した時刻を表示
ここでlメソッドが登場する。lメソッドを使うことでymlファイルで指定したフォーマットを利用して時刻を表示させる。
使い方としては頭にlをつける。view<%= l @laundry.open_time %> 〜 <%= l @laundry.close_time %>これで
strftime
をビューファイルに直書きすることなく好みを時刻表記を表示することができる。view(strftime直書き)<%= @laundry.open_time.strftime('%-H:%M')%> 〜 <%= @laundry.close_time.strftime('%-H:%M')%>view(lメソッド使用)<%= l @laundry.open_time %> 〜 <%= l @laundry.close_time %>lメソッドを使いすっきりとした見た目に変更!!
ちなみにフォーマットを複数用意して使い分け流ことも可能(下記参考記事参照)参考記事
- 投稿日:2020-10-10T13:31:20+09:00
Failure/Error: require File.expand_path('../config/environment', __dir__)の対処法
何が起こったか
rspecコマンドでテストを実行したら,今までなんの問題もなく実行できていたのに突如タイトルのようなエラーが起きました
結果的にbundle updateのせいとわかりました.何気なくbundle updateをしていた身としては「何もしてないのにエラー出た」な気分でした以下rspecコマンドを実行した時に出力された結果です
$ rspec spec/models/task_spec.rb An error occurred while loading ./spec/models/task_spec.rb. Hint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files. Failure/Error: require File.expand_path('../config/environment', __dir__) LoadError: cannot load such file -- public_suffix # ./config/application.rb:7:in `<top (required)>' # ./config/environment.rb:2:in `require_relative' # ./config/environment.rb:2:in `<top (required)>' # ./spec/rails_helper.rb:5:in `<top (required)>' # ./spec/models/task_spec.rb:1:in `<top (required)>' No examples found. Finished in 0.00005 seconds (files took 1.39 seconds to load) 0 examples, 0 failures, 1 error occurred outside of examples解決方法
先に解決方法を書いていきます
このエラーの問題はライブラリのバージョンです
cannot load such file -- public_suffix
とあるようにpublic_suffixというライブラリがバージョンの影響なのか読み込めていません
私の場合はエラー時点でpublic_suffix (4.0.6)
でした
エラーが出ていない前コミットを見るとpublic_suffix (4.0.5)
となっていたので,Gemfile.lockからpublic_suffix (4.0.6)
→public_suffix (4.0.5)
へと変更してbundle installを実行もう一度rspecでテストを実行すると
$ rspec spec/models/task_spec.rb An error occurred while loading ./spec/models/task_spec.rb. Hint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files. Failure/Error: require 'rspec/rails' LoadError: cannot load such file -- minitest/assertions # ./spec/rails_helper.rb:8:in `<top (required)>' # ./spec/models/task_spec.rb:1:in `<top (required)>' No examples found. Finished in 0.00013 seconds (files took 2.54 seconds to load) 0 examples, 0 failures, 1 error occurred outside of examplesと,またエラーが出ましたが
cannot load such file -- minitest/assertions
とminitestのバージョンをpublic_suffixと同様に直してあげます
私の場合はminitest (5.14.2)
→minitest (5.14.1)
にしました
またbundle installしてrspecを実行すると$ rspec spec/models/task_spec.rb Task ほげ2 Finished in 0.00808 seconds (files took 1.99 seconds to load) 1 example, 0 failures通った!直った
原因・解決のためにやったこと
以下,何故このエラーが起こったのか,どんな検索をして解決しようとしたのか書いていきます
問題の本質は
Failure/Error: require File.expand_path('../config/environment', __dir__)
ではなく,cannot load such file -- public_suffix
です
public_suffixというライブラリがあるのですが,それが読み込まれていないことが問題でした
ただGemfile.lockを見るとインストールされています.なのでバージョン違いか?という考えに行きついて解決できました当初はエラーだと思って検索したのが
Failure/Error: require File.expand_path('../config/environment', __dir__)
でした
そのためタイトルにも盛り込んでいます.しかし,検索してもしても問題は解決できず,rspec側の問題では無いということしか分かりませんでしたrails newで初期プロジェクトから改めてrspecを導入してもエラーは消えなかったので,このことからもrspecの問題では無いことが明らかでした
6時間くらい格闘して
Failure/Error: require File.expand_path('../config/environment', __dir__)
は本質的な問題の副次作用だと気がついたのでLoadError: cannot load such file -- public_suffix
がエラーの本質なのだと気がつきました
public_suffixがライブラリだと知らなかったのでそう言うエラーなのかと思ってたのも解決に時間がかかった理由です
あとはGemfile.lockをpublic_suffixで検索して,前コミットと見比べてバージョンが違うことに気が付いたので戻してあげたら直ったのが事の顛末ですちなみに
Failure/Error: require File.expand_path('../config/environment', __dir__)
で検索しても解決には至らなかったので,同じエラーが出た人のためにこの記事を書きました教訓
無闇なbundle updateは辞めましょう!
エラーの原因はライブラリのバージョン変化でしたのでいきなりエラーが出たらバージョンを疑いましょう
そもそも,bundle周りの挙動がわからず,なんとなくで使っていたのが一番のダメ要素でした
bundleで痛い目見たのでここの記事で勉強しました
https://qiita.com/lasershow/items/1a048d03ddaaba98171e付録
public_suffixとminitestのバージョンを直して,rspecが動作するようになったGemfileとGemfile.lockです
Gemfile
ruby '2.6.5' gem 'bootstrap', '4.5.2' gem 'slim-rails', '3.2.0' gem 'html2slim', '0.2.0' gem 'enum_help', '0.0.17' gem 'rails', '~> 6.0.1' gem 'pg', '>= 0.18', '< 2.0' gem 'puma', '~> 4.1' gem 'sass-rails', '>= 6' gem 'webpacker', '~> 4.0' gem 'turbolinks', '~> 5' gem 'jbuilder', '~> 2.7' gem 'bootsnap', '>= 1.4.2' group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] end group :development do gem 'web-console', '>= 3.3.0' gem 'listen', '>= 3.0.5', '< 3.2' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' end group :test do gem 'capybara', '>= 2.15' gem 'selenium-webdriver' gem 'webdrivers' gem 'rspec-rails', '< 4.0.0' end gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]Gemfile.lock
GEM remote: https://rubygems.org/ specs: actioncable (6.0.3.4) actionpack (= 6.0.3.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) actionmailbox (6.0.3.4) actionpack (= 6.0.3.4) activejob (= 6.0.3.4) activerecord (= 6.0.3.4) activestorage (= 6.0.3.4) activesupport (= 6.0.3.4) mail (>= 2.7.1) actionmailer (6.0.3.4) actionpack (= 6.0.3.4) actionview (= 6.0.3.4) activejob (= 6.0.3.4) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) actionpack (6.0.3.4) actionview (= 6.0.3.4) activesupport (= 6.0.3.4) rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) actiontext (6.0.3.4) actionpack (= 6.0.3.4) activerecord (= 6.0.3.4) activestorage (= 6.0.3.4) activesupport (= 6.0.3.4) nokogiri (>= 1.8.5) actionview (6.0.3.4) activesupport (= 6.0.3.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) activejob (6.0.3.4) activesupport (= 6.0.3.4) globalid (>= 0.3.6) activemodel (6.0.3.4) activesupport (= 6.0.3.4) activerecord (6.0.3.4) activemodel (= 6.0.3.4) activesupport (= 6.0.3.4) activestorage (6.0.3.4) actionpack (= 6.0.3.4) activejob (= 6.0.3.4) activerecord (= 6.0.3.4) marcel (~> 0.3.1) activesupport (6.0.3.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) zeitwerk (~> 2.2, >= 2.2.2) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) autoprefixer-rails (10.0.1.0) execjs bindex (0.8.1) bootsnap (1.4.8) msgpack (~> 1.0) bootstrap (4.5.2) autoprefixer-rails (>= 9.1.0) popper_js (>= 1.14.3, < 2) sassc-rails (>= 2.0.0) builder (3.2.4) byebug (11.1.3) capybara (3.33.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (~> 1.5) xpath (~> 3.2) childprocess (3.0.0) concurrent-ruby (1.1.7) crass (1.0.6) diff-lcs (1.4.4) enum_help (0.0.17) activesupport (>= 3.0.0) erubi (1.9.0) execjs (2.7.0) ffi (1.13.1) globalid (0.4.2) activesupport (>= 4.2.0) hpricot (0.8.6) html2slim (0.2.0) hpricot i18n (1.8.5) concurrent-ruby (~> 1.0) jbuilder (2.10.1) activesupport (>= 5.0.0) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) loofah (2.7.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) marcel (0.3.3) mimemagic (~> 0.3.2) method_source (1.0.0) mimemagic (0.3.5) mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.14.1) msgpack (1.3.3) nio4r (2.5.4) nokogiri (1.10.10) mini_portile2 (~> 2.4.0) pg (1.2.3) popper_js (1.16.0) public_suffix (4.0.5) puma (4.3.6) nio4r (~> 2.0) rack (2.2.3) rack-proxy (0.6.5) rack rack-test (1.1.0) rack (>= 1.0, < 3) rails (6.0.3.4) actioncable (= 6.0.3.4) actionmailbox (= 6.0.3.4) actionmailer (= 6.0.3.4) actionpack (= 6.0.3.4) actiontext (= 6.0.3.4) actionview (= 6.0.3.4) activejob (= 6.0.3.4) activemodel (= 6.0.3.4) activerecord (= 6.0.3.4) activestorage (= 6.0.3.4) activesupport (= 6.0.3.4) bundler (>= 1.3.0) railties (= 6.0.3.4) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) railties (6.0.3.4) actionpack (= 6.0.3.4) activesupport (= 6.0.3.4) method_source rake (>= 0.8.7) thor (>= 0.20.3, < 2.0) rake (13.0.1) rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) regexp_parser (1.8.1) rspec-core (3.9.3) rspec-support (~> 3.9.3) rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-rails (3.9.1) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) rspec-core (~> 3.9.0) rspec-expectations (~> 3.9.0) rspec-mocks (~> 3.9.0) rspec-support (~> 3.9.0) rspec-support (3.9.3) ruby_dep (1.5.0) rubyzip (2.3.0) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) railties (>= 4.0.0) sassc (>= 2.0) sprockets (> 3.0) sprockets-rails tilt selenium-webdriver (3.142.7) childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) slim (4.1.0) temple (>= 0.7.6, < 0.9) tilt (>= 2.0.6, < 2.1) slim-rails (3.2.0) actionpack (>= 3.1) railties (>= 3.1) slim (>= 3.0, < 5.0) spring (2.1.1) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.2.2) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) temple (0.8.2) thor (1.0.1) thread_safe (0.3.6) tilt (2.0.10) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) tzinfo (1.2.7) thread_safe (~> 0.1) web-console (4.0.4) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) webdrivers (4.4.1) nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (>= 3.0, < 4.0) webpacker (4.3.0) activesupport (>= 4.2) rack-proxy (>= 0.6.1) railties (>= 4.2) websocket-driver (0.7.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) zeitwerk (2.4.0) PLATFORMS ruby DEPENDENCIES bootsnap (>= 1.4.2) bootstrap (= 4.5.2) byebug capybara (>= 2.15) enum_help (= 0.0.17) html2slim (= 0.2.0) jbuilder (~> 2.7) listen (>= 3.0.5, < 3.2) pg (>= 0.18, < 2.0) puma (~> 4.1) rails (~> 6.0.1) rspec-rails (< 4.0.0) sass-rails (>= 6) selenium-webdriver slim-rails (= 3.2.0) spring spring-watcher-listen (~> 2.0.0) turbolinks (~> 5) tzinfo-data web-console (>= 3.3.0) webdrivers webpacker (~> 4.0) RUBY VERSION ruby 2.6.5p114 BUNDLED WITH 2.1.4
- 投稿日:2020-10-10T13:31:20+09:00
RSpecでFailure/Error: require File.expand_path('../config/environment', __dir__)が出たときの対処法
何が起こったか
rspecコマンドでテストを実行したら,今までなんの問題もなく実行できていたのに突如タイトルのようなエラーが起きました
結果的にbundle updateのせいとわかりました.何気なくbundle updateをしていた身としては「何もしてないのにエラー出た」な気分でした以下rspecコマンドを実行した時に出力された結果です
$ rspec spec/models/task_spec.rb An error occurred while loading ./spec/models/task_spec.rb. Hint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files. Failure/Error: require File.expand_path('../config/environment', __dir__) LoadError: cannot load such file -- public_suffix # ./config/application.rb:7:in `<top (required)>' # ./config/environment.rb:2:in `require_relative' # ./config/environment.rb:2:in `<top (required)>' # ./spec/rails_helper.rb:5:in `<top (required)>' # ./spec/models/task_spec.rb:1:in `<top (required)>' No examples found. Finished in 0.00005 seconds (files took 1.39 seconds to load) 0 examples, 0 failures, 1 error occurred outside of examples解決方法
先に解決方法を書いていきます
このエラーの問題はライブラリのバージョンです
cannot load such file -- public_suffix
とあるようにpublic_suffixというライブラリがバージョンの影響なのか読み込めていません
私の場合はエラー時点でpublic_suffix (4.0.6)
でした
エラーが出ていない前コミットを見るとpublic_suffix (4.0.5)
となっていたので,Gemfile.lockからpublic_suffix (4.0.6)
→public_suffix (4.0.5)
へと変更してbundle installを実行もう一度rspecでテストを実行すると
$ rspec spec/models/task_spec.rb An error occurred while loading ./spec/models/task_spec.rb. Hint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files. Failure/Error: require 'rspec/rails' LoadError: cannot load such file -- minitest/assertions # ./spec/rails_helper.rb:8:in `<top (required)>' # ./spec/models/task_spec.rb:1:in `<top (required)>' No examples found. Finished in 0.00013 seconds (files took 2.54 seconds to load) 0 examples, 0 failures, 1 error occurred outside of examplesと,またエラーが出ましたが
cannot load such file -- minitest/assertions
とminitestのバージョンをpublic_suffixと同様に直してあげます
私の場合はminitest (5.14.2)
→minitest (5.14.1)
にしました
またbundle installしてrspecを実行すると$ rspec spec/models/task_spec.rb Task ほげ2 Finished in 0.00808 seconds (files took 1.99 seconds to load) 1 example, 0 failures通った!直った
原因・解決のためにやったこと
以下,何故このエラーが起こったのか,どんな検索をして解決しようとしたのか書いていきます
問題の本質は
Failure/Error: require File.expand_path('../config/environment', __dir__)
ではなく,cannot load such file -- public_suffix
です
public_suffixというライブラリがあるのですが,それが読み込まれていないことが問題でした
ただGemfile.lockを見るとインストールされています.なのでバージョン違いか?という考えに行きついて解決できました当初はエラーだと思って検索したのが
Failure/Error: require File.expand_path('../config/environment', __dir__)
でした
そのためタイトルにも盛り込んでいます.しかし,検索してもしても問題は解決できず,rspec側の問題では無いということしか分かりませんでしたrails newで初期プロジェクトから改めてrspecを導入してもエラーは消えなかったので,このことからもrspecの問題では無いことが明らかでした
6時間くらい格闘して
Failure/Error: require File.expand_path('../config/environment', __dir__)
は本質的な問題の副次作用だと気がついたのでLoadError: cannot load such file -- public_suffix
がエラーの本質なのだと気がつきました
public_suffixがライブラリだと知らなかったのでそう言うエラーなのかと思ってたのも解決に時間がかかった理由です
あとはGemfile.lockをpublic_suffixで検索して,前コミットと見比べてバージョンが違うことに気が付いたので戻してあげたら直ったのが事の顛末ですちなみに
Failure/Error: require File.expand_path('../config/environment', __dir__)
で検索しても解決には至らなかったので,同じエラーが出た人のためにこの記事を書きました教訓
無闇なbundle updateは辞めましょう!
エラーの原因はライブラリのバージョン変化でしたのでいきなりエラーが出たらバージョンを疑いましょう
そもそも,bundle周りの挙動がわからず,なんとなくで使っていたのが一番のダメ要素でした
bundleで痛い目見たのでここの記事で勉強しました
https://qiita.com/lasershow/items/1a048d03ddaaba98171e付録
public_suffixとminitestのバージョンを直して,rspecが動作するようになったGemfileとGemfile.lockです
Gemfile
ruby '2.6.5' gem 'bootstrap', '4.5.2' gem 'slim-rails', '3.2.0' gem 'html2slim', '0.2.0' gem 'enum_help', '0.0.17' gem 'rails', '~> 6.0.1' gem 'pg', '>= 0.18', '< 2.0' gem 'puma', '~> 4.1' gem 'sass-rails', '>= 6' gem 'webpacker', '~> 4.0' gem 'turbolinks', '~> 5' gem 'jbuilder', '~> 2.7' gem 'bootsnap', '>= 1.4.2' group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] end group :development do gem 'web-console', '>= 3.3.0' gem 'listen', '>= 3.0.5', '< 3.2' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' end group :test do gem 'capybara', '>= 2.15' gem 'selenium-webdriver' gem 'webdrivers' gem 'rspec-rails', '< 4.0.0' end gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]Gemfile.lock
GEM remote: https://rubygems.org/ specs: actioncable (6.0.3.4) actionpack (= 6.0.3.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) actionmailbox (6.0.3.4) actionpack (= 6.0.3.4) activejob (= 6.0.3.4) activerecord (= 6.0.3.4) activestorage (= 6.0.3.4) activesupport (= 6.0.3.4) mail (>= 2.7.1) actionmailer (6.0.3.4) actionpack (= 6.0.3.4) actionview (= 6.0.3.4) activejob (= 6.0.3.4) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) actionpack (6.0.3.4) actionview (= 6.0.3.4) activesupport (= 6.0.3.4) rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) actiontext (6.0.3.4) actionpack (= 6.0.3.4) activerecord (= 6.0.3.4) activestorage (= 6.0.3.4) activesupport (= 6.0.3.4) nokogiri (>= 1.8.5) actionview (6.0.3.4) activesupport (= 6.0.3.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) activejob (6.0.3.4) activesupport (= 6.0.3.4) globalid (>= 0.3.6) activemodel (6.0.3.4) activesupport (= 6.0.3.4) activerecord (6.0.3.4) activemodel (= 6.0.3.4) activesupport (= 6.0.3.4) activestorage (6.0.3.4) actionpack (= 6.0.3.4) activejob (= 6.0.3.4) activerecord (= 6.0.3.4) marcel (~> 0.3.1) activesupport (6.0.3.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) zeitwerk (~> 2.2, >= 2.2.2) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) autoprefixer-rails (10.0.1.0) execjs bindex (0.8.1) bootsnap (1.4.8) msgpack (~> 1.0) bootstrap (4.5.2) autoprefixer-rails (>= 9.1.0) popper_js (>= 1.14.3, < 2) sassc-rails (>= 2.0.0) builder (3.2.4) byebug (11.1.3) capybara (3.33.0) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (~> 1.5) xpath (~> 3.2) childprocess (3.0.0) concurrent-ruby (1.1.7) crass (1.0.6) diff-lcs (1.4.4) enum_help (0.0.17) activesupport (>= 3.0.0) erubi (1.9.0) execjs (2.7.0) ffi (1.13.1) globalid (0.4.2) activesupport (>= 4.2.0) hpricot (0.8.6) html2slim (0.2.0) hpricot i18n (1.8.5) concurrent-ruby (~> 1.0) jbuilder (2.10.1) activesupport (>= 5.0.0) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) loofah (2.7.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) marcel (0.3.3) mimemagic (~> 0.3.2) method_source (1.0.0) mimemagic (0.3.5) mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.14.1) msgpack (1.3.3) nio4r (2.5.4) nokogiri (1.10.10) mini_portile2 (~> 2.4.0) pg (1.2.3) popper_js (1.16.0) public_suffix (4.0.5) puma (4.3.6) nio4r (~> 2.0) rack (2.2.3) rack-proxy (0.6.5) rack rack-test (1.1.0) rack (>= 1.0, < 3) rails (6.0.3.4) actioncable (= 6.0.3.4) actionmailbox (= 6.0.3.4) actionmailer (= 6.0.3.4) actionpack (= 6.0.3.4) actiontext (= 6.0.3.4) actionview (= 6.0.3.4) activejob (= 6.0.3.4) activemodel (= 6.0.3.4) activerecord (= 6.0.3.4) activestorage (= 6.0.3.4) activesupport (= 6.0.3.4) bundler (>= 1.3.0) railties (= 6.0.3.4) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) railties (6.0.3.4) actionpack (= 6.0.3.4) activesupport (= 6.0.3.4) method_source rake (>= 0.8.7) thor (>= 0.20.3, < 2.0) rake (13.0.1) rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) regexp_parser (1.8.1) rspec-core (3.9.3) rspec-support (~> 3.9.3) rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-rails (3.9.1) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) rspec-core (~> 3.9.0) rspec-expectations (~> 3.9.0) rspec-mocks (~> 3.9.0) rspec-support (~> 3.9.0) rspec-support (3.9.3) ruby_dep (1.5.0) rubyzip (2.3.0) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) railties (>= 4.0.0) sassc (>= 2.0) sprockets (> 3.0) sprockets-rails tilt selenium-webdriver (3.142.7) childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) slim (4.1.0) temple (>= 0.7.6, < 0.9) tilt (>= 2.0.6, < 2.1) slim-rails (3.2.0) actionpack (>= 3.1) railties (>= 3.1) slim (>= 3.0, < 5.0) spring (2.1.1) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.2.2) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) temple (0.8.2) thor (1.0.1) thread_safe (0.3.6) tilt (2.0.10) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) tzinfo (1.2.7) thread_safe (~> 0.1) web-console (4.0.4) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) webdrivers (4.4.1) nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (>= 3.0, < 4.0) webpacker (4.3.0) activesupport (>= 4.2) rack-proxy (>= 0.6.1) railties (>= 4.2) websocket-driver (0.7.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) zeitwerk (2.4.0) PLATFORMS ruby DEPENDENCIES bootsnap (>= 1.4.2) bootstrap (= 4.5.2) byebug capybara (>= 2.15) enum_help (= 0.0.17) html2slim (= 0.2.0) jbuilder (~> 2.7) listen (>= 3.0.5, < 3.2) pg (>= 0.18, < 2.0) puma (~> 4.1) rails (~> 6.0.1) rspec-rails (< 4.0.0) sass-rails (>= 6) selenium-webdriver slim-rails (= 3.2.0) spring spring-watcher-listen (~> 2.0.0) turbolinks (~> 5) tzinfo-data web-console (>= 3.3.0) webdrivers webpacker (~> 4.0) RUBY VERSION ruby 2.6.5p114 BUNDLED WITH 2.1.4
- 投稿日:2020-10-10T13:21:48+09:00
DockerでRails5、postgresqlの環境を作り、pgadminも使えるようにする
mysqlしかdockerで使ったことがなくて、時間がかかってしまったので、後で見れるように書きました。
pgadminも便利なので入れました。
docoker-compose up
したあとrails s
しなくてもいいようにdocker-compose.ymlにcommandで自動でやってくれるように書きました。普段はコンテナ再作成しなくてもよいと思うので
docker-compose start / stop
でやる想定です。DockerfileFROM ruby:2.6 RUN apt-get update -y && \ apt-get install -y nodejs COPY Gemfile /Gemfile COPY Gemfile.lock /Gemfile.lock RUN gem install bundler RUN bundle installdocker-compose.ymlversion: "3" services: db: image: postgres ports: - 5432:5432 environment: POSTGRES_USER: root POSTGRES_PASSWORD: root volumes: - "./postgres-data:/var/lib/postgresql/data" pgadmin4: image: dpage/pgadmin4:4.2 ports: - 80:80 volumes: - ./docker/pgadmin4:/var/lib/pgadmin environment: PGADMIN_DEFAULT_EMAIL: root PGADMIN_DEFAULT_PASSWORD: root depends_on: - db web: build: . volumes: - ".:/app" ports: - "3000:3000" tty: true depends_on: - db working_dir: "/app" command: "rails s -b 0.0.0.0"mysqlとdatabase.ymlが違っていて、ここらへんを書くところをdevelopとかに書いてしまっていてハマりました。ちゃんとdefaultに書かなくてはいけませんでした。
config/database.ymldefault: &default adapter: postgresql encoding: unicode # For details on connection pooling, see Rails configuration guide # http://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> # ここらへん username: root password: root host: db
- 投稿日:2020-10-10T12:10:16+09:00
【Rails】ActiveStorageを用いた画像複数枚投稿のエラー
エラー【undefined method `to_model'】
フリマアプリの開発中、以下のようなエラーが出ました。
Can't resolve image into URL: undefined method
to_model' for #<ActiveStorage::Attached::Many:0x00007fb7ffa59fb0>
Did you mean? to_yaml現状
- ActiveStorageというGemをを用いて画像保存を可能にしている。
- 1つの商品につき複数の画像の投稿を可能にした。←今ここ
エラーの内容
to_model
というメソッドは定義されていないよーって言われてます。以下のように、保存した画像を表示させたい時にエラーがでました。
問題があったコード
<%= image_tag @item.images, class: 'buy-item-img' %>解決したコード
<%= image_tag @item.images[0], class: 'buy-item-img' %>一つの商品に複数枚画像があるため、どの画像を表示させるかを記述する必要があります。
そうじゃなかったらどの画像を表示するのか判断できないですもんね?他の方の記事などを見てみると、
@item.images.url
と記述すれば解決することもあったそうです。
参考になれば、と思います!
- 投稿日:2020-10-10T11:19:28+09:00
[初心者でもできた]RailsアプリにDockerを導入する方法!
こんにちは。「Gitって何???」って状態から独学でプログラミングを勉強し始め、今はRails API × Nuxt SPA × Firebase AuthでTodoアプリを作成し終えたところです。
そこにDockerを導入したので今回はその導入方法をご紹介します。
はじめに
Dockerを導入したいと思うようになったきっかけの記事
Vue.jsとRails APIモードでストレスフリーに少人数開発できた話
自分でもDockerを導入できれば今後チーム開発で役立つと確信した。とりあえずDocker試す
で、なにから始めればいいの?すでにアプリ完成してるんだけど、、、
既存のRailsアプリにDockerを導入できるのかな、、、まず試したのがこの記事。
丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)
10分くらいでできたのでDockerって意外と簡単かもって少し思えました。では、実際にRailsアプリにDockerを導入していく。
Docker導入
まずはインストール
docker for mac~$ docker -v Docker version 19.03.12, build 48a66213fe ~$ docker-compose -v docker-compose version 1.26.2, build eefe0d31Dockerを導入したいRailsアプリに移動
~ $ cd backendDockerファイルを作成
以下を参考
既存のRailsアプリにDockerを導入する手順~/backend $ vi DockerfileFROM ruby:2.7.1 RUN apt-get update && apt-get install -y nodejs --no-install-recommends && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y postgresql-client --no-install-recommends && rm -rf /var/lib/apt/lists/* RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs WORKDIR /myproject ADD Gemfile /myproject/Gemfile ADD Gemfile.lock /myproject/Gemfile.lock RUN gem install bundler RUN bundle install ADD . /myproject?FROMでどのDockerイメージからコンテナを生成するか記述
使用できるコンテナイメージはdockerhubから確認します。
今回はrails2.7.1をインストールしました
(Dockerfileで指定するバージョンは、プロジェクトのRubyバージョンに合わせておく必要があります)?RUNでコマンド実行
mysqlだとエラーが出すぎて嫌になったので今回はpostgresqlを使用?WORKDIRで各種命令実行時のカレントディレクトリを指定
?ファイル / ディレクトリの追加
docker-compose.yml作成
~/backend $ vi docker-compose.ymldocker-compose.yml#docker-composeのバージョン version: '3' services: db: image: postgres ports: - '5432:5432' # ホストからゲストへポートフォワード volumes: - postgresql-data:/var/lib/postgresql/data environment: - POSTGRES_PASSWORD=password web: build: context: . dockerfile: Dockerfile command: bundle exec rails s -p 3000 -b '0.0.0.0' tty: true stdin_open: true depends_on: - db # DB側のコンテナが出来上がってからwebを実行する ports: - "3000:3000" # ホストからゲストへポートフォワード volumes: - .:/myproject # ソースコード変更したらDocker側も即反映されるように volumes: postgresql-data: driver: local?僕はPostgreSQL(Docker)にRails(Docker)が接続できなくなったのでpostgresコンテナに環境変数POSTGRES_PASSWORDを設定して、その値をconfig/database.ymlのpasswordに定義しました。
database.yml編集
database.ymldefault: &default adapter: postgresql encoding: unicode host: db username: postgres password: password pool: 5 development: <<: *default database: myproject_development test: <<: *default database: myproject_test?アンカー: 名前をつける機能
&default?エイリアス: 名前をつけたものを呼び出す
*default?ハッシュのマージ: 一つにまとめる
<<: *defaultdatabase.ymldefault: &default #ここで共通部分にアンカーで名前をつけておくと adapter: postgresql encoding: unicode host: db username: postgres password: password pool: 5 development: <<: *default #ここでdefaultとして呼び出し、スッキリ書ける database: myproject_development test: <<: *default #ここでdefaultとして呼び出し、スッキリ書ける database: myproject_test参考
【YAML】Railsのdatabase.ymlについてなんとなく分かった気になっていた記法・意味まとめbuild
サービスのビルドを実行します。
サービスとは「web」や「db」のことを指します。ymlファイルにimage:が書かれている場合はそのイメージ名がローカルになければ、リモートからプルしてきます。imageが書かれていない場合、buildに書かれているパスの(デフォルトは)Dockerfileを参考にしてイメージを構築します。~/backend $ docker-compose build~/backend $ docker-compose run web rake db:create db:migrate ~/backend $ docker-compose upこれで localhost:3000 にアクセスしたら確認できます!
僕は仕事終わりに試してはエラーがでて、、、を繰り返しなんだかんだ導入に一週間は使いました笑
最後に
今回、未経験からWeb系エンジニアに転職したく勉強しました。
Dockerのメリットなど知らないことが多いので、これを機にもっと勉強します。
1月からの入社を目指してがんばるぞ〜
- 投稿日:2020-10-10T10:17:51+09:00
with_optionの使い方
with_optionとは
同じオプションを使った記述が複数存在している場合、
それらを一つにまとめることができるもの。
記述量を少なくできることが利点。使い方
例 app/models/item.rbファイルに、以下の同じオプションを使っているコードが複数あるとする。
validates :category_id, numericality: { other_than: 1 } validates :days_to_send_id, numericality: { other_than: 1 } validates :item_condition_id, numericality: { other_than: 1 } validates :prace_id, numericality: { other_than: 1 } validates :fee_id, numericality: { other_than: 1 }この時、with_optionを使えば、以下のように記述量を少なく、みやすいコードにすることができる。
with_option numericality: { other_than:1} do validates :category_id validates :days_to_send validates :item_condition_id validates :prace_id validates :fee_id end以上になります。
- 投稿日:2020-10-10T09:30:10+09:00
Railsで本番環境(EC2, AmazonLinux)でPDF出力ができない
本番環境(EC2, AmazonLinux)でPDF出力ができない
wicked_pdf
とwkhtmltopdf-binary
というGemを使ってPDF出力機能を付けていますが、開発環境ではうまく行くのに本番環境では下記のエラーが発生。
Railsのバージョンは4.2です。RuntimeError (Failed to execute: ["/var/www/~~~/shared/bundle/ruby/2.4.0/gems/wkhtmltopdf-binary-0.12.6.3/bin/wkhtmltopdf", "--encoding", "UTF-8", "--page-size", "A4", "file:////tmp/wicked_pdf20201007-11835-gppxfs.html", "/tmp/wicked_pdf_generated_file20201007-11835-j83wu8.pdf"] Error: PDF could not be generated! Command Error: /var/www/~~~/shared/bundle/ruby/2.4.0/gems/wkhtmltopdf-binary-0.12.6.3/bin/wkhtmltopdf_centos_7_amd64: error while loading shared libraries: libpng15.so.15: cannot open shared object file: No such file or directory ):
libpng15.so.15
というライブラリがない様ですが、同様のエラーをネットで調べて色々と試しましたが解決しませんでした。Gemを変更し解決
2016年の記事ですが、下記が参考になりました。
https://qiita.com/s-mori/items/00aef46e6a10499f8254
https://qiita.com/yaboojp/items/526c9397070ca5d05256
wkhtmltopdf-binary
はAmazonLinuxに対応していない様で、
AmazonLinuxに対応しているwkhtmltopdf-binary-aml
を使うことでうまくいきました。修正前のGemfile
gem 'wicked_pdf' gem 'wkhtmltopdf-binary-aml'修正後のGemfile
gem 'wicked_pdf' gem 'wkhtmltopdf-binary-aml', git: 'https://github.com/insphire/wkhtmltopdf-binary-aml'修正前の
wicked_pdf
config/initializers/wicked_pdf.rbWickedPdf.config = { :exe_path => "#{Gem.loaded_specs['wkhtmltopdf-binary'].full_gem_path}/bin/wkhtmltopdf" }修正後の
wicked_pdf
config/initializers/wicked_pdf.rbWickedPdf.config = { :exe_path => "#{Gem.loaded_specs['wkhtmltopdf-binary-aml'].full_gem_path}/bin/wkhtmltopdf" }Bundlerのバージョンが変更されない様にして、bundle installしました。
$ bundle _1.16.1_ install
日本語表示に対応させる
本番環境にデプロイすると、日本語表示がされていませんでした。
なのでIPAフォントを本番サーバでインストールします。cd /usr/share/fonts $ yum install -y ipa-gothic-fonts ipa-mincho-fontsフォントを変更したことでレイアウトが崩れてしまったので、CSSなどを整えて、領収書機能を完成させることができました。
開発環境でのエラー
AmazonLinux対応のGemに変更したことで、今度は開発環境でエラーが起こる様になってしまいました。
RuntimeError - PDF could not be generated! Command Error: /Users/~~~/vendor/bundle/ruby/2.4.0/bundler/gems/wkhtmltopdf-binary-aml-e5340ed88aa8/bin/wkhtmltopdf:15:in `exec': Bad CPU type in executable - /Users/~~~/vendor/bundle/ruby/2.4.0/bundler/gems/wkhtmltopdf-binary-aml-e5340ed88aa8/libexec/wkhtmltopdf-darwin-x86 (Errno::E086)開発環境でも本番環境でもうまくやるには、
wicked_pdf.rb
をif Rails.env.production?
などを使ってかき分けたり、
Gemfileを以下の様にして環境別に切り替えればできます。group :development do gem 'wkhtmltopdf-binary' endgem 'wkhtmltopdf-binary', group: :development
- 投稿日:2020-10-10T00:33:58+09:00
Dockerと仮装サーバーとコンテナについて
Docker!仮想サーバー!コンテナ!について
Dockerとは
コンテナ化を用いてアプリケーションを開発・配置・実行するためのオープンソースソフトウェアのこと!
仮想サーバーとは
1台のサーバーで仮想的に複数のサーバーを稼働させる仕組みのこと!
コンテナとは
他のユーザーから隔離された実行環境のこと。仮想サーバーに比べて起動時間が短く、同じ性能のハードウェアであれば、より多くのコンテナを同時に動かすことができる!
デプロイの種類について
カナリアリリースとは
一部のユーザーにのみ新機能の公開を行い、新しいバージョンにバグなどがないかを検証する手法のこと。サービスに不具合があっても全体に影響しませんよ!というもの。
ブルーグリーンデプロイメントとは
仮想サーバーを用い、2つの本番環境を用意し、それぞれバージョンを設定することができるデプロイの運用方法のこと。2つ用意するからどちらかに不具合が生じた場合どちらかにリクエストの方向を変える事ができる為、その間に不具合が生じた方をロールバックする事ができる!
イミュータブルデプロイメントとは
常に変更を行わない環境を構築することで、デプロイ時に新しい環境に切り替える手法のこと。古い環境を消去しますよというもの!
Docker公式 http://docs.docker.jp/
まとめ
Dockerについて概要を知るkとができましたね。おまけでクラウドの種類もよく使われるので載せておきます。
おまけ
SaaS
「Software as a Service」の略で、「サース」または「サーズ」と読む。
クライアント側に導入せずに、サービスを提供しているサーバーに直接アクセスをしてサービスを利用する状況を指す!
(例)
Microsoft Office 365などのオフィスソフト
GmailなどのWebメール
Dropboxなどのオンラインストレージ
サイボウズなどのグループウェアPaaS
「Platform as a Service」の略で、「パース」と読む。
作成したアプリケーションなどを、ネットワーク上に公開するためのプラットフォームを提供するサービスのこと!
(例)
HerokuIaaS
「Infrastructure as a Service 」の略で、「イアース」や「アイアース」と読む。
サービスを利用するユーザーが、仮想化をしたCPUやメモリ、ストレージなどをインターネット経由で提供するサービスのこと!
(例)
Microsoft Azure
Google Compute Engine現場からは以上です!
- 投稿日:2020-10-10T00:23:55+09:00
[Ruby on rails] いいね機能の実装
はじめに
投稿アプリで他のユーザーがいいねを出来るようにいいね機能を実装。
usersテーブルとpostsテーブルとlikesテーブルがあるものとします。アソシエーション
まずは、各テーブルの関係性を考え、アソシエーションを定義します。
ユーザー(1):いいね(多)
投稿(1):いいね(多)
1人1投稿に1回のいいねまでにしたいのでバリデーションもかけます。like.rbclass Like < ApplicationRecord belongs_to :user belongs_to :post validates_uniqueness_of :post_id, scope: :user_id end投稿が削除されたされた場合いいねも削除。
post.rbhas_many :likes, dependent: :destroyuser.rbhas_many :likes, dependent: :destroy def already_liked?(post) self.likes.exists?(post_id: post.id) endコントローラー実装
likes_controller.rbclass LikesController < ApplicationController def create @like = current_user.likes.create(post_id: params[:post_id]) redirect_back(fallback_location: root_path) end def destroy @post = Post.find(params[:post_id]) @like = current_user.likes.find_by(post_id: @post.id) @like.destroy redirect_back(fallback_location: root_path) end endルーティングの設定
routes.rbresources :posts do resource :likes, only: [:create, :destroy] end
post_likes
DELETE /posts/:post_id/likes(.:format) likes#destroy
POST /posts/:post_id/likes(.:format) likes#create
viewの実装
~.html.erb<% if current_user.already_liked?(post) %> <%= link_to post_likes_path(post), method: :delete do %> <i class="fas fa-heart"></i> <% end %> <% else %> <%= link_to post_likes_path(post), method: :post do %> <i class="far fa-heart"></i> <% end %> <% end %> <%= post.likes.count %> //いいねの数を表示すでにcurrent_userがいいねしているか?
trueであればいいねを解除
して、falseであればいいね
をする。最後に
いいねの実装方法は他にも非同期での実装などもあります。
まだまだ勉強中ですが、色々な技術を勉強し出来ることを増やしていきたいと思います。
最後まで読んでいただきありがとうございます