- 投稿日:2021-02-26T21:19:32+09:00
Couldn't find User without an ID エラーを解決した
rails初学者です。
現在オリジナルアプリを開発しております。
ユーザー詳細機能を実装しようとして、ユーザー詳細画面へのリンクを設定し、動作確認してみたら
「Couldn't find User without an ID」
というエラーが発生したため、エラー解決をしました。環境
ruby '2.6.5'
rails '6.0.0'ビュー
<%= link_to "#{current_user.name}", user_path(current_user) %>このように、ログイン中のユーザーの名前を表示し、名前にユーザー詳細ページへ飛ぶリンクを設定しました。
Usersコントローラー
def show @user = User.find(params[:id]) endUsersコントローラーのshowアクションで、パラメータのidを受け取って
そのidに合致するユーザーを@userに格納しました。挙動確認
予想される挙動は、「ユーザー名をクリックするとユーザー詳細ページに遷移する」
ですが、いざクリックしたところ、Usersコントローラーのshowアクションで下記のようなエラーメッセージが出ました。ActiveRecord::RecordNotFound in UsersController#show Couldn't find User without an ID簡単に言うとIDがありませんよ、ということです。
パラメーターの確認
showアクションにbinding.pryを仕込み、パラメーターを確認してみました。
すると下記のような結果でした。pry(#<UsersController>)> params => <ActionController::Parameters {"controller"=>"users", "action"=>"show", "format"=>"1"} permitted: false>本来ならばid="1"と出て欲しいところが、format="1"となっており
パラメーターにidが含まれていないようです。結論
ルーティングの記述にミスがありました。
resource :users, only: :show'resources'と書かなければならないところを、'resource'と書いてしまっておりました。
resourceメソッドはresourcesメソッドに対して、indexアクションとid付パスを生成してくれないみたいです。
そのため、パラメーターにidが含まれていませんでした。解決策
ルーティングを
resources :users, only: :showと書き直しました。
再度挙動を確かめたところ、問題なくユーザー詳細ページに遷移しました。
参考
- 投稿日:2021-02-26T21:10:16+09:00
Rails「paramsの使用法」
はじめに
今回、テキストの一覧ページを作成する際、link_toとhelperよりparamsの使用方法を復習できたので共有します。
やりたいこと
・viewで各教材の一覧ページへのリンクを設定する
・controllerで各教材の一覧ページに適した教材を取得する
・helperで一覧ページのタイトルを表示する処理を書くviewで各教材の一覧ページへのリンクを設定する
_header.html.erb<%= link_to "Ruby/Rails教材", texts_path, class: "dropdown-item" %> <%= link_to "AWS講座", texts_path(genre: "AWS"), class: "dropdown-item" %> <%= link_to "PHPテキスト教材", texts_path(genre: "Php"), class: "dropdown-item" %><%= link_to "表示名",次のアクションへ遷移するパス(パラメーター),class: "クラス名" %>
となるよう記述します。各教材の一覧ページへのパスtext_pathにパラメーターとしてgenreを指定することで、表示する教材を制限します。
controllerに条件分岐を記述
texts_controller.rbdef index if params[:genre] == nil @texts = Text.where(genre: ["Basic", "Git", "Ruby", "Ruby on Rails"]) else @texts = Text.where(genre: params[:genre]) end endパラメーターgenreの指定がない場合とある場合で条件分岐し、条件にあった教材だけ取得するようにします。
helperに変換部分を作り教材毎にページタイトルが変わるようにする
texts_helper.rbmodule TextsHelper def page_title if params[:genre] == nil "Ruby/Rails" else params[:genre] end end endhelperにpage_titleというメソッドを定義し、パラメーターgenreの指定がない場合はRuby/Railsを表示し、指定がある場合はその教材のgenre名を表示するよう処理を書きます。
helperは、定義しておくことでview内でその処理を呼び出すことができ、viewをよりシンプルに書くことができます。
一覧ページのview
index.html.erb<div class="text-title"> <h3><%= page_title %>テキスト教材</h3> </div> <% @texts.each do |text| %> <div class="card-body"> <p class="card-text"><%= text.title %></p> <p class="card-text"><%= text.genre %></p> </div> <% end %>viewで <%= page_title(helperで定義したメソッド) %> を記述し、ページ毎にタイトルを表示するようにします。
controllerで定義した@textsはeachの繰り返し処理でtitleとgenreを表示するようにします。
まとめ
このようにcontrollerとhelperでパラメーターを元に条件分岐することで、一覧ページごとに適したタイトルとテキストを表示することができました。
- 投稿日:2021-02-26T20:16:54+09:00
MacbookにRuby導入
環境
MacOS:Catalina
MacbookPro
初期の状態からRuby
、Rails
、MySQL
をインストールします。手順は以下です。
・Zshをデフォルトに
・Command line tools導入
・Homebrew導入
・Rubyインストール
・MySQLを導入
・Railsを導入
・Node.jsの導入
・yarnの導入Zshをデフォルトに
# zshをデフォルトに設定 % chsh -s /bin/zsh # ログインシェルを表示 % echo $SHELL # 以下のように表示されれば成功 /bin/zshパスワード求められたらPCのパスワードを入力します。
Command line tools導入
以下をターミナルで
% xcode-select --installインストールをクリックして進める。
Homebrew導入
Homebrewというソフトウェア管理ツールを導入します。
以下をターミナルで。% cd # ホームディレクトリに移動 % pwd # ホームディレクトリにいるかどうか確認 % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" # コマンドを実行PCパスワードを求められます。
途中エンターキー入力あります。以下でインストールできたか確認。
% brew -v最新の状態にします。
% brew updateHomebrewの権限変更
% sudo chown -R `whoami`:admin /usr/local/binRubyインストール
Rubyの土台となるrbenvとruby-buildを、Homebrewを用いてインストール
% brew install rbenv ruby-buildrbenvをどこからも使用できるようにする。
% echo 'eval "$(rbenv init -)"' >> ~/.zshrczshrcの変更を反映
% source ~/.zshrcターミナルのirb上で日本語入力を可能にする設定を行うために、以下のコマンドでインストール
% brew install readlineすでにインストール済と出る場合あります。
readlineをどこからも使用できるようにします。
% brew link readline --forcerbenvを利用してRubyをインストール。
% RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)" % rbenv install 2.6.5時間がかかるコマンドです。
rbenvを読み込んで変更を反映。
% rbenv rehashRubyのバーションを確認。
% ruby -vMySQLを導入
% brew install mysql@5.6MySQLの自動起動設定。
MySQLは本来であればPC再起動のたびに起動し直す必要がありますが、それは面倒であるため、自動で起動するように。% mkdir ~/Library/LaunchAgents % ln -sfv /usr/local/opt/mysql\@5.6/*.plist ~/Library/LaunchAgents % launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql\@5.6.plistmysqlコマンドをどこからでも実行できるように。
% echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc # mysqlのコマンドを実行できるようにする設定 % source ~/.zshrc # 設定を読み込むコマンド % which mysql # mysqlのコマンドが打てるか確認する # 以下のように表示されれば成功 /usr/local/opt/mysql@5.6/bin/mysqlMySQLの起動を確認。
% mysql.server status # MySQLの状態を確認するコマンド # 以下のように表示されれば成功 SUCCESS! MySQL runningRailsを導入
Rubyの拡張機能(gem)を管理するためのbundler(バンドラー)をインストールします。
% gem install bundler --version='2.1.4'Railsをインストール。
% gem install rails --version='6.0.0'処理に時間かかります。
rbenvを再読み込み。
% rbenv rehashRailsが導入できたか確認。
% rails -v Rails 6.0.0 # 「Rails」のあとに続く数字は変わる可能性がありますNode.jsの導入
Railsを動かすためにはNode.jsが必要となり、それをHomebrewを用いてインストールします。
% brew install node@14Warningでても問題ないです。
Node.jsへのパスを設定。
% echo 'export PATH="/usr/local/opt/node@14/bin:$PATH"' >> ~/.zshrc % source ~/.zshrcNode.jsが導入できたか確認。
% node -v v14.15.3 # 数値は異なる場合がありますyarnの導入
yarnのインストール。
% brew install yarnyarnが導入できたか確認。
% yarn -v以上で、Railsでアプリ作成ができる環境が整いました。
- 投稿日:2021-02-26T18:57:10+09:00
*.js.erbファイルは読み込めているのに表示されないときの対処方法
- 「Completed 200 OK」となっているのに*.js.erbに書いたjavascriptのコードが動かないとき。
- javascriptのエラーも発生していないとき。
原因
https://github.com/rails/rails/issues/33115
どうやらrails-ujsが求めているContent-Typeが一致しないことが原因っぽいです。対策
【対策1】レイアウトファイル名を変更する
views/layouts/*.erb
↓
views/layouts/*.html.erb
【対策2】render時に
layout: false
を指定する*_controller.rbdef create @sending_user = User.find(current_user.id) @receiving_user = User.find(params[:user_id]) new_like_balance = @sending_user.like_balance - 1 @sending_user.update(like_balance: new_like_balance) @like = Like.create(sender_id: @sending_user.id, receiver_id: @receiving_user.id) # ここ render formats: :js, layout: false endどちらの方法でも解決できました。
- 投稿日:2021-02-26T15:51:55+09:00
初めてのRuby On Rails
Ruby On Railsとは
オープンソースのWebアプリケーションフレームワークである。RoRまたは単にRailsと呼ばれる。その名にも示されているようにRubyで書かれている。またModel View Controller(MVC)アーキテクチャに基づいて構築されている。
Ruby_on_Rails - Wikipedia初めてRubyに触れるので、Progateを使って学習してみました。忘れそうなことや頭を整理するために、記載しています。
Ruby on Rails5 - ProgateRailsアプリケーションの作成
以下コマンドにより必要なファルダ等が作成される
ターミナルrails new app_name
ツリー構造
app_name/
├ app/
├ config/
├ db/
└ その他サーバーの起動
ターミナルrails server
アプリ表示
サーバーを起動後に、ブラウザで
localhost:3000
にアクセストップページの作成(新しくページを作るコマンド)
以下のコマンドにより自動で
top
というページが作成されるターミナルrails generate controller home top
home: コントローラー名
top: アクション名※すでにhomeコントローラーがある場合には別アクションでは使用不可
トップ画面
localhost:3000/home/top
にアクセスできるページを表示するのに必要な3ファイル
- ビュー(view)
- コントローラー(controller)
- ルーティング(routing)
ビュー(view)
ビューとは、ページの「見た目」を作るためのHTMLファイルである
ブラウザとRailsのやりとりの中で、Railsからビューが返され、ページが表示されるツリー構造
viewsフォルダの中に
homeフォルダ
とtop.html.erb
というファイルが作成されるapp/ アプリケーションのメインフォルダ
└ view/ ビューフォルダ
│ ┌──────────┐
└ │ home/ │
│ └ top.html.erb │
└──────────┘コントローラー(controller)
ページを表示するとき、Railsの中ではコントローラを経由してビューをブラウザに返している
ツリー構造
controllersフォルダの中に
home_controller.rb
というファイルが作成されるapp/ アプリケーションのメインフォルダ
└ controllers/ コントローラーフォルダ
│ ┌──────────┐
└ │ home_controller.rb │
└──────────┘ターミナルrails generate controller home top
上記実行時に
home_controller.rb
というコントローラのファイルが作成される
ファイルの中にtopメソッド
が追加される
コントローラ内のメソッドをアクション
と呼ぶhome_controller.rbclass HomeController < ApplicationController def top end endアクションは、コントローラと同じ名前のビューフォルダから、アクションと同じ名前のHTMLファイルを探してブラウザに返す
ルーティング(routing)
ルーティングはブラウザとコントローラを繋ぐ役割を担う
送信されたURLに対して「どのコントローラの、
どのアクション」で処理するかを決める「対応表」のことページが表示されるまでに、
ルーティング→コントローラ→ビュー
という順で処理トップ画面
localhost:3000/home/top
にアクセスした時を例に考えてみよう
- URL(home/top)に対応するHTMLファイルをリクエスト
ルーティング(対応表)
URL コントローラ アクション home/top home top ︙ ︙ ︙ 2.homeコントローラーのtopアクションを呼び出す
3.URLに対応したHTMLファイルを送信ツリー構造
configフォルダの中に
routes.rb
というファイルが作成されるconfig/ 設定情報に関するフォルダ
│ ┌────────┐
└ │ routes.rb │
└────────┘routes.rbRails.application.routes.draw do get "home/top" => "home#top" endまとめ
初めてRubyOnRailsを学習しました。
学習して、改めて整理すると意外とわかりやすいと思いました。これからも追加で記事を作成していきます。参考サイト
ディレクトリ構成図を書くときに便利な記号
https://qiita.com/paty-fakename/items/c82ed27b4070feeceff6
Markdown記法 サンプル集
https://qiita.com/tbpgr/items/989c6badefff69377da7
Qiitaのテーブルの書き方についてまとめた
https://qiita.com/zakuroishikuro/items/f33929eab9d55c5bd073
- 投稿日:2021-02-26T15:51:55+09:00
初めてのRuby On Rails その1
Ruby On Railsとは
オープンソースのWebアプリケーションフレームワークである。RoRまたは単にRailsと呼ばれる。その名にも示されているようにRubyで書かれている。またModel View Controller(MVC)アーキテクチャに基づいて構築されている。
Ruby_on_Rails - Wikipedia初めてRubyに触れるので、Progateを使って学習してみました。忘れそうなことや頭を整理するために、記載しています。
Ruby on Rails5 - ProgateRailsアプリケーションの作成
以下コマンドにより必要なファルダ等が作成される
ターミナルrails new app_name
ツリー構造
app_name/
├ app/
├ config/
├ db/
└ その他サーバーの起動
ターミナルrails server
アプリ表示
サーバーを起動後に、ブラウザで
localhost:3000
にアクセストップページの作成(新しくページを作るコマンド)
以下のコマンドにより自動で
top
というページが作成されるターミナルrails generate controller home top
以下のように
generate
をg
に省略可能ターミナルrails g controller home top
home: コントローラー名
top: アクション名※すでにhomeコントローラーがある場合には別アクションでは使用不可
トップ画面
localhost:3000/home/top
にアクセスできるページを表示するのに必要な3ファイル
- ビュー(view)
- コントローラー(controller)
- ルーティング(routing)
ビュー(view)
ビューとは、ページの「見た目」を作るためのHTMLファイルである
ブラウザとRailsのやりとりの中で、Railsからビューが返され、ページが表示されるツリー構造
viewsフォルダの中に
homeフォルダ
とtop.html.erb
というファイルが作成されるapp/ アプリケーションのメインフォルダ
└ view/ ビューフォルダ
│ ┌──────────┐
└ │ home/ │
│ └ top.html.erb │
└──────────┘コントローラー(controller)
ページを表示するとき、Railsの中ではコントローラを経由してビューをブラウザに返している
ツリー構造
controllersフォルダの中に
home_controller.rb
というファイルが作成されるapp/ アプリケーションのメインフォルダ
└ controllers/ コントローラーフォルダ
│ ┌──────────┐
└ │ home_controller.rb │
└──────────┘ターミナルrails generate controller home top
上記実行時に
home_controller.rb
というコントローラのファイルが作成される
ファイルの中にtopメソッド
が追加される
コントローラ内のメソッドをアクション
と呼ぶhome_controller.rbclass HomeController < ApplicationController def top end endアクションは、コントローラと同じ名前のビューフォルダから、アクションと同じ名前のHTMLファイルを探してブラウザに返す
ルーティング(routing)
ルーティングはブラウザとコントローラを繋ぐ役割を担う
送信されたURLに対して「どのコントローラの、
どのアクション」で処理するかを決める「対応表」のことページが表示されるまでに、
ルーティング→コントローラ→ビュー
という順で処理トップ画面
localhost:3000/home/top
にアクセスした時を例に考えてみよう
- URL(home/top)に対応するHTMLファイルをリクエスト
ルーティング(対応表)
URL コントローラ アクション home/top home top ︙ ︙ ︙ 2.homeコントローラーのtopアクションを呼び出す
3.URLに対応したHTMLファイルを送信ツリー構造
configフォルダの中に
routes.rb
というファイルが作成されるconfig/ 設定情報に関するフォルダ
│ ┌────────┐
└ │ routes.rb │
└────────┘routes.rbRails.application.routes.draw do get "home/top" => "home#top" endまとめ
初めてRubyOnRailsを学習しました。
学習して、改めて整理すると意外とわかりやすいと思いました。これからも追加で記事を作成していきます。参考サイト
ディレクトリ構成図を書くときに便利な記号
https://qiita.com/paty-fakename/items/c82ed27b4070feeceff6
Markdown記法 サンプル集
https://qiita.com/tbpgr/items/989c6badefff69377da7
Qiitaのテーブルの書き方についてまとめた
https://qiita.com/zakuroishikuro/items/f33929eab9d55c5bd073
- 投稿日:2021-02-26T14:25:54+09:00
RailsのSQLインジェクション対策まとめ
find、find_by
普通に使っている分にはSQLインジェクションは発生しないのであまり気にする必要なし。
(参考)
ActiveRecord::Base#find is SQL injection free? - Rails - Ruby-Forumシンプルなwhere句
# NG User.where("name='#{params[:name]}'") # OK User.where(name: params[:name]) User.where("name = ?", params[:name])複数条件で検索
# NG User.where("name = #{params[:name]} or age < #{params[:age]}") # OK User.where("name = ? or age < ?", params[:name], params[:age])Likeを使ったあいまい検索
# NG User.where('name LIKE ?', "%#{params[:name]}%") # OK User.where('name LIKE ?', "%#{sanitize_sql_like(params[:name])}%")参考
- 投稿日:2021-02-26T14:15:48+09:00
【rails】モデルに定義するメソッドのselfについて
モデルにメソッドを書く際に、”self.メソッド名”と書くことがあるかと思います。
まだ完全には理解できていませんが、わかった内容を記事にまとめます。モデル
cook.rbclass Cook < ApplicationRecord def self.ranks Cook.find(Like.group(:cook_id).order(Arel.sql('count(cook_id) desc')).limit(9).pluck(:cook_id)) end endとある記事を参考にして、いいねのランキング機能を実装しました。
ですが、このコード省略は以下のように省略できます。cook.rbclass Cook < ApplicationRecord def self.ranks find(Like.group(:cook_id).order(Arel.sql('count(cook_id) desc')).limit(9).pluck(:cook_id)) end end何が変わったのかというと、3行目の'Cook'を外しました。
なぜ省略できるのか
今回の記事のポイントはここです。まず、2行目のselfについて。
selfとは"自分自身"という意味なので、今回の場合、selfはCookを指します。(ここでのCookは1行目class CookのCookです)
説明文書的に表現すると、「selfの実行主体はCookである」ということです。
そして3行目のCook.find~の「findの実行主体もCook」です。
つまり、"self.メソッド名"と"self.メソッド名の中で実行しているメソッド"の実行主体が同じであれば、省略可能
となります。
最後に
そもそもほとんど記事のコピペでランキング機能を実装してしまったのがダメでしたね。コピペでコードを使うのは良くないことを実感しました笑
また、selfが表しているものについては何となく理解できてきた気はしますが、どんなときにselfを使うのかはボンヤリしています。。。以上です!ありがとうございました!
- 投稿日:2021-02-26T14:12:59+09:00
Railsアプリの標準言語を日本語に設定する
環境
macOS Big Sur Ver11.2.1
Rails6.0.0
Ruby2.6.5目的
Railsで開発中のアプリケーションの標準言語を日本語に変更すること。
これにより、エラーメッセージが日本語表示されること。
※今回のアプリではdeviseを導入しているので、deviseによるデフォルトのメッセージも日本語化します!手順
①アプリケーション自体の言語設定を日本語にする
以下の一文を追加
config/application.rbrequire_relative 'boot' require 'rails/all' Bundler.require(*Rails.groups) module アプリ名 class Application < Rails::Application config.load_defaults 6.0 # 日本語の言語設定を追記する config.i18n.default_locale = :ja end end②必要なGemのインストール
Gemfileに以下のgemを追記して、ターミナルでbundle installする。
すべての環境に適用されるよう、最下部とかに記述する。
参考:rails-i18nGemfile# アプリケーションの日本語対応用gem gem 'rails-i18n'ここまででアプリの設定は完了!
③deviseの言語設定
上記までの作業である程度の日本語化は完了しますが、deviseを導入してユーザー管理機能を構築しているので、deviseに関係するエラーメッセージはまだ英語表記のはず。
そこで、以下のファイルを作成し、config/locales 内に配置する。
ファイルの中身は、コピペでOK
devise-i18n
これでdeviseに関連する部分はすべて日本語化されます。④独自に追加したカラムなどの日本語化
deviseを導入していてもusersテーブルに独自にカラムを追加している場合は、これまでの作業を実施しても独自カラムの表記は英語のままです。
おそらくほとんどの場合は上記までの作業では不十分なはず。
ここからは、自分で設定用ファイルを作って、設定も自分で記述していく必要がある。ということで、config/localesに、「ja.yml」というファイルを作成し、その中に自分で設定を記述していく!
例えばこんな感じ。ja: activerecord: attributes: user: nickname: ニックネーム birthday: 生年月日 item: name: 商品名 explain: 商品の説明 # フォームオブジェクトを用いている場合 activemodel: attributes: order_shipping: postal_code: 郵便番号 prefecture_id: 都道府県 token: カード情報attributesは属性、userはモデルのファイル名(フォームオブジェクトならクラスのファイル名)、nicknameはモデルやクラスで定義している属性名といった感じでしょうか。
モデルやクラスの継承先に気をつけて記述をしてください!
記述したらサーバーを再起動して、エラーメッセージが全て日本語化できているか確認してください。以上、Railsアプリケーションの日本語化の手順でした。
- 投稿日:2021-02-26T14:03:22+09:00
[memo]Heroku利用方法
Herokuを使ってアプリケーションをデプロイする方法をまとめます。
[手順]
●はじめてデプロイをする場合
・Herokuにアカウント登録する
・Heroku CLIをインストールする
・masterブランチへcommitする
・Heroku上にアプリケーションを作成する
・MySQLを使用できるように設定する
・master.keyを環境変数として設定する
・Herokuへアプリケーションの情報をpushする
・Heroku上でマイグレーションを実行する●デプロイ済みのアプリケーションに変更修正を加えた場合
・変更修正をcommitする
・ブランチを作成していた場合は、masterブランチへマージする
・Heroku上にpushする
(テーブルに変更を加えた場合は)Heroku上でマイグレーションを実行するHerokuの導入
Heroku CLIをインストール
# ターミナル % brew tap heroku/brew && brew install heroku # 完了確認コマンド % heroku --version # バージョンが出力されれば成功 heroku/7.40.0 darwin-x64 node-v12.16.2インストールについての公式ドキュメント
https://devcenter.heroku.com/articles/heroku-cliHerokuにログイン
% heroku login --interactive => Enter your Heroku credentials. # メールアドレスを入力し、エンターキーを押す => Email: # パスワードを入力して、エンターキーを押す => Password:Heroku上にアプリケーションを作成
アプリ作成公式ドキュメント
https://devcenter.heroku.com/articles/getting-started-with-rails6# 作成したいアプリのディレクトリへ移動し以下コマンド実行 % heroku create アプリ名アプリ名はアンダーバー
_
ではなくハイフン-
を使う
例:meisai-app 等Heroku上でMySQLを使えるようにする
# ClearDBアドオンを追加 % heroku addons:add cleardb # 下記のように出力されれば成功 Creating cleardb on ⬢ meisai-app... free Created cleardb-vertical-00000 as CLEARDB_DATABASE_URL Use heroku addons:docs cleardb to view documentation # 設定を変更 # ClearDBデータベースのURLを変数heroku_cleardbに格納 % heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL` # データベースのURLを再設定 % heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5} →mysql2というGemを使用しているので、DATABASE_URLの冒頭がmysql2://に変更されているHeroku上で非公開の値を管理する
credentials.yml.encファイル
Railsにて、外部に漏らしたくない情報を扱う際に用いるファイル。
通常時は、英数字の文字列で構成された暗号文が表示され、ファイル内に何が書かれているのか分からないようになっています。このcredentials.yml.encと対になるmaster.keyが存在する場合、credentials.yml.encの暗号文を復号し、ファイル内の記述を確認できます。master.keyファイル
credentials.yml.encの暗号文を復号する、鍵の役割を持ったファイルです。特定のcredentials.yml.encと対になっているため、その他のcredentials.yml.encへは、効果を発揮しません。
master.keyはデフォルトで.gitignoreに記述されており、Gitで管理されない仕組みになっています。credentials.yml.encの中身を確認
アプリのディレクトリを開き、config/credentials.yml.encを開くと暗号文が確認できる
credentials.yml.encをmaster.keyによって復号
# ターミナル % EDITOR="vi" bin/rails credentials:edit # 確認後は、「escキー」→「:」→「q」と入力し、「enterキー」を押して credentials.yml.encを閉じるHeroku上にmaster.keyを設置
Herokuへアプリケーションのコードをデプロイします。
しかし、その際にデプロイされるコードというのは、Gitで管理されているコードになります。
つまり、master.keyはこのままだとHeroku上へデプロイできず、credentials.yml.encもHeroku上で扱えないということになります。そこで、Heroku上に別途master.keyを設置し、Heroku上でもcredentials.yml.encを扱えるようにする必要があります。
環境変数
という「どのディレクトリ・ファイルからでも参照できる変数」を使いmaster.keyの値を設置します。
heroku configコマンドの使用
Heroku上で環境変数の参照・追加・削除等をする場合に用います。環境変数の追加であればheroku config:set 環境変数名="値"と実行します。そうすることによって、Heroku上で環境変数を追加できます。Heroku上で環境変数を設定
# Heroku上に環境変数を設定 % heroku config:set RAILS_MASTER_KEY=`cat config/master.key` # Heroku上で環境変数を確認 % heroku config # RAILS_MASTER_KEYという変数名で値が設定されていれば成功アプリケーションをプッシュ
# Rubyのバージョン2.6.5が動作するStack(動作環境)を指定 % heroku stack:set heroku-18 -a アプリ名 # アプリケーションをHerokuへ追加 % git push heroku masterエラーが出る場合
git push heroku master 実行時に「remote: ! Could not detect rake tasks」「remote: ! ensure you can run $ bundle exec rake -P against your app」とエラーが表示される場合は、bundlerのバージョンがエラーの原因である可能性が高いので次の手順を試す。
# 現在入っているbundlerを削除[何度か確認を求められますが、「y」を入力してエンター] % gem uninstall bundler # bundlerのバージョン2.1.4を指定してインストール % gem install bundler -v '2.1.4' # ディレクトリ内の Gemfile.lock を削除 # Gemfile.lockを作り直す % bundle install # 変更をGitHubへ反映 GitHubDesktopより、commit と push # Herokuにアプリケーションの情報を追加 % git push heroku masterHerokuデータベースにマイグレーションの情報を反映
# Heroku上でマイグレーションを実行 % heroku run rails db:migrate公開を確認
# Herokuにデプロイされたアプリケーションの情報を確認 % heroku apps:info ===meisai-app Addons: cleardb:ignite Auto Cert Mgmt: false Dynos: web: 1 Git URL: https://git.heroku.com/meisai-app.git Owner: sample@sample.com Region: us Repo Size: 165 KB Slug Size: 56 MB Stack: heroku-18 Web URL: https:/meisai-app.herokuapp.com/エラーが出る場合のログの確認
# ログの最後の10行を表示するためのtailオプションを使いログ表示 % heroku logs --tail --app アプリ名 # 出力結果 2020-05-08T09:03:30.572301+00:00 app[web.1]: F, [2020-05-08T09:03:30.572206 #4] FATAL -- : [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 2020-05-08T09:03:30.572302+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] ActionView::Template::Error (undefined method `checked' for #<Post:0x000055f0ec57ec88>): 2020-05-08T09:03:30.572303+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 7: <div id="list"> 2020-05-08T09:03:30.572303+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 8: </div> 2020-05-08T09:03:30.572304+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 9: <% @posts.each do |post| %> 2020-05-08T09:03:30.572305+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 10: <div class="post" data-id = <%= post.id %> data-check=<%= post.checked %>> 2020-05-08T09:03:30.572305+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 11: <div class="post-date"> 2020-05-08T09:03:30.572306+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 12: 投稿日時:<%= post.created_at %> 2020-05-08T09:03:30.572306+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 13: </div> 2020-05-08T09:03:30.572306+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] 2020-05-08T09:03:30.572307+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] app/views/posts/index.html.erb:10 2020-05-08T09:03:30.572307+00:00 app[web.1]: [52bf1bef-ea70-4df0-897a-6d0c3d925b1e] app/views/posts/index.html.erb:9 2020-05-08T09:03:30.574057+00:00 heroku[router]: at=info method=GET path="/" host=ajax-app-123456.herokuapp.com request_id=52bf1bef-ea70-4df0-897a-6d0c3d925b1e fwd="125.12.120.68" dyno=web.1 connect=1ms service=79ms status=500 bytes=1827 protocol=https 2020-05-08T09:03:31.014022+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=ajax-app-123456.herokuapp.com request_id=56eb5531-4288-48d7-9614-b58fed6deb86 fwd="125.12.120.68" dyno=web.1 connect=2ms service=2ms status=304 bytes=48 protocol=httpsデプロイ済みのアプリケーションに変更を加えた場合
ファイルの変更履歴が存在する場合
# Githubのmasterブランチへcommitする(Githubデスクトップからでもok) % git add . % git commit -m "後から見てわかりやすいコミット名" # 作成したコミットをHerokuへプッシュ % git push heroku masterファイルの変更履歴が存在しない場合
Herokuの仕様上、最新のコミット履歴が存在しない状態でgit push heroku masterコマンドを実行すると「Everything up-to-date(すでに最新の状態に更新されています)」と表示されます。その場合は空のコミットを作成してHerokuにプッシュする方法を使います。
# 空のコミットを生成 % git commit --allow-empty -m "空のcommit" # 作成したコミットをHerokuへプッシュ % git push heroku master
- 投稿日:2021-02-26T12:53:42+09:00
学び直し Rubyがミニツク Part9
今日の教科書
モジュールモジュール
モジュールは手続きの部分だけのまとめ。
moduleでモジュールを定義。module <モジュール名> <モジュールの定義> endmodule Foo def foo puts("module foo") end endクラスとモジュールの違いは
- モジュールはインスタンスを作ることができない
- モジュールは継承ができないクラスは継承をおこなうことでスーパークラスから機能を渡せました。
しかし、クラスの継承ではスーパークラスがひとつしか指定できません。
そのため、複数のスーパークラスを継承して新しいサブクラスを作れません。そのようなクラスに異なる機能を渡したい時にRubyではモジュールを使います。このさまざまな機能を混ぜ合わせるやり方のことをMix-inと呼びます。include
モジュールに含まれているメソッドや定数をクラスの中に取り込む。
module Greeting def hello puts("Hello, Ruby!") end end class Foo include Greeting end class Bar include Greeting end Foo.hello #=> Hello, Ruby! Bar.hello #=> Hello, Ruby!同じクラスへ複数のモジュールをインクルード
module Foo def foo puts("foo") end end module Bar def bar puts("bar") end end class Baz include Foo include Bar end baz = Baz.new baz.foo #=> foo baz.bar #=> bar別のモジュールに機能を渡す
module Foo
def foo
puts("foo")
end
end
module Bar
include Foo
end
class Baz
include Bar
end
baz = Baz.new
baz.foo #=> foo
モジュール関数
module Foo def foo puts("foo") end module_function :foo end Foo.foo #=> fooモジュールで定義したインスタンスメソッドはレシーバを指定した定式では呼べない。
レシーバにモジュールを指定してメソッド呼び出しを行う。モジュール内でmodule_functionの引数にメソッド名をシンボルで指定することで設定できる。このようなメソッドをモジュール関数と呼ぶ。名前空間の提供
別の人が開発していたライブラリを使ったり、複数のメンバーで開発をおこなっていると使いたい名前が被ってしまうことがあります。
ただし、同じ名前のクラスやメソッドを定義すると、前に定義していたものを上書きしてしまいます。上書きする前に使っていたものと違う機能になってしまうと、上書きする前の機能を使っていたところでエラーが発生するかもしれません。このような名前が衝突することによって生まれる問題を避け、自由に名前を付けることができることを名前空間と呼びます。Rubyではモジュールを使うことによって、名前空間を提供することができます。module Foo def foo puts("module foo") end module_function :foo end module Bar def foo puts("module bar") end module_function :foo end Foo.foo #=> module foo Bar.foo #=> module bar特異メソッド
特定の一つのオブジェクトだけで使えるメソッド。
メソッド定義時にオブジェクト.メソッド名
で使える。obj = Object.new def obj.foo puts("foo") endクラスやモジュールも特異メソッドを定義できる。クラスメソッドも特異メソッドの一種。
クラスメソッドはクラスをレシーバにして呼び出す。# def self.メソッド名; end module Foo def self.foo puts("foo") end end Foo.foo #=> foo # def モジュール.メソッド名; end module Bar def Bar.bar puts("bar") end end Bar.bar #=> barモジュールには似たようなモジュール関数がある。両方ともレシーバにモジュールを指定する。
モジュール関数ではインクルードした際のインスタンスメソッドとして。
特異メソッドではインクルードしたときに渡さない。module Foo def self.foo puts("foo") end end module Bar include Foo end Foo.foo #=> foo Bar.foo NoMethodError: undefined method `foo' for Bar:Module from (irb):9 from :0モジュール関数はインクルードすることによって、インスタンスメソッドとしてインクルード先で使うことができる。
module Foo def foo puts("foo") end module_function :foo end class Bar include Foo def bar foo end end bar = Bar.new bar.foo #=> fooextend
オブジェクトに対して引数に指定したモジュールのインスタンスメソッドを特異メソッドとして渡す。
module Foo def foo puts("foo") end end class Bar end str = "" str.extend(Foo) str.foo #=> foo Bar.extend(Foo) Bar.foo #=> foo定義しているクラスやモジュールの中でも呼び出せる。
module Foo def foo puts("foo") end end module Bar extend Foo end Bar.foo #=> foo組み込みモジュール
Rubyに最初から組み込まれているモジュール。
Comparableモジュール
比較演算子をクラスに加えるモジュールです。ArrayやStringなどの大小関係があるオブジェクトはこのモジュールをインクルードしている。使用時には<=>演算子を定義する。
この演算子を使って比較するメソッドの集まりがComparableモジュール。class Foo include Comparable attr_accessor :num def initialize(num) @num = num end def <=>(other) return @num <=> other.num end end foo = Foo.new(10) bar = Foo.new(5) p foo < bar p foo > barEnumerableモジュール
繰り返しを行うクラスのためのモジュール。eachメソッドを必要とする。
これで様々なイテレータを定義できる。class MetaSyntax include Enumerable def initialize @variables = [] end def add(value) @variables << value end def each @variables.each do |variable| yield variable end end end ary = MetaSyntax.new ary.add("foo") ary.add("bar") ary.add("baz") ary.each do |i| puts i end ary.each_with_index do |item, index| puts("これは#{index}番目の#{item}です") endクラス内にeachを定義すると、Enumerableモジュールをインクルードする。
そしてeach_with_indexメソッドといったメソッドが使えるようになる。
- 投稿日:2021-02-26T11:32:05+09:00
TD Toolbelt (Mac)のインストール
業務でTDを使うことになったので、コマンドラインをインストールしようとして、若干ハマったのでメモ
ホントはBrewでサクッとインストールできればいいと思って、調べてみたけど見つからず・・・・
まずはRubyのバージョンの確認
% ruby --version ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]※これが原因でインストールに失敗する
$ sudo -s % gem install td※RootにならないとPermissionエラーになってインストールがポシャった
"xcrun clang -o conftest -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin19 -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.Internal.sdk/usr/local/lib -arch x86_64 -lruby.2.6 " In file included from conftest.c:1: In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby.h:33: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: fatal error: 'ruby/config.h' file not found #include "ruby/config.h" ^~~~~~~~~~~~~~~ /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: note: did not find header 'config.h' in framework 'ruby' (loaded from '/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks') 1 error generated. checked program was: /* begin */ 1: #include "ruby.h" 2: 3: int main(int argc, char **argv 4: { 5: return 0; 6: } /* end */こんなエラーが出た
なんとなく、Rubyのバージョンが怪しいと思って、バージョンを上げてみる
参考にしたのは、https://qiita.com/Ficus/items/bdef5c2b504d7a4008fb
めちゃ参考になりました# ruby --version ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19] % gem install td Parsing documentation for td-0.16.9 Installing ri documentation for td-0.16.9 Done installing documentation for msgpack, yajl-ruby, hirb, parallel, httpclient, td-client, fluent-logger, td-logger, rubyzip, zip-zip, ruby-progressbar, td after 3 seconds 12 gems installedインストールできました
- 投稿日:2021-02-26T07:39:45+09:00
【Docker】エラー Could not find gem 'mysql2 (~> 0.5)' in any of the gem sources listed in your Gemfile
はじめに
Dockerの環境構築中に発生したエラーの解決した方法を記録します。
ただ、エラーは発生までの経緯で解決方法が違ってくるので、参考程度にしてください。【エラー文】
Could not find gem 'mysql2 (~> 0.5)' in any of the gem sources listed in your Gemfile環境
Docker version 20.10.0
docker-compose version 1.27.4Docker内の環境
ruby:2.6.5
Rails:6.0.0
データベース:mysqlDockerfile
docker-compose.ymlFROM ruby:2.6.5 RUN apt-get update && apt-get install -y \ build-essential \ libpq-dev \ nodejs\ vim WORKDIR /[作成したディレクトリ名] COPY Gemfile Gemfile.lock /[作成したディレクトリ名]/ RUN bundle installdocker-compose.yml
docker-compose.ymlversion: '3' services: web: build: . ports: - 3000:3000 volumes: - '.:/[作成したディレクトリ名]' tty: true stdin_open: true結論
結論は以下の2点を事項することで解決に至りました。
・bundle installしてmysqlをインストール
・webpackerをインストール経緯と対応
dockerc-composeでコンテナを作成後Railsのセットアップを行いサーバーを起動した時に発生しました。
エラー内容はmysql2が見つからない
という内容でした。対応1
Dockerfileにmysqlの記述がないから当然??と思いましたが、Gemfileにはmysqlが記述されてるのでとりあえずコンテナ内で
bundle install
をしてインストールしてみることにしてみました。かなり時間が経ってgemfileにインストールされました。
対応2
再び
rails s -b 0.0.0.0
をして起動しようと試みましたが今度はPlease run rails webpacker:install Error docker
というエラーが出ました。
これも、Dockerfileに書いてないので当然??と思いながら調べてるとwebpacker
を使う為にはyarn
が必要で、yarnをインストールする為には下記の記述も必要との事でDockerfileを編集してイメージ作成からやり直しました。
最後コンテナ内でwebpackerをインストールするとうまくいきました。対応2の手順
Dockerfileにyarnを追記します。
DockerfileFROM ruby:2.6.5 RUN apt-get update && apt-get install -y \ build-essential \ libpq-dev \ nodejs\ yarn \ ←ここ vim WORKDIR /exam COPY Gemfile Gemfile.lock /exam/ RUN bundle install
rails webpacker:install
のコマンドを入力してwebpackerをインストールします。ターミナルroot@c21d03f52523:/exam# rails webpacker:install . . . 省略 Webpacker successfully installed ? ? root@c21d03f52523:/exam#サーバーを起動します。
ターミナルroot@c21d03f52523:/exam# rails s -b 0.0.0.0 => Booting Puma => Rails 6.1.2.1 application starting in development => Run `bin/rails server --help` for more startup options Puma starting in single mode... * Puma version: 5.2.1 (ruby 2.6.5-p114) ("Fettisdagsbulle") * Min threads: 5 * Max threads: 5 * Environment: development * PID: 156 * Listening on http://0.0.0.0:3000 Use Ctrl-C to stopかなり時間がかかりましたが、インストール後
rails s -b 0.0.0.0
で見事サーバーが立ち上がりました!この記事では上記のエラー解決のみの内容ですがこの後データベースを作ってDocker内での開発環境を整えて行きます。
もしこの先にもご興味あれば下記の記事を参考にしてみてください。【Docker】Ruby2.6.5とRails6.0.0とmysql DockerComposeで環境構築
後日更新予定最後に
Dockerについて完全に理解できておらず、今回の対応も対処療法ですのでこれからも継続学習が必要です。
万が一情報が間違っている場合ご指摘していただけると幸いです。参考
- 投稿日:2021-02-26T05:54:12+09:00
正規表現エンジン(ロブ・パイクのバックトラック実装)をRubyで写経した
元になっているのは『プログラミング作法』に載っている、ロブ・パイクが書いたコード(以下「ロブ・パイク版」)ですが、40行以内で正規表現エンジンを構築 | POSTD の方を見て書いてみました。JavaScript の方が読み慣れているのと、リポジトリにテストコードが用意されていたためです。
def drop(text, n) text.chars.drop(n).join() end def empty?(str) str.nil? || str.empty? end def match_one(pattern_char, char) return true if empty?(pattern_char) return false if empty?(char) pattern_char == "." || pattern_char == char end # ロブ・パイク版では match def search(pattern, text) if pattern[0] == "^" match(drop(pattern, 1), text) else match(".*" + pattern, text) end end # ロブ・パイク版では matchhere def match(pattern, text) return true if empty?(pattern) return true if empty?(text) && pattern == "$" case pattern[1] # when "?" # match_question(pattern, text) when "*" match_star(pattern, text) else match_one(pattern[0], text[0]) && match(drop(pattern, 1), drop(text, 1)) end end # def match_question(pattern, text) # ( # match_one(pattern[0], text[0]) && # match(drop(pattern, 2), drop(text, 1)) # ) || # match(drop(pattern, 2), text) # end def match_star(pattern, text) ( match_one(pattern[0], text[0]) && match(pattern, drop(text, 1)) ) || match(drop(pattern, 2), text) endたったこれだけで正規表現の基本的な機能が実現できるのすごい&おもしろいですね。
ロブ・パイク版では
.
^
$
*
だけをメタ文字としてサポートする最低限の実装をまず示し、演習問題で?
などを追加する流れになっています(なので、上に貼ったコードでは?
の部分をコメントアウトしてみました)。
書籍『ビューティフルコード』では「1章 正規表現マッチャ」をブライアン・カーニハンが書いており、ロブ・パイク版について解説しています。
このコードが生まれた経緯について書かれていて、ここもおもしろい。
1998年、ロブ・パイク(Rob Pike)と私は、『プログラミング作法』(原題『The Practice of Programming』、Addison-Wesley刊)という本を執筆していました。(略)
問題は、既存の正規表現パッケージはどれも大き過ぎたということでした。(略)それでは教育用に適しているとは到底言えません。
そこで私はロブに、正規表現の基本的な考え方が読み取れる最小限の、ただしそれでいて有用でつまらなくないパターンが書けるようなパッケージを探そうと提案しました。コードが本の1ページに納まるようなら理想的だと思いました。
ロブは自分の部屋に入って行きました。今思い返してみると、1〜2時間も経たないうちだったと思います。彼は30行のCのコードを携えて部屋から出て来ました。そのコードが、『プログラミング作法』の第9章に掲載されているものです。(略)参考
- O'Reilly Japan - ビューティフルコード
- 英語ですが A Regular Expression Matcher(www.cs.princeton.edu) で「1章 正規表現マッチャ」と同じ内容が読めるようです。
- プログラミング作法
- 正規表現技術入門 ――最新エンジン実装と理論的背景:書籍案内|技術評論社
- 「5.1 基本的なVM型エンジンの実装」にロブ・パイク版についての説明があります。
- 投稿日:2021-02-26T01:51:54+09:00
Ruby 3.x, Rails 6.x, MySQL 8.x の Docker 環境構築。
概要
Docker と docker-compose を使い、アプリケーションサーバを Ruby 3.x, Ruby on Rails 6.x、DB サーバを MySQL 8.x でコンテナの構築するまでの手順となります。
なお、この記事ではセキュリティについての考慮は一切していません。
Windows 10 の WSL2 - Ubuntu 18.04 と Mac で確認していますが、後述する理由により Mac の方がお勧めです。
PostgreSQL の方が好みの方は、以下のページをご確認ください。
cf. クィックスタート: Compose と Rails前提
下記の環境が設定されていること。
Windows 10
- WSL2 Ubuntu 18.04+
- Docker
- docker-compose
- MySQL Client(必要に応じて)
Mac
- Docker
- docker-compose
- Docker Desktop for Mac
- MySQL Client(必要に応じて)
また、Docker のサービスが起動済みであること。
初期ファイル構成
以下のファイルから Rails プロジェクトを新規に作成します。
. ├── docker │ ├── app │ │ ├── Dockerfile │ │ └── entrypoint.sh │ └── db │ ├── Dockerfile │ ├── conf.d │ │ └── my.cnf │ └── initdb.d │ └── init.ddl.sql ├── scripts │ └── wait-for-it.sh ├── docker-compose.yml ├── Gemfile └── Gemfile.lock
wait-for-it.sh
は https://github.com/vishnubob/wait-for-it からいただきました。最初にchmod +x scripts/wait-for-it.sh
で実行権限を付加しておいてください。初期ファイル設定
docker-compose.yml
注意点は app 側の
build: context:
で基準になるフォルダを root として、Dockerfile のパスを指定しているところです。
これは Dockerfile 内で Gemfile をコピーする必要があるのですが、build: ./docker/app
としてしまうとフォルダを遡って Gemfile の操作ができないため、起点を root にしています。
Dockerfile が root にあれば関係ないのですが、今回は docker フォルダ下にしているため、このような対応となります。また app の
command:
で先述のwait-for-it.sh
を利用し、DB が起動するまでrails server
を立ち上げないようにしています。docker-compose.ymlversion: "3.3" services: db: container_name: "db" build: ./docker/db restart: always tty: true environment: MYSQL_DATABASE: app_development MYSQL_USER: user MYSQL_PASSWORD: password MYSQL_ROOT_PASSWORD: password TZ: 'Asia/Tokyo' command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci ports: - "3306:3306" volumes: - ./docker/db/conf.d:/etc/mysql/conf.d - ./docker/db/initdb.d:/docker-entrypoint-initdb.d networks: - backend app: container_name: "app" build: context: ./ dockerfile: ./docker/app/Dockerfile ports: - "3000:3000" environment: PORT: 3000 BINDING: 0.0.0.0 tty: true depends_on: - "db" command: ["./scripts/wait-for-it.sh", "db:3306", "--", "bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"] volumes: - .:/app networks: - frontend - backend networks: frontend: driver: bridge ipam: driver: default config: - subnet: 192.168.10.0/24 backend: driver: bridge ipam: driver: default config: - subnet: 192.168.20.0/24app 用設定ファイル
docker/app/Dockerfile
ここで Gemfile および Gemfile.lock をホスト(ローカル)からゲスト(コンテナ)にコピーしています。
docker/app/DockerfileFROM ruby:3.0 ENV LANG C.UTF-8 ENV TZ Asia/Tokyo RUN apt-get update -qq && \ apt-get install -y --no-install-recommends sudo curl apt-transport-https wget build-essential libpq-dev nodejs default-mysql-client 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 && \ apt-get install --no-install-recommends -y yarn RUN apt-get clean && \ rm -rf /var/lib/apt/lists/* RUN mkdir /app WORKDIR /app COPY Gemfile /Gemfile COPY Gemfile.lock /Gemfile.lock RUN bundle install COPY . /app COPY docker/app/entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000docker/app/entrypoint.sh
こちらは以下のサイトの
entrypoint.sh
からいただきました。
cf. Quickstart: Compose and Railsdocker/app/entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"db 用設定ファイル
docker/db/Dockerfile
docker/db/DockerfileFROM mysql:8.0 RUN apt-get update -qq && \ apt-get install -y --no-install-recommends locales && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ locale-gen ja_JP.UTF-8 RUN sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen && locale-gen ENV LANG ja_JP.UTF-8 ENV TZ Asia/Tokyodocker/db/conf.d/my.cnf
my.cnf は文字コード指定が中心ですが、今回はシンプルに Rails の実行のみを考えているため、
default_authentication_plugin=mysql_native_password
で認証プラグインを変更しておきます。docker/db/conf.d/my.cnf[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_bin default-storage-engine=INNODB explicit-defaults-for-timestamp=1 general-log=1 general-log-file=/var/log/mysql/mysqld.log default_authentication_plugin=mysql_native_password [mysqldump] default-character-set=utf8mb4 [mysql] default-character-set=utf8mb4 [client] default-character-set=utf8mb4docker/db/initdb.d/init.ddl.sql
データベースを development 以外に test 用も作成する場合のファイルです。
実際は test 用データベースはrake db:create
で作成されるはずですのでなくても問題ないと思われます。
コピー先の/docker-entrypoint-initdb.d
フォルダではシェルの実行も可能なので、組み込み次第ではいろいろと対応できるようです。docker/db/initdb.d/init.ddl.sqlCREATE DATABASE IF NOT EXISTS `app_test`; GRANT ALL ON app_test.* TO 'user'@'%';Gemfile
Gemfile
初期は Rails のバージョン指定のみとなります。
Gemfilesource 'https://rubygems.org' gem 'rails', '~>6'Gemfile.lock
初期状態は空ファイルとなります。
Gemfile.lock初期起動までの手順
rails new
Docker のサービスが起動しているか確認の上、docker-compose.yml があるフォルダで
database
を MySQL に指定してrails new
を実行します。$ docker-compose run app rails new . --force --database=mysql問題なく実行が完了すると、実行したフォルダに Rails アプリのファイル群が作成されます。
このとき、Mac の場合は実行したユーザーの権限でファイルが作成されますが、WSL の場合は root 権限となり、そのままではファイルの更新が行えません。cf. 【Docker】 WSL 2 を利用したコンテナー内開発で権限をどう設定するべきか
根本的な解決ではないとは思いますが、とりあえず以下のコマンドで権限を実行ユーザーに振り替えて対応することは可能です。
ただし、ここだけではなく、scaffold など rails のコマンドでファイルを作成・編集するごとに権限を書き換える必要があります。
(これが WSL よりも Mac をお勧めする理由です)$ sudo chown -R $USER:$USER .この時点で、作成された config/database.yml を編集し、MySQL へのアクセス設定を変更します。
config/database.ymldefault: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: user password: password host: dbdocker-compose build, up
build でサービスを作成し、問題がなければ up でコンテナを起動します。
この際、--no-cache
オプションを付けるのは、bundle install
実行時に gem ファイルを巧く取り込めない場合がある(らしい)ためです。$ docker-compose build --no-cache $ docker-compose up -dこの時、db および app がほぼ同時に立ち上がりますが、docker-compose.yml で記述した通り、app の rails server は MySQL サーバとの接続が確立するまで実行されないようになっています。
それぞれのコンテナのログはdocker logs
で確認できるため、以下のように確認して下さい。$ docker logs app # アプリケーションサーバのログ $ docker logs db # DB サーバのログ実行に問題がなければ、ブラウザまたは curl などで
http://localhost:3000/
にアクセスすることで、いつもの Rails の初期画面が表示されます。scaffold 作成と実行の確認
scaffold で MVC を作成して動作が可能か確認します。
$ docker-compose run app rails g scaffold user name:string email:stringWSL で操作している場合はファイル権限の変更をしてください。
$ sudo chown -R $USER:$USER .
db:migrate
でテーブルを作成します。$ docker-compose run app rails db:migrateブラウザで
http://localhost:3000/users
にアクセスすることで scaffold で作成した Rails 標準の UI が表示され、CRUD の一連の操作が可能なことが確認できます。
http://localhost:3000/users/new
実際の DB を確認したい場合は MySQL Client が入っていればコマンドで確認できます。
(設定を変えていない場合は、user アカウントのパスワードはpassword
となります)$ mysql -u user -h 127.0.0.1 -D app_development -pmysql> show create table users; +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | users | CREATE TABLE `users` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, `created_at` datetime(6) NOT NULL, `updated_at` datetime(6) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci | +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> select * from users; +----+------+-----------------+----------------------------+----------------------------+ | id | name | email | created_at | updated_at | +----+------+-----------------+----------------------------+----------------------------+ | 1 | test | aaa | 2021-02-25 15:56:02.123864 | 2021-02-25 15:56:02.123864 | | 2 | test | aaa@example.com | 2021-02-25 16:38:33.311981 | 2021-02-25 16:38:33.311981 | +----+------+-----------------+----------------------------+----------------------------+ 2 rows in set (0.00 sec)今回は以上となります。
参考資料
以下の記事、情報を参考にさせていただきました。
ありがとうございます。
- 投稿日:2021-02-26T01:38:12+09:00
includeメソッド学びメモ書き
includesメソッドとは
アソシエーションの関連付けを事前に取得し
N +1問題
を解決してくれるメソッドのこと。N + 1問題
SQLが大量に発行されることで動作が重くなる問題のこと
なぜSQLが大量に発行されるのか?
(例)
user_idにuserテーブルのidを外部キーとして参照するfruitsテーブルがあったとして・・・
fruitsテーブル
id name user_id 1 りんご 2 2 みかん 3 3 バナナ 1 4 マンゴー 2 userテーブル
1 2 1 タケシ 2 サトシ 3 カスミ UserモデルはFruitモデルに対して1対他の関係になっている
Userモデルclass User < ActiveRecord::Base has_many :fruits endFruitモデルclass Fruits < ActiveRecord::Base belongs_to :user endこの関係で、「全てのユーザーのもってるフルーツを一覧表示する」コードを以下のように書くと、
userテーブルに1回アクセスが行われる(=SQLが発行される。)controller@users = User.allまた、ビューで以下のように記述して一覧表示すると、user1つずつに対してfruitsテーブルにアクセスするので、
userテーブルのレコードの数だけ(3回)SQLが発行される。view@users.each do |user| user.fruits.each do |fruit| fruit.name end endこの1+3回のSQL発行が
1+N問題
となる。includeメソッド
上記の状態で、usersテーブルにアクセスする際に、fruitsテーブルの情報ももってきてくれるのが
includeメソッド
。
引数にアソシエーションで定義した関連名を指定して定義する。(NOTテーブル名)Userモデルclass User < ActiveRecord::Base has_many :fruits #←関連名 endcontroller@users = User.all #変更 @users = User.include(:fruits) #変更後これでusersテーブルへのアクセス1回、fruitsテーブルへのアクセスは1回の計2回に変更される。
コントローラーの時点で関連テーブルの情報を一括取得することで、ビューが動いた時に必要以上のSQLが発行されない。
includeメソッドの親子関係
次に、このFruitモデルのidをさらに外部キーにする関連Juiceモデルがある場合は、以下のように書くことで、親子の子の情報もまとめて取得することができる(便利!)
Userモデル、Fruitモデル、Juiceモデル# User.rb class User < ActiveRecord::Base has_many :fruits end # fruit.rb class Fruit < ActiveRecord::Base belongs_to :user has_many :Juices end # juice.rb class Juice < ActiveRecord::Base belongs_to :fruit endusers_controller.rb@users = User.includes(fruits: :juices)上記を実行すると・・・
実行結果SELECT `users`.* FROM `users` SELECT `fruits`.* FROM `fruits` WHERE `fruits`.`user_id` IN (1, 2, 3) SELECT `juices`.* FROM `juices` WHERE `juices`.`fruit_id` IN (1, 2, 3, 4)3回のアクセスで全て取得できる!!
お疲れ様でした。
- 投稿日:2021-02-26T01:23:28+09:00
式展開ってなんなの?
式展開とは?
- 文字列の中に式を入れることができる機能
- 書き方は文字列中で
#{式}
とするだけ- 文字列を作るときにダブルクォーテーション
"
で囲む記述例と出力例
記述例"今日で#{20+1}歳になりました"表示例今日で21歳になりましたターミナルでirbを使用して確認してみる
出力成功例irb(main):001:0> "今日で#{20+1}歳になりました" => "今日で21歳になりました"
出力失敗例# シングルクォーテーションだと式展開されない irb(main):002:0> '今日で#{20+1}歳になりました' => "今日で\#{20+1}歳になりました"
シングルコーテーションは不可
シングルクォーテーション'
で囲んだ場合は式展開が行われません補足
式展開の
式
に関して
Rubyにおける「式」とは
文字列や数値の他に
- メソッドの呼び出し
- 変数
- 演算子式
などが含まれます。
すなわち、
"文字列", 1000, (1 + 5)
などはすべて式Rubyは記述をすべて式と捉えます。
ターミナル【例】irbirb(main):001:0> "記述はすべて式" # 式の結果 => "記述はすべて式"
irb
でとくにメソッドも使用せずに文字列や数値の値を確認できていた
これは、式の結果が表示されていたからこそ可能となっていた
irb
を使用して想定通りの出力がされない場合は、以下の可能性がある
- irbを起動できていない
(最初にirbとコマンドを入力しないとirbは起動しません)
- 式展開 #{ }が全角になっている
(式展開は半角で記述します)
- 投稿日:2021-02-26T00:50:03+09:00
Active Strageでファイル形式を指定して保存
今回はrailsでの動画投稿機能実装のアウトプットです。
動画投稿の際にActive Strageでmp4ファイルのみ保存させたかったので、自己流ではありますが今回はcontroller側で制御する形で実装しました。
posts_controller.rbclass PostsController < ApplicationController def index @posts = Post.all end def new @post = Post.new end def create @post = Post.new(post_params) if @post.valid? && @post.video.content_type == "video/mp4" @post.save redirect_to posts_path else render :new end end private def post_params params.require(:post).permit(:title, :video).merge(user_id: current_user.id) end endmodels/post.rbclass Post < ApplicationRecord belongs_to :user has_one_attached :video validates :title, :video, presence: true end今回はcontent_typeで保存するデータのファイル形式を指定できるということに気づくことが出来ました。
ですが本当は他の書き方で、より効率的に、ファイルサイズ等もまとめて指定して書ける方法があるのだと思います。
モデル側で書く方が正しいのかもしれません。まだまだ自分は初心者なので、
よりDRYな実装ができるよう更に学習を深めて改善に努めていきたいと思います。