- 投稿日:2021-03-14T23:49:17+09:00
Action Cableでできることと実装方法
Action Cableは、Railsのアプリケーションと同様の記述で、WebSocket通信という双方向の通信によるリアルタイム更新機能を実装できるフレームワークで、Rails5から実装されました。
Action Cableを利用することで、たとえばリアルタイムで更新されるチャット機能を実装することができます。
チャネルの作成
channel
リアルタイム更新機能を実現するサーバー側の仕組みでデータの経路を設定したり、送られてきたデータを画面上に表示させたりします。
チャネルを作成するコマンド
ターミナルrails g channel message #チャネル名 # ↓実行結果↓ Running via Spring preloader in process ***** invoke test_unit create test/channels/message_channel_test.rb create app/channels/message_channel.rb identical app/javascript/channels/index.js identical app/javascript/channels/consumer.js create app/javascript/channels/message_channel.js生成されるファイルのうち以下の2つが重要です。
app/controller/message_controller.rb
→ クライアントとサーバーを結びつけるためのファイル
app/javascript/channels/message_channel.js
→ サーバーから送られてきたデータをクライアントに描画するためのファイルクライアントとサーバーを結びつける
stream_fromメソッド
サーバーとクライアントを関連づけるメソッドでAction Cableにあらかじめ用意されています。
app/controller/messages_controller.rbclass MessageChannel < ApplicationCable::Channel def subscribed stream_from "message_channel" #記述箇所 end def unsubscribed # Any cleanup needed when channel is unsubscribed end endbroadcast
stream_fromメソッドで関連付けられるデータの経路で、サーバーから送られるデータの経路です。broadcastを介してデータをクライアントに送信します。
broadcast
を使ってコントローラーへ以下のように記述します。app/controller/messages_controller.rbclass MessagesController < ApplicationController def new @messages = Message.all @message = Message.new end def create @message = Message.new(text: params[:message][:text]) if @message.save ActionCable.server.broadcast 'message_channel', content: @message end end end
broadcast
という経路を通して、message_channel
に向けて@message
をcontent
に入れてデータを送信しています。JavaScriptへの記述
サーバーから送信されたデータは
data
の中にcontent
という名称で入っているので、data.content.text
で使用できます。app/javascript/channels/message_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("MessageChannel", { received(data) { // 以下で使用 const html = `<p>${data.content.text}</p>`; const messages = document.getElementById('messages'); const newMessage = document.getElementById('message_text'); messages.insertAdjacentHTML('afterbegin', html); newMessage.value=''; } });ちなみにビューはこんな感じです。
app/views/messages/new.html.erb<h3>mini-talk-app</h3> <%= form_with model: @message do |f| %> <%= f.text_field :text %> <%= f.submit '送信' %> <% end %> <div id="messages"> <% @messages.reverse_each.each do |message| %> <p><%= message.id %></p> <p><%= message.text %></p> <% end %> </div>content以外もクライアントに送信できる
app/controller/messages_controller.rb
では保存したメッセージの値をcontent
に入れて送信していましたが、設定すれば他の値も送ることができます。ぼくが今開発しているアプリでは、こんな感じで情報を追加してクライアント側に送信しています。
app/controllers/chats_controller.rbclass ChatsController < ApplicationController def create @chat = Chat.new(chat_params) if @chat.save # userとtimeにそれぞれ@userと@timeを入れている @user = current_user @time = @chat.created_at.strftime("%Y-%m-%-d %-H:%-M") ActionCable.server.broadcast 'chat_channel', content: @chat, user: @user, time: @time end end private def chat_params params.require(:chat).permit(:text).merge( user_id: current_user.id, group_id: params[:group_id] ) end endクライアント(JavaScript)側ではこのようにデータを利用しています。JavaScript側で投稿日時の表示形式を変更する方法が分からなかったので、事前にサーバー(Ruby)側で表示形式を変更してから送信しています。
app/javascript/channels/chat_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("ChatChannel", { received(data) { // 送信されたデータを以下で使用 const html = `<div class="each-chat"> <p class="chat-time">${data.time}</p> <p class="chat-text">${data.content.text}</p> <p class="chat-user">${data.user.name}</p> </div>`; const chats = document.getElementById("chats"); chats.insertAdjacentHTML("afterbegin", html); const newChat = document.getElementById("chat_text"); newChat.value=""; } });参考資料
- 投稿日:2021-03-14T23:23:44+09:00
docker内でGem(特にbundler)のdefault設定を解除したい!
概要
ローカル環境でも厄介なGemのdefault設定。
「このバージョンのGemで運用していきたいのに!」といった気持ちに反して、bundle installすると、default設定されたバージョンでインストールされてしまう。。。ローカル環境ではその解除はできるが、Dockerのコンテナ内では少しだけ訳が違ったので、まとめたいと思い本記事を制作しました。
不足点、誤りがあれば遠慮なくご指摘いただけますと幸いです。
経緯
これまでローカル環境にて開発を行い、いざ本番環境へデプロイ!となるとエラーが返され、苦い思いをしてきた私。
今回はdocker-compose up
で立ち上げたコンテナ内でgem list
を見ると、ローカルでのgem list
とバージョンの違いが生じていました。
そこで、コンテナ内でも指定したバージョンのGemで運用したく奮闘したのが経緯です。方法
①コンテナ内に入る
ローカルにて自身のアプリのディレクトリにて
% docker-compose exec web bash root@コンテナID:/アプリ名# 以降プロンプト以外は省略します②Gemを確認(省略可能)
# gem list bundler ※ちなみに全てのGemをリスト化するには"bundler"を省略 *** LOCAL GEMS *** bundler (2.1.4, default: 1.17.2)**この忌々しいdefaultを解除していきます。
③default設定されているGemのありかを探る。
# gem environment RubyGems Environment: ...略 - INSTALLATION DIRECTORY: /usr/local/bundle - USER INSTALLATION DIRECTORY: /root/.gem/ruby/2.6.0 ...略 - GEM PATHS: - /usr/local/bundle - /root/.gem/ruby/2.6.0 - /usr/local/lib/ruby/gems/2.6.0 ...略ここでちなみにローカルで同様に
gem environment
コマンドを実施した時の結果を下記に記します。% gem environment RubyGems Environment: ...略 - INSTALLATION DIRECTORY: /Users/ユーザー/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0 - USER INSTALLATION DIRECTORY: /Users/ユーザー/.gem/ruby/2.6.0 ...略 - GEM PATHS: - /Users/ユーザー/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0 - /Users/ユーザー/.gem/ruby/2.6.0 ...略INSTALLATION DIRECTORYに注目してください。
指定されているディレクトリがわずかに違います。
⑤の項目にてさらに解説します。④ホームに戻る
# cd root@コンテナID:~#⑤default設定のあるディレクトリまでたどり着く
ここで、ローカル環境でのたどり着き方の場合は、INSTALLATION DIRECTORYに記載されているパスまで
cdコマンド
で行けばいいのですが・・・ローカルの場合!
% cd /Users/ユーザー/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0 ユーザー@PC 2.6.0 %コンテナ内の場合!!
# cd /usr/local/lib/ruby/gems/2.6.0 root@コンテナID:/usr/local/lib/ruby/gems/2.6.0#INSTALLATION DIRECTORYに記載されているパスでは「そんなディレクトリは無い!!」と怒られたので、別のディレクトリを探したところ,
GEM PATHSに記載のあったディレクトリが正解だったようです。⑥specificationsの中のdefaultを削除する
lsコマンド
を使ってみると、このディレクトリの中にspecifications
というディレクトリがあるはずです。
この中にdefault設定があります。
※気になる方はここで、cd specifications
して、lsコマンド
をすると、default
と記載のあるGemがいくつかあると思います。# rm -rf specifications/default/これでspecifications内のdefaultディレクトリが全て削除されます。
⑦念の為、確認。
# gem list bundler *** LOCAL GEMS *** bundler (2.1.4)bundlerのdefaultが解除されています。
ここでbundle installし、Gemfile.lockを確認すると最後の行に・・・BUNDLED WITH 2.1.4と、自分の好きなバージョンでバンドルされていることが確認できました!
考察
ここで学んだことが2つあります。
まず1つ
当然ではありますが、ローカル環境とコンテナ環境は別物。Gemのバージョンの相違に気づけたこと。もう1つ
同じgem environment
に返される結果は、似てこそあれども微妙な違いがあること。
自分の求める情報はどこに記載があるのか注視しなければならない。これまでデプロイの時に何かしらのエラーに叩きのめされていたので、docker使用時に未然にGemのバージョンを合わせれて良かったと思います。
誰かの参考になれば幸いです。
ありがとうございました。
- 投稿日:2021-03-14T22:56:01+09:00
rails&vueでherokuにデプロイする際に参考になったこと
はじめに
Rails 6.0.3.5
ruby 2.6.5
yarn 1.22.10
にてデプロイを実行した。アプリ概要
投稿サイト
・jwtによるログインログアウト機能
・投稿機能(ログイン後投稿可、投稿者のみ削除編集可)
・プロフィール編集機能エラー内容
デプロイは成功し、
ログイン機能は正常に動いたのだが、
POST機能、EDIT機能を行う際に、
検証ツールのconsoleにて、401 Unauthorizedエラーが出現、
なぜ、ログインしているにも関わらず401が出現するのか、
protect_from_forgery with: :null_sessionになっていることを確認し、
skip_before_action :verify_authenticity_tokenを追加。
すると、今度は、
500 (Internal Server Error)が出現、
$ heroku logsにてエラー内容を確認したところ、
undefined method `post' for nil:NilClassというエラー内容、
また、プロフィール編集時のエラーは、undefined method `update' for nil:NilClassとなりました、、、
原因検索1
ローカル環境で動いているため、
[post]や[update]が定義されていないということは考えにくい。def create post = current_user.posts.create!(post_params) #ここのcurrent_user render json: post, serializer: postSerializer endpostする前のcurrent_userが渡っていなと推測。
なぜcurrent_userが取れていないのか、
local環境とproduction環境の違いをチェック。config/enviroments/development.rbとproduction.rbの違いを確認した。
認証に関わりそうな部分はなく、
特にここが原因ではないみたい。原因検索2
jwtが送信できているかを、ブラウザ検証ツールのnetworkタブで確認する.
Authorization: Bearer にて、トークンが到達していることを確認。原因検索3
ローカル環境で、問題なく動いているため、
再度、herokuにて問題がないことを確認、
herokuのsettings/Reveal Config Varsにて、、、、、master_keyの設定をしていませんでした。。。
secret_key_baseを設定し安心し切っていましたが、
思わぬところに原因がありました。補足
rails & vueのデプロイ
(herokuにpushした後)につきましては、
ログアウト → ログインをしてから、挙動を確かめるようにしましょう。最後に
初心者のうちは思いもよらないところにミスがある可能性が高いので、
深読みしすぎず、やるべきことをしっかりやってあるかの確認を、
怠らないように気をつけたいと思います。ご視聴いただきありがとうございます。
誰かの役に立てば幸いです。
- 投稿日:2021-03-14T22:49:31+09:00
GitにおけるRails Tutorialになかった注意点!(in チーム開発の学び)
はじめに
これは実務と同じ方式で行うチーム開発体験学習で、
GitHubを利用する際、現役の先輩エンジニアにご指摘いただいた内容です。
それまで私は全く考えてこなかったことですが、セキュリティを考えると当然のことです。目次
gitに記録してはいけないもの
端的に言うと下記2つのファイル
・ config/credential.yml.enc
・ config/master.keyこれらをgitに記録してしまい、
あまつさえGitHubにプッシュしてしまいました。
やっちまったなあ!もし実務でやったらセキュリティインシデント
こちらをご覧ください
つまり、秘密情報を管理する仕組みとその鍵を自分は公衆の面前に晒してしまったわけですね。
これが仕事だったらもう大変なことです。ネットに放流した鍵はもう安全ではない。対策方法は?
GitHubに一度上げてしまったことを完全に戻すのは以下の方法を取らないといけないみたいです。
しかし、これも結局は悪用する人が先にセンシティブファイルを発見したら終わりなわけで…
こんなことにならないよう、普段から大事なファイルはgitignoreに書き込んでおきましょう!
- 投稿日:2021-03-14T21:53:08+09:00
flutter webがrailsを乗る
内容
flutter webで開発したfrontをrails serverでアクセルできるようにする手順
バージョン
flutter 2.0.1
rails 6.1.3手順
- flutterをbuildする
flutter projectのフォルダで以下のcommandでbuildすると
flutter build webフォルダ
build/web
が作成される
- rails applicationを作成
rails new flutter-on-rails rails g controller Home indexconfig/routes.rbを以下のように設定
Rails.application.routes.draw do root 'home#index' end
- flutterのbuildしたファイルをrailsにcopy
- flutter projectのフォルダ
build/web/assets
をrailsのapp/assets/
下にcopy- flutter projectのフォルダ
build/web/icons
をrailsのapp/assets/
下にcopy- flutterのbuildした
build/web/main.dart.js
ファイルの内容をrailsのapp/javascript/packs/flutter.js
にcopy- flutterのファイル
build/web/index.html
の内容でrailsのapp/views/home/index.html.erb
ファイルを上書きする- railsの
app/views/home/index.html.erb
ファイルにflutter.jsの参照するようにする<!DOCTYPE html> <html> <head> .......省略 <link rel="manifest" href="manifest.json"> <%= javascript_pack_tag "flutter" %> </head> .......省略
- rails sでserverを起動する
- 投稿日:2021-03-14T19:34:36+09:00
ActiveRecord を googleauth の TokenStore として使う方法
Google API を Ruby から操作するための Google 製 gem が googleauth で、これは当然 OAuth2 のトークンをファイルや DB に保存しておくように作られているが、中に含まれているトークン保存のためのクラス (TokenStore) は以下の2種類しかなくて、つまり ActiveRecord 用の TokenStore は用意されていない。
- FileTokenStore (https://github.com/googleapis/google-auth-library-ruby/blob/master/lib/googleauth/stores/file_token_store.rb)
- RedisTokenStore (https://github.com/googleapis/google-auth-library-ruby/blob/master/lib/googleauth/stores/redis_token_store.rb)
だけど、上記 2 クラスは Google::Auth::TokenStore https://github.com/googleapis/google-auth-library-ruby/blob/master/lib/googleauth/stores/redis_token_store.rb を継承しているようだったので、雑に継承して通せたのでメモ。
これから作るのは、
- GoogleAuthToken ( token_key と client_id の組をもつ Model )
- ActiveRecordTokenStore ( Google::Auth::TokenStore のサブクラスで、 ActiveRecord 用 )
$ rails generate model google_auth_token token_key:string client_id:string
class GoogleAuthToken < ApplicationRecord belongs_to :user #これは任意だけど、ふつうこういう形になるんじゃないかな validates :token_key, presence: true, uniqueness: true validates :client_id, presence: true end
app/models/active_record_token_store.rb
に、class ActiveRecordTokenStore < Google::Auth::TokenStore def initialize user_id @user_id = user_id end def load id record = GoogleAuthTokenStore.find_by! user_id: @user_id, token_key: id record.to_json end def store id, token json = JSON.parse(token) client_id = json["client_id"] access_token = json["access_token"] refresh_token = json["refresh_token"] expiration_time_millis = json["expiration_time_millis"] record = GoogleAuthToken.find_or_initialize_by user_id: @user_id, token_key: id, client_id: client_id, access_token: access_token, refresh_token: refresh_token, expiration_time_millis: expiration_time_millis record.save end def delete id record = GoogleAuthToken.find_by! user_id: @user_id, token_key: id record.destroy end endこんな感じにしておけば、例えば
def authorize client_id, token_store Google::Auth::UserAuthorizer.new client_id, [ Google::Apis::DriveV3::AUTH_DRIVE, ], token_store endとすると、 API アクセスしやすい。
- 投稿日:2021-03-14T19:28:12+09:00
【Rails】CSSにbackground-imageで設定した背景画像が表示されない
現在作成している自作のアプリケーションで、
上のように背景に画像を設定したく、
CSSでbackground-image プロパティを使えば良いことが分かり、
以下のように記述しました。.sample-contents { width: 100vw; height: 350px; background-image: image-url('sample-top.JPG'); background-repeat: no-repeat; background-size: cover; background-position: center; }これで画像が出るはず!だったんですが、画像が表示されなかったので、
解決するために試したこと等を記録します。解決したかったこと
CSSでbackground-image プロパティを使ったが、画像が表示されない
今回の解決方法
記述していた.css ファイルの拡張子を.scss に変更したら表示できた。
というわけで本当にただのケアレスミスでした。
ただまだcssとscssがどう違うのかや、何故cssだと表示できなくてscssだとできたのかはちゃんと理解できていないのでこれから勉強します。解決するまでにチェックしたポイント
background-color は表示できるか
クラス名のスペルミスが無いかの確認も兼ねて一旦background-color を指定すると、
きちんと表示できました。
じゃあ画像に何か問題があるのかと考えて次へ。画像パスの指定は合っているか
app/assets/images にちゃんと保存してあるかどうかを確認。
VSCodeの場合、ファイルを右クリックで画像のようにパスがコピーできたので、パスは確かだろうと確認できました。
以上です。
- 投稿日:2021-03-14T17:51:29+09:00
ActionController::UnknownFormat in ○○Controller#show
○○Controller#show is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot.
うわあああああ~!!!!!!
難しそうなエラーだけど、対応するViewが無い時に起こるエラー
Viewがどうなっているか確認すれば治ることが多い!いかなる時も落ち着いて!私っ!!
- 投稿日:2021-03-14T16:26:37+09:00
サーバーの構築からソースコードのデプロイまで(Amazon Linux 2, Nginx + Puma, Rails 6.1, Ruby2.7.2)
この記事での注意
この作業はローカル環境からの作業とサーバーでの作業が複雑に入り交じっています。その為、ターミナルで実行するコマンドの説明に「ローカル」「サーバー」と記載しています。お間違えないように気をつけてください。
アプリケーション名を「sample611」 としています。ご自身のアプリケーション名に読み替えてご参考ください。
サーバーにインストールする環境
- Amazon Linux 2
- Ruby 2.7.2 (rbenv)
- Rails 6.1
- MySQL or PostgreSQL
- Nginx + Puma 5
- Node.js 12
- Yarn
本番サーバーの構築
本番サーバーのEC2のOSがAmazon Linux2の場合の設定手順を紹介します。
1. システムライブラリアップデート
まずはAmazon Linux 2のシステムライブラリを更新します。
サーバー$ sudo yum update -y2. タイムゾーン、ロケール
日本での運用する場合はタイムゾーンと言語の設定を日本にした方がわかりやすいと思います。
サーバー$ sudo timedatectl set-timezone Asia/Tokyo $ sudo localectl set-locale LANG=ja_JP.UTF-83. NTP
サーバーの時刻のずれを自動で補正するタイムサーバーの設定を行います。
サーバー$ yum info chrony $ chronyc sources -v4. 標準パッケージインストール
Rubyのインストールなどで必要になるライブラリをインストールします。
サーバー$ sudo yum -y install gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel libffi-devel libxml2 libxslt libxml2-devel libxslt-devel git5. データベースモジュールのインストール
Railsからデータベースにアクセスするためのアダプタをインストールする必要がありますが、そのインストールに必要なライブラリをデータベース別にインストールします。
- PostgreSQLの場合
サーバー$ sudo yum -y install postgresql-devel
- MySQLの場合
サーバー$ sudo yum -y install mysql-devel6. Rubyのインストール
Railsを動かすためにはRubyのインストールが必要です。いろんなインストール方法がありますが、Rubyのバージョンを切り替えることができるrbenvを使った方法を説明します。
6.1 rbenvのインストール
サーバー$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile $ rbenv -v6.2 ruby-buildのインストール
サーバー$ git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build $ cd ~/.rbenv/plugins/ruby-build $ sudo ./install.sh $ rbenv install -l6.3 Rubyのインストール
rbenvとruby-buildをインストールが終わったので、Rubyのインストールを行います。
サーバー$ rbenv install 2.7.2 $ rbenv global 2.7.2rubyのインストールにはすこし時間がかかります。
rbenv globalでシステム全体で使用するRubyのバージョンを指定しています。7. Rails 6で必要なモジュールのインストール
7.1 Node.jsのインストール
Rails 6からは Node.js や Yarn が必要になりました。Node.jsはサーバーサイドでのJavaScript実行環境で、Yarnはfacebook製のJavaScriptのパッケージマネージャーです。
サーバー$ curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash - $ sudo yum install -y nodejs $ sudo npm install -g n $ sudo n stable $ sudo ln -snf /usr/local/bin/node /usr/bin/node7.2 Yarnのインストール
サーバー$ curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo $ sudo yum install yarn -y8. Railsのインストール
Rails 6のインストールを行います。gem install railsでもインストールできますが、ここではパッケージ管理ソフトbundlerからインストールする方法を紹介します。
サーバー$ cd ~ $ mkdir rails $ cd rails $ bundle init $ vim Gemfile (gem "rails"をコメントアウト解除) $ bundle install $ bundle exec rails -v (インストールされたことを確認) $ cd .. $ rm -rf railsデータベースの設定
9. 本番データベースの作成
ここではRDSの設定については詳しく説明しません。すでにRDSにデータベースが設定されていることを前提に進めていきますが、下記をご確認ください。
- RDSにて作成したデータベースのユーザー名、パスワード、データベースエンドポイントをご確認ください。
- RDSのセキュリティグループでウェブサーバーとなるEC2のセキュリティグループのアクセス許可を設定してください。
10. Railsアプリと本番データベースの接続設定
Railsとデータベースの接続設定を行います。データベースの設定にはデータベースにアクセスするためのパスワードが含まれますが、これを直接database.ymlに記載すると、github上でパスワードを知ることができてしまい危険です。
こういった機密情報は config/credentials.yml.enc にセットします。(Rails5.2以降で可能)
このファイルはエディタで表示すると暗号化された文字列になっていますが、 master.key を使うことで閲覧、編集することができます。この credentials.yml.enc と master.key の関係を理解しておいてください。
次のコマンドでcredentials.yml.encを編集します。ここではエディタをvimに指定しています。
ローカル$ EDITOR=vim bin/rails credentials:edit下記のようにdbの項目を追記し、RDS作成時に設定したユーザー名、パスワード、データベースエンドポイントを記述します。
credential.yml.enc(ローカル)# aws: # access_key_id: 123 # secret_access_key: 345 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx db: username: (username) password: (password) host: (database endpoint)終わったら、:wqでエディタを終えます。
次に本番環境において、データベースの設定が適用されるようconfig/database.ymlを編集します。database.ymlの最後の方にproductionの項があります。そこを下記のように修正します。
database.yml(ローカル)production: <<: *default database: sample611_production username: <%= Rails.application.credentials.db[:username] %> password: <%= Rails.application.credentials.db[:password] %> host: <%= Rails.application.credentials.db[:host] %> port: 5432Rails.application.credentials.dbとすることでcredentials.yml.encのdbの項を読みにいきます。
portはデータベースで使用するポートを指定します。一般的にはPostgreSQLの場合は 5432、MySQLの場合は 3306 になります。ここで、改修したコードをいったんコミットし、githubへプッシュしましょう。
ローカル$ git add . $ git commit -m "Setup database" $ git push origin masterデプロイ設定
デプロイツールCapistranoを使ったデプロイの設定を行います。
11. Capistranoのインストール
Capistranoをインストールします。下記のようにGemfileのdevelopmentグループにcapistrano, capistrano-rails, capistrano-rbenvを追加し、bundle installします。
Gemfile(ローカル)group :development do gem "capistrano", "~> 3.14", require: false gem "capistrano-rails", "~> 1.6", require: false gem 'capistrano-rbenv', '~> 2.2' endローカル$ bundle install $ bundle exec cap install12. Capfileの設定
下記のようにCapfileに追記します。
Capfile(ローカル): # require "capistrano/rails/migrations" # require "capistrano/passenger" require 'capistrano/rails' require 'capistrano/rbenv' :13. デプロイの設定
Capistranoのデプロイ設定を行います。全体的なデプロイの設定を「config/deploy.rb」、本番環境用の設定を「config/deploy/production.rb」に記述します。
config/deploy.rb(ローカル)lock "~> 3.15.0" set :application, "sample611" # 自分のリポジトリを設定 set :repo_url, "git@github.com:yukeippi/sample611.git" # rbenvの設定 set :rbenv_type, :user set :rbenv_ruby, '2.7.2' # Railsアプリの設置先 set :deploy_to, "/var/rails/sample611" # 共有ファイルの設定 append :linked_files, "config/master.key" # 共有ディレクトリの設定 append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system" # リリース保存数 set :keep_releases, 5linked_filesはデフォルトではconfig/database.ymlとなっていますが、config/master.keyに変更してください。
次にconfig/deploy/production.rbを編集します。下記の (server ip) にはデプロイ先EC2のIPをセットしてください。
githubへのキー登録については下記をご参照ください。config/deploy/production.rb(ローカル)set :rails_env, 'production' set :branch, 'master' # githubへの設定 set :ssh_options, { auth_methods: [ 'publickey' ], keys: [ '~/.ssh/aws.pem' ], } server '(server IP)', user: 'ec2-user', roles: %w{app db web}区切りが良いので、ここでいったんコミット、プッシュしましょう。(プッシュは任意です)
ローカル$ git add . $ git commit -m "Setup capistrano" $ git push oririn master14. Githubへのキー登録
デプロイ時にデプロイ先サーバーがgithubからソースコードを取得するための設定を行います。
この設定は下記の記事をご参照ください。
15. デプロイ先ディレクトリの作成
Railsアプリのプログラムの設置(デプロイ)を行います。アプリケーションの設置場所を「/var/rails」、実行者を「ec2-user」とした場合、下記のコマンドを実行し、ディレクトリを作ります。
サーバー$ cd /var/ $ sudo mkdir rails $ sudo chown ec2-user:ec2-user railsここで「cap deploy:check」を実行しますが、下記のように失敗します。この処理の目的は、Capistranoにデプロイに必要なディレクトリを作らせることです。
ローカル$ bundle exec cap production deploy:check 00:02 deploy:check:make_linked_dirs 01 mkdir -p /var/rails/sample611/shared/config ✔ 01 ec2-user@(server IP) 0.078s 00:02 deploy:check:linked_files ERROR linked file /var/rails/sample611/shared/config/master.key does not exist on (server IP)エラーメッセージにあるように「master.key」がないことが原因です。サーバーに入って下記のようにmaster.keyを作成してください。
サーバー$ cd /var/rails/sample611/shared/config/ $ vim master.key (ローカルにあるmaster.keyを貼り付け)これで問題は解決しましたので、再度、デプロイチェックを行います。
ローカル$ bundle exec cap production deploy:check成功するはずです。これでデプロイ先ディレクトリができました。
16. Databaseのcreate
しかし、まだこの時点でもデプロイ途中で失敗します。それはデータベースがcreateされていないからです。deployでデータベースのmigrateは実行しますが、createしていないため、migrateできずに失敗してしまいます。
そこで、失敗前提でデプロイを実行し、そのときにデプロイされたソースコードを使ってcreateします。
ローカル$ bundle exec cap production deploy最初はgemをインストールするため、時間がかかります。
しばらく待つとデプロイは終わり、途中で失敗していることが確認できます。その後、本番サーバーに入り、下記のようにデプロイされたソースコードのルートに移動し、データベースを作成します。サーバー$ cd /var/rails/sample611/releases/(最新リリース)/ $ bundle exec rails db:create RAILS_ENV=production(最新リリース)は「20210216104333」といったディレクトリ作成日時を意味するディレクトリを指します。ソースコードがデプロイされると、Capistranoがこのようなディレクトリを作成し、そこにプログラムを保存します。
これでデータベースが作成されたら、デプロイが可能になります。
再度デプロイします。ローカル$ bundle exec cap production deployこれで成功するはずです。
WebサーバーとRailsアプリの連携
17. Pumaの設定
本番環境のPumaの設定を行います。
config/puma/production.rb(ローカル)# Puma can serve each request in a thread from an internal thread pool. # The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } threads min_threads_count, max_threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # port ENV.fetch("PORT") { 3000 } # Specifies the `environment` that Puma will run in. # environment ENV.fetch("RAILS_ENV") { "production" } # Specifies the `pidfile` that Puma will use. # pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } # Specifies the number of `workers` to boot in clustered mode. # Workers are forked web server processes. If using threads and workers together # the concurrency of the application would be max `threads` * `workers`. # Workers do not work on JRuby or Windows (both of which do not support # processes). # # workers ENV.fetch("WEB_CONCURRENCY") { 2 } # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code # before forking the application. This takes advantage of Copy On Write # process behavior so workers use less memory. # # preload_app! # Allow puma to be restarted by `rails restart` command. plugin :tmp_restart bind "unix:///var/rails/sample611/shared/tmp/sockets/puma.sock"RAILS_ENVをproductionに指定しているところと、最下行でsocketを指定しているのがポイントです。
ここでいったんコミット、プッシュしてください。
ローカル$ git add . $ git commit -m "Setup puma for production" $ git push origin master18. Nginxのインストール
ここからはサーバーでの作業になります。
サーバーにNginxをインストールします。サーバー$ sudo amazon-linux-extras install nginx1 -y19. NginxとPumaの紐付け
次にNginxとPumaを紐付けるための設定ファイルを作成します。ポイントはuptreamの設定、rootの指定、proxy_passの指定です。
サーバー$ sudo vim /etc/nginx/conf.d/sample611.conf/etc/nginx/conf.d/sample611.conf(サーバー)upstream puma { server unix:///var/rails/sample611/shared/tmp/sockets/puma.sock fail_timeout=0; } server { listen 80; server_name example.com; root /var/rails/sample611/current/public; try_files $uri/index.html $uri @railsapp; location @railsapp { proxy_pass http://puma; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; } error_page 500 502 503 504 /500.html; client_max_body_size 4G; keepalive_timeout 10; }upstream puma内にあるpuma.sockのパスに気をつけてください。これは15のbindで設定したパスにあわせなければなりません。
20. Pumaのサービス設定
次にpumaをsystemctlで起動や再起動ができるように設定を行います。
サーバー$ sudo vim /etc/systemd/system/puma.service/etc/systemd/system/puma.service(サーバー)[Unit] Description=Puma HTTP Server After=network.target [Service] Type=simple Environment="RAILS_ENV=production" WorkingDirectory=/var/rails/sample611/current ExecStart=/home/ec2-user/.rbenv/shims/bundle exec /var/rails/sample611/shared/bundle/ruby/2.7.0/bin/puma -C /var/rails/sample611/current/config/puma/production.rb Restart=always [Install] WantedBy=multi-user.targetここでpumaの実行パスや実行時の設定ファイルを指定しています。
あと、このままでは実行権限がないため、下記のコマンドでpuma.serviceに実行権限を付与してください。サーバー$ sudo chmod +x /etc/systemd/system/puma.serviceこれでサーバー側の設定は終わりです。
21. Puma, Nginxの起動
いよいよウェブサーバー、アプリケーションサーバーの起動です。まず、Pumaを起動します。
サーバー$ sudo systemctl daemon-reload $ sudo systemctl start puma下記のコマンドで起動を確認します。
サーバー$ sudo systemctl status puma ● puma.service - Puma HTTP Server for sample611 (production_mysql) Loaded: loaded (/etc/systemd/system/puma.service; enabled; vendor preset: disabled) Active: active (running) since 土 2021-02-20 15:47:30 JST; 27min ago Main PID: 2876 (bundle) CGroup: /system.slice/puma.service └─2876 puma 5.1.1 (tcp://0.0.0.0:3000,unix:///var/rails/sample611/shared/tmp/sockets/puma.sock) [20210216072308]上記のようになければ「Active: active」になっていれば、起動成功です。
もし「Active: inactive」になっていれば、何かしらの設定に間違いがある可能性があります。
journalctl -xe等を使って原因を調べてください。次にnginxの起動します。
サーバー$ sudo systemctl start nginx.serviceこれでブラウザを使って下記のようにサイトにアクセスすれば画面に表示されるはずです。
http://(サイトのIP)/
21. デプロイ時のリスタート設定
プログラムをデプロイしたときはアプリケーションサーバーをリスタートする必要があります。capistranoのrestartタスクを追加しておくと、それも自動で実行してくれます。
config/deploy.rb(ローカル)namespace :deploy do task :restart do on roles(:web), in: :sequence, wait: 5 do within release_path do execute "sudo systemctl daemon-reload" execute "sudo systemctl restart puma" end end end after :finishing, :restart endこれでローカル側のコード改修が終わりです。
最後にここまでをコミットしましょう。ローカル$ git add . $ git commit -m "Setup restart puma"再起動することを確認するため、デプロイします。
ローカル$ bundle exec cap production deploy : 00:34 deploy:cleanup Keeping 5 of 6 deployed releases on (server IP) 01 rm -rf /var/rails/sample611/releases/20210314010936 ✔ 01 ec2-user@(server IP) 0.797s 00:35 deploy:restart 01 sudo systemctl daemon-reload ✔ 01 ec2-user@(server IP) 0.199s 02 sudo systemctl restart puma ✔ 02 ec2-user@(server IP) 0.094s 00:35 deploy:log_revision 01 echo "Branch master (at 0e3b185044e3a56f20a3bf37d7c6ce31f5a92b54) deployed as release 20210314015609 b… ✔ 01 ec2-user@(server IP) 0.119sデプロイログからrestartが実行されていることがわかると思います。
これでデプロイは成功です!お疲れ様でした。puma, nginxの自動起動設定
最後にpuma,nginxの自動起動設定を行っておくとよいでしょう。これを行うことでサーバーを再起動しても自動でWebアプリケーションを起動してくれます。
サーバー$ sudo systemctl enable puma.service $ sudo systemctl enable nginx.service以上です。
- 投稿日:2021-03-14T16:26:37+09:00
Rails 6アプリをAWSへのデプロイする手順
この記事での注意
この作業はローカル環境からの作業とサーバーでの作業が複雑に入り交じっています。その為、ターミナルで実行するコマンドの説明に「ローカル」「サーバー」と記載しています。お間違えないように気をつけてください。
アプリケーション名を「sample611」 としています。ご自身のアプリケーション名に読み替えてご参考ください。
サーバーにインストールする環境
- Amazon Linux 2
- Ruby 2.7.2 (rbenv)
- Rails 6.1
- MySQL or PostgreSQL
- Nginx + Puma 5
- Node.js 12
- Yarn
本番サーバーの構築
本番サーバーのEC2のOSがAmazon Linux2の場合の設定手順を紹介します。
1. システムライブラリアップデート
まずはAmazon Linux 2のシステムライブラリを更新します。
サーバー$ sudo yum update -y2. タイムゾーン、ロケール
日本での運用する場合はタイムゾーンと言語の設定を日本にした方がわかりやすいと思います。
サーバー$ sudo timedatectl set-timezone Asia/Tokyo $ sudo localectl set-locale LANG=ja_JP.UTF-83. NTP
サーバーの時刻のずれを自動で補正するタイムサーバーの設定を行います。
サーバー$ yum info chrony $ chronyc sources -v4. 標準パッケージインストール
Rubyのインストールなどで必要になるライブラリをインストールします。
サーバー$ sudo yum -y install gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel libffi-devel libxml2 libxslt libxml2-devel libxslt-devel git5. データベースモジュールのインストール
Railsからデータベースにアクセスするためのアダプタをインストールする必要がありますが、そのインストールに必要なライブラリをデータベース別にインストールします。
- PostgreSQLの場合
サーバー$ sudo yum -y install postgresql-devel
- MySQLの場合
サーバー$ sudo yum -y install mysql-devel6. Rubyのインストール
Railsを動かすためにはRubyのインストールが必要です。いろんなインストール方法がありますが、Rubyのバージョンを切り替えることができるrbenvを使った方法を説明します。
6.1 rbenvのインストール
サーバー$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile $ rbenv -v6.2 ruby-buildのインストール
サーバー$ git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build $ cd ~/.rbenv/plugins/ruby-build $ sudo ./install.sh $ rbenv install -l6.3 Rubyのインストール
rbenvとruby-buildをインストールが終わったので、Rubyのインストールを行います。
サーバー$ rbenv install 2.7.2 $ rbenv global 2.7.2rubyのインストールにはすこし時間がかかります。
rbenv globalでシステム全体で使用するRubyのバージョンを指定しています。7. Rails 6で必要なモジュールのインストール
7.1 Node.jsのインストール
Rails 6からは Node.js や Yarn が必要になりました。Node.jsはサーバーサイドでのJavaScript実行環境で、Yarnはfacebook製のJavaScriptのパッケージマネージャーです。
サーバー$ curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash - $ sudo yum install -y nodejs $ sudo npm install -g n $ sudo n stable $ sudo ln -snf /usr/local/bin/node /usr/bin/node7.2 Yarnのインストール
サーバー$ curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo $ sudo yum install yarn -y8. Railsのインストール
Rails 6のインストールを行います。gem install railsでもインストールできますが、ここではパッケージ管理ソフトbundlerからインストールする方法を紹介します。
サーバー$ cd ~ $ mkdir rails $ cd rails $ bundle init $ vim Gemfile (gem "rails"をコメントアウト解除) $ bundle install $ bundle exec rails -v (インストールされたことを確認) $ cd .. $ rm -rf railsデータベースの設定
9. 本番データベースの作成
ここではRDSの設定については詳しく説明しません。すでにRDSにデータベースが設定されていることを前提に進めていきますが、下記をご確認ください。
- RDSにて作成したデータベースのユーザー名、パスワード、データベースエンドポイントをご確認ください。
- RDSのセキュリティグループでウェブサーバーとなるEC2のセキュリティグループのアクセス許可を設定してください。
10. Railsアプリと本番データベースの接続設定
Railsとデータベースの接続設定を行います。データベースの設定にはデータベースにアクセスするためのパスワードが含まれますが、これを直接database.ymlに記載すると、github上でパスワードを知ることができてしまい危険です。
こういった機密情報は config/credentials.yml.enc にセットします。(Rails5.2以降で可能)
このファイルはエディタで表示すると暗号化された文字列になっていますが、 master.key を使うことで閲覧、編集することができます。この credentials.yml.enc と master.key の関係を理解しておいてください。
次のコマンドでcredentials.yml.encを編集します。ここではエディタをvimに指定しています。
ローカル$ EDITOR=vim bin/rails credentials:edit下記のようにdbの項目を追記し、RDS作成時に設定したユーザー名、パスワード、データベースエンドポイントを記述します。
credential.yml.enc(ローカル)# aws: # access_key_id: 123 # secret_access_key: 345 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx db: username: (username) password: (password) host: (database endpoint)終わったら、:wqでエディタを終えます。
次に本番環境において、データベースの設定が適用されるようconfig/database.ymlを編集します。database.ymlの最後の方にproductionの項があります。そこを下記のように修正します。
database.yml(ローカル)production: <<: *default database: sample611_production username: <%= Rails.application.credentials.db[:username] %> password: <%= Rails.application.credentials.db[:password] %> host: <%= Rails.application.credentials.db[:host] %> port: 5432Rails.application.credentials.dbとすることでcredentials.yml.encのdbの項を読みにいきます。
portはデータベースで使用するポートを指定します。一般的にはPostgreSQLの場合は 5432、MySQLの場合は 3306 になります。ここで、改修したコードをいったんコミットし、githubへプッシュしましょう。
ローカル$ git add . $ git commit -m "Setup database" $ git push origin masterデプロイ設定
デプロイツールCapistranoを使ったデプロイの設定を行います。
11. Capistranoのインストール
Capistranoをインストールします。下記のようにGemfileのdevelopmentグループにcapistrano, capistrano-rails, capistrano-rbenvを追加し、bundle installします。
Gemfile(ローカル)group :development do gem "capistrano", "~> 3.14", require: false gem "capistrano-rails", "~> 1.6", require: false gem 'capistrano-rbenv', '~> 2.2' endローカル$ bundle install $ bundle exec cap install12. Capfileの設定
下記のようにCapfileに追記します。
Capfile(ローカル): # require "capistrano/rails/migrations" # require "capistrano/passenger" require 'capistrano/rails' require 'capistrano/rbenv' :13. デプロイの設定
Capistranoのデプロイ設定を行います。全体的なデプロイの設定を「config/deploy.rb」、本番環境用の設定を「config/deploy/production.rb」に記述します。
config/deploy.rb(ローカル)lock "~> 3.15.0" set :application, "sample611" # 自分のリポジトリを設定 set :repo_url, "git@github.com:yukeippi/sample611.git" # rbenvの設定 set :rbenv_type, :user set :rbenv_ruby, '2.7.2' # Railsアプリの設置先 set :deploy_to, "/var/rails/sample611" # 共有ファイルの設定 append :linked_files, "config/master.key" # 共有ディレクトリの設定 append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system" # リリース保存数 set :keep_releases, 5linked_filesはデフォルトではconfig/database.ymlとなっていますが、config/master.keyに変更してください。
次にconfig/deploy/production.rbを編集します。下記の (server ip) にはデプロイ先EC2のIPをセットしてください。
githubへのキー登録については下記をご参照ください。config/deploy/production.rb(ローカル)set :rails_env, 'production' set :branch, 'master' # githubへの設定 set :ssh_options, { auth_methods: [ 'publickey' ], keys: [ '~/.ssh/aws.pem' ], } server '(server IP)', user: 'ec2-user', roles: %w{app db web}区切りが良いので、ここでいったんコミット、プッシュしましょう。(プッシュは任意です)
ローカル$ git add . $ git commit -m "Setup capistrano" $ git push oririn master14. Githubへのキー登録
デプロイ時にデプロイ先サーバーがgithubからソースコードを取得するための設定を行います。
この設定は下記の記事をご参照ください。
15. デプロイ先ディレクトリの作成
Railsアプリのプログラムの設置(デプロイ)を行います。アプリケーションの設置場所を「/var/rails」、実行者を「ec2-user」とした場合、下記のコマンドを実行し、ディレクトリを作ります。
サーバー$ cd /var/ $ sudo mkdir rails $ sudo chown ec2-user:ec2-user railsここで「cap deploy:check」を実行しますが、下記のように失敗します。この処理の目的は、Capistranoにデプロイに必要なディレクトリを作らせることです。
ローカル$ bundle exec cap production deploy:check 00:02 deploy:check:make_linked_dirs 01 mkdir -p /var/rails/sample611/shared/config ✔ 01 ec2-user@(server IP) 0.078s 00:02 deploy:check:linked_files ERROR linked file /var/rails/sample611/shared/config/master.key does not exist on (server IP)エラーメッセージにあるように「master.key」がないことが原因です。サーバーに入って下記のようにmaster.keyを作成してください。
サーバー$ cd /var/rails/sample611/shared/config/ $ vim master.key (ローカルにあるmaster.keyを貼り付け)これで問題は解決しましたので、再度、デプロイチェックを行います。
ローカル$ bundle exec cap production deploy:check成功するはずです。これでデプロイ先ディレクトリができました。
16. Databaseのcreate
しかし、まだこの時点でもデプロイ途中で失敗します。それはデータベースがcreateされていないからです。deployでデータベースのmigrateは実行しますが、createしていないため、migrateできずに失敗してしまいます。
そこで、失敗前提でデプロイを実行し、そのときにデプロイされたソースコードを使ってcreateします。
ローカル$ bundle exec cap production deploy最初はgemをインストールするため、時間がかかります。
しばらく待つとデプロイは終わり、途中で失敗していることが確認できます。その後、本番サーバーに入り、下記のようにデプロイされたソースコードのルートに移動し、データベースを作成します。サーバー$ cd /var/rails/sample611/releases/(最新リリース)/ $ bundle exec rails db:create RAILS_ENV=production(最新リリース)は「20210216104333」といったディレクトリ作成日時を意味するディレクトリを指します。ソースコードがデプロイされると、Capistranoがこのようなディレクトリを作成し、そこにプログラムを保存します。
これでデータベースが作成されたら、デプロイが可能になります。
再度デプロイします。ローカル$ bundle exec cap production deployこれで成功するはずです。
WebサーバーとRailsアプリの連携
17. Pumaの設定
本番環境のPumaの設定を行います。
config/puma/production.rb(ローカル)# Puma can serve each request in a thread from an internal thread pool. # The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } threads min_threads_count, max_threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # port ENV.fetch("PORT") { 3000 } # Specifies the `environment` that Puma will run in. # environment ENV.fetch("RAILS_ENV") { "production" } # Specifies the `pidfile` that Puma will use. # pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } # Specifies the number of `workers` to boot in clustered mode. # Workers are forked web server processes. If using threads and workers together # the concurrency of the application would be max `threads` * `workers`. # Workers do not work on JRuby or Windows (both of which do not support # processes). # # workers ENV.fetch("WEB_CONCURRENCY") { 2 } # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code # before forking the application. This takes advantage of Copy On Write # process behavior so workers use less memory. # # preload_app! # Allow puma to be restarted by `rails restart` command. plugin :tmp_restart bind "unix:///var/rails/sample611/shared/tmp/sockets/puma.sock"RAILS_ENVをproductionに指定しているところと、最下行でsocketを指定しているのがポイントです。
ここでいったんコミット、プッシュしてください。
ローカル$ git add . $ git commit -m "Setup puma for production" $ git push origin master18. Nginxのインストール
ここからはサーバーでの作業になります。
サーバーにNginxをインストールします。サーバー$ sudo amazon-linux-extras install nginx1 -y19. NginxとPumaの紐付け
次にNginxとPumaを紐付けるための設定ファイルを作成します。ポイントはuptreamの設定、rootの指定、proxy_passの指定です。
サーバー$ sudo vim /etc/nginx/conf.d/sample611.conf/etc/nginx/conf.d/sample611.conf(サーバー)upstream puma { server unix:///var/rails/sample611/shared/tmp/sockets/puma.sock fail_timeout=0; } server { listen 80; server_name example.com; root /var/rails/sample611/current/public; try_files $uri/index.html $uri @railsapp; location @railsapp { proxy_pass http://puma; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; } error_page 500 502 503 504 /500.html; client_max_body_size 4G; keepalive_timeout 10; }upstream puma内にあるpuma.sockのパスに気をつけてください。これは15のbindで設定したパスにあわせなければなりません。
20. Pumaのサービス設定
次にpumaをsystemctlで起動や再起動ができるように設定を行います。
サーバー$ sudo vim /etc/systemd/system/puma.service/etc/systemd/system/puma.service(サーバー)[Unit] Description=Puma HTTP Server After=network.target [Service] Type=simple Environment="RAILS_ENV=production" WorkingDirectory=/var/rails/sample611/current ExecStart=/home/ec2-user/.rbenv/shims/bundle exec /var/rails/sample611/shared/bundle/ruby/2.7.0/bin/puma -C /var/rails/sample611/current/config/puma/production.rb Restart=always [Install] WantedBy=multi-user.targetここでpumaの実行パスや実行時の設定ファイルを指定しています。
あと、このままでは実行権限がないため、下記のコマンドでpuma.serviceに実行権限を付与してください。サーバー$ sudo chmod +x /etc/systemd/system/puma.serviceこれでサーバー側の設定は終わりです。
21. Puma, Nginxの起動
いよいよウェブサーバー、アプリケーションサーバーの起動です。まず、Pumaを起動します。
サーバー$ sudo systemctl daemon-reload $ sudo systemctl start puma下記のコマンドで起動を確認します。
サーバー$ sudo systemctl status puma ● puma.service - Puma HTTP Server for sample611 (production_mysql) Loaded: loaded (/etc/systemd/system/puma.service; enabled; vendor preset: disabled) Active: active (running) since 土 2021-02-20 15:47:30 JST; 27min ago Main PID: 2876 (bundle) CGroup: /system.slice/puma.service └─2876 puma 5.1.1 (tcp://0.0.0.0:3000,unix:///var/rails/sample611/shared/tmp/sockets/puma.sock) [20210216072308]上記のようになければ「Active: active」になっていれば、起動成功です。
もし「Active: inactive」になっていれば、何かしらの設定に間違いがある可能性があります。
journalctl -xe等を使って原因を調べてください。次にnginxの起動します。
サーバー$ sudo systemctl start nginx.serviceこれでブラウザを使って下記のようにサイトにアクセスすれば画面に表示されるはずです。
http://(サイトのIP)/
21. デプロイ時のリスタート設定
プログラムをデプロイしたときはアプリケーションサーバーをリスタートする必要があります。capistranoのrestartタスクを追加しておくと、それも自動で実行してくれます。
config/deploy.rb(ローカル)namespace :deploy do task :restart do on roles(:web), in: :sequence, wait: 5 do within release_path do execute "sudo systemctl daemon-reload" execute "sudo systemctl restart puma" end end end after :finishing, :restart endこれでローカル側のコード改修が終わりです。
最後にここまでをコミットしましょう。ローカル$ git add . $ git commit -m "Setup restart puma"再起動することを確認するため、デプロイします。
ローカル$ bundle exec cap production deploy : 00:34 deploy:cleanup Keeping 5 of 6 deployed releases on (server IP) 01 rm -rf /var/rails/sample611/releases/20210314010936 ✔ 01 ec2-user@(server IP) 0.797s 00:35 deploy:restart 01 sudo systemctl daemon-reload ✔ 01 ec2-user@(server IP) 0.199s 02 sudo systemctl restart puma ✔ 02 ec2-user@(server IP) 0.094s 00:35 deploy:log_revision 01 echo "Branch master (at 0e3b185044e3a56f20a3bf37d7c6ce31f5a92b54) deployed as release 20210314015609 b… ✔ 01 ec2-user@(server IP) 0.119sデプロイログからrestartが実行されていることがわかると思います。
これでデプロイは成功です!お疲れ様でした。puma, nginxの自動起動設定
最後にpuma,nginxの自動起動設定を行っておくとよいでしょう。これを行うことでサーバーを再起動しても自動でWebアプリケーションを起動してくれます。
サーバー$ sudo systemctl enable puma.service $ sudo systemctl enable nginx.service以上です。
- 投稿日:2021-03-14T15:22:51+09:00
プログラミング初学者がLINE-botを作ってみた
概要
私事ですが、全く天気予報を見ないです。
そのため朝に雨が降っていなければ傘を持っていかず、毎回コンビニで傘を買ったり、雨に打たれながら帰宅することが多々あります。
そこで!傘代の節約、これ以上家に傘を増やさないためにも、自分用に朝7時に傘が必要か通知してくれて、こちらが「明日・明後日・今日・などなど」と入力すればその日に雨が降るかを返してくれるLINE Botを実装してみました。手順
・事前にRailsの環境を整える
新規Railsアプリケーションの作成「rails new」コマンドを実行$ rails new アプリケーション名作成されたディレクトリ(フォルダ)に移動
$ cd 作成されたディレクトリ名Gemfileの一番下に以下のコードを追加
gem ' dotenv-rails ' gem 'line-bot-api' #dotenv-railsは環境変数を扱うために使用するgem以下のコマンドでGemをインストール
$bundle installUserモデルを作成
$ rails generate model モデル名 カラム名:データ型上記のコマンドで新しくマイグレーションファイルも作成される。このマイグレーションファイルを以下のように編集
class CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| t.string :line_id, null: false # この行を修正 t.timestamps end end end以下のコマンドでデータベース・テーブルを作成
$ bundle exec rails db:create $ bundle exec rails db:migrate新規作成されたファイルを編集
lib/tasks/scheduler.raketask :update_feed => :environment do require 'line/bot' # gem 'line-bot-api' require 'open-uri' require 'kconv' require 'rexml/document' #line-bot側の設定 client ||= Line::Bot::Client.new { |config| config.channel_secret = ENV["LINE_CHANNEL_SECRET"] config.channel_token = ENV["LINE_CHANNEL_TOKEN"] } # 使用したいxmlデータURLを入力 # xmlデータをパース # パスの共通部分を変数化 # 6時〜12時の降水確率 # メッセージを発信する降水確率の下限値の設定 min_per = 20 if per06to12.to_i >= min_per || per12to18.to_i >= min_per || per18to24.to_i >= min_per word1 = ["テキスト入力",].sample word2 = ["テキスト入力"].sample # 降水確率によってメッセージを変更する閾値の設定。 #ifの条件式の中で降水確率によって送信されるメッセージを変更 mid_per = 50 if per06to12.to_i >= mid_per || per12to18.to_i >= mid_per || per18to24.to_i >= mid_per word3 = "テキスト入力" else word3 = "テキスト入力!" end # 発信するメッセージの設定 push = "#{word1}\n#{word3}\n降水確率\n 6〜12時 #{per06to12}%\n 12〜18時 #{per12to18}%\n 18〜24時 #{per18to24}%\n#{word2}" # メッセージの発信先idを配列で渡す必要があるため、userテーブルよりpluck関数を使ってidを配列で取得。 #multicastsメソッドは、今回利用しているgem「line-bot-api」で定義されており、このメソッドを呼び出している user_ids = User.all.pluck(:line_id) message = { type: 'text', text: push } response = client.multicast(user_ids, message) end "OK" end以下のコマンドでコントローラの作成
$ bundle exec rails g controller linebot作成されたコントローラーの編集
app/controller/linebot_controller.rbclass LinebotController < ApplicationController require 'line/bot' # gem 'line-bot-api' require 'open-uri' require 'kconv' require 'rexml/document' def callback body = request.body.read signature = request.env['HTTP_X_LINE_SIGNATURE'] unless client.validate_signature(body, signature) return head :bad_request end events = client.parse_events_from(body) events.each { |event| case event # メッセージが送信された場合の対応を入力 # ユーザーからテキスト形式のメッセージが送られて来た場合の対応を入力 when /.*(明日|あした).*/ # info[2]:明日の天気 per06to12 = doc.elements[xpath + 'info[2]/rainfallchance/period[2]'].text per12to18 = doc.elements[xpath + 'info[2]/rainfallchance/period[3]'].text per18to24 = doc.elements[xpath + 'info[2]/rainfallchance/period[4]'].text if per06to12.to_i >= min_per || per12to18.to_i >= min_per || per18to24.to_i >= min_per push = "テキスト\nテキスト\n降水確率\n 6〜12時 #{per06to12}%\n 12〜18時 #{per12to18}%\n 18〜24時 #{per18to24}%\nテキスト" else push = "テキスト" end # テキスト以外(画像等)のメッセージが送られた場合 else push = "テキスト" end message = { type: 'text', text: push } client.reply_message(event['replyToken'], message) # LINEお友達追された場合 when Line::Bot::Event::Follow # 登録したユーザーのidをユーザーテーブルに格納 line_id = event['source']['userId'] User.create(line_id: line_id) # LINEお友達解除された場合 when Line::Bot::Event::Unfollow # お友達解除したユーザーのデータをユーザーテーブルから削除 line_id = event['source']['userId'] User.find_by(line_id: line_id).destroy end } head :ok end private def client @client ||= Line::Bot::Client.new { |config| config.channel_secret = ENV["LINE_CHANNEL_SECRET"] config.channel_token = ENV["LINE_CHANNEL_TOKEN"] } end end記号の意味
・「.」:何か1文字(「あ」や「a」など1文字なら何でもヒットします)
・「*」:直前の文字が0回以上繰り返す場合にマッチ
ルーティングの設定
メッセージが来た時、友達追加された時、解除がされた時に、linebotコントローラのcallbackアクションが呼ばれるようにしています。config/routes.rbRails.application.routes.draw do post '/callback' => 'linebot#callback' endこの後、お好きなレンタルサーバー、共有サーバーでデプロイ
(私はHerokuでデプロイしました)
こちらの
LINE Botアカウント作成・設定
をして完成!!
- 投稿日:2021-03-14T14:49:32+09:00
テストコード
テストコードについて
テストコードとはアプリの挙動を確認するためのコードである。
テストコードを行うことで以下のような利点がある。1.アプリの内容が理解できる
これはコードを書くときは必ず確認したい事項があるのでそれを記述できるということは、アプリの内容を理解しているということになる。
2.クオリティが担保できる
ブラウザでひとつひとつ確認していると抜け漏れが発生し、全て確認できない可能性がある。また仕様が変更されるたびにブラウザで確認をしなければならなくなる。これらを防ぐことができる
テストコードの種類
単体テストコード
モデルやコントローラーなどのひとつひとつの機能を確認する。例えばバリデーションが働くかなど。
結合テストコード
ユーザーが行う挙動の流れを再現して問題ないか確認する。
テストコードのパターン
テストコードにはうまくいく時とうまくいかない時を記述する。
前者を正常系、後者を異常系という。テストコードを書くための準備
Rspecの導入
Gemfileのgroup developement, ;test do~end の間にgem 'rspec-rails,' '~> 4.0.0'
と記述する。
記述できたらbundle install
アプリでRspecを使えるようにする
rails g rspec:install
いくつかファイルが形成される。
そのなかに.rspecというファイルがあるのでそこに-format documentation
と記述する。
そうすることでターミナルで実行結果が見れるようになる。
テストコードを書くためのファイル
Userモデルだったらターミナルrails g rspec:model user最後にファイルを開いて
require 'rails_helper'
という記述が一番上にあることを確認する。
これはRspecでrailsの機能のテストを行うときは、共通の設定やメソッドを適用するためにspec/rails_helper.rbというファイルを読みこむ必要がある。
これは生成時にはすでに記述されている。以上です。
- 投稿日:2021-03-14T13:44:16+09:00
Ruby on Rails ディレクトリ構造
【Ruby on Rails ディレクトリ構造】
Ruby on RailsでのWebアプリケーション開発をする時に
ディレクトリ構造の理解が必要なため備忘録としてまとめました。Webアプリケーションを開発する時は、app、config、dbをよく使います。
それぞれのディレクトリ構造の理解をしよう。
app:MVCのアプリケーションに関するものを管理
bin:Webアプリケーション起動時に使用するスクリプトファイルを管理
config:ルーティングやDBの設定ファイルを管理
db:DB関連のマイグレーションスクリプトやシートデータを管理
lib:全体で共有する必要のあるライブラリを管理
log:Webアプリケーション実行時のログファイルを管理
public:静的なファイルやページなどのリソースを管理
test:テスト用のコードファイルやデータなどを管理
tmp:一時的な情報を管理
vendor:第三者によって開発されたコードを配置するWebアプリケーション直下に生成されるファイル
.gitignore:gitのバージョン管理対象から外すファイル
.ruby-version:Rubyのバージョンを管理するファイル
Gemfile:Railsで使用するGemパッケージの設定ファイル
Gemfile.lock:Gemパッケージの依存関係を管理するファイル
README.md:起動実行手順の説明用のファイル
Rakefile:Rakeコマンドの実行を管理するファイル
config.ru:Rackがrailsサーバー起動に使用する設定ファイル
package.json:npmを使用する場合に必要なファイル
- 投稿日:2021-03-14T12:54:19+09:00
ポートフォリオのAWSネットワーク構成を整理した【Rails + Nginx + Unicorn + MySQL】
Railsアプリポートフォリオのネットワーク構成を頭の整理と備忘を兼ねて整理してみました。
俯瞰的に整理するものなので各リソースやネットワークの細かい設定等には触れません。
初学者なりに自分の言葉で整理したものになっていますので、おかいし点などありましたらご指摘いただけると幸いです。はじめに:参考書籍
AWSのネットワーク構築において以下の本を参考にさせていただきました。
初学者でも大変分かりやすく、かつ内容に従って操作することで本格的でセキュアなネットワークを構築できるハンズオン形式にもなっており、これ無くしてこのネットワーク環境は作れなかったと思います。
まだ発刊されて日も浅い(2021年3月時点)ため、ネット上の記事や古い書籍でありがちな、解説のUIと現在のUIが違って迷う、ということもほぼ無かったのも良かったです。おすすめです!
ネットワーク構成図
開発環境・デプロイ
開発・テストはDockerリモートコンテナ上で実施
ソースコードはGitHubで管理、デプロイはCapistranoで行うIAM
IAM(Identity and Access Management)はAWSへのアクセスを安全に管理するための仕組み
AWSアカウントのデフォルトユーザであるルートユーザは権限が強く、通常の開発等で使うにはリスクが大きい
そのため別途権限を抑えたユーザを作成し、こちらでネットワークの構築を行う
また仮想デバイスを用いたMFA(多要素認証)を導入し、更にセキュアな接続を実現するVPC
ここは私の領域ですよ、と宣言する領域。Virtual Private Cloud
この中にAWSのネットワークを構築していくアベイラビリティゾーン
耐障害性を上げるために、同じ東京リージョンの中でも異なるアベイラビリティゾーンにそれぞれサブネットを構成する
サブネット
VPCのIPアドレスの範囲を分割する範囲。分割する理由は主に以下2点
- 役割の分離:外部に公開するリソースかどうかを区別するため
- 機器の分離:AWS内での物理的な冗長化を行うため
今回は、2つのアベイラビリティゾーンにそれぞれパブリック・プライベートサブネットを用意する
インターネットゲートウェイ
VPCで作成したネットワークとインターネット間の通信を可能にするためのもの
NATゲートウェイ
インターネットゲートウェイを使った通信ではVPCのリソースは外部のネットワークと直接通信を行うため、VPCリソースはパブリックIPを持っている必要がある
しかしパブリックIPを持つ=インターネットに直接公開されている状態になり、サブネットをパブリックとプライベートに分けた意味が無くなる
プライベートサブネットのリソースは、インターネットに出ていく必要はあってもインターネットから直接アクセスはされたくない
→このような要求を実現するための仕組みがNAT(ネットワークアドレス変換)AWSではこの役割を果たすのがNATゲートウェイになる
ルートテーブル
サブネット同士、あるいはサブネットと各ゲートとの間には、通信をするための経路がまだ無い
そのため、サブネットが別のサブネット内のリソースを見たり、サブネット外のリソースにアクセスができない
サブネット間の通信経路を設定するために、AWSにはルートテーブルという機能がある
今回はサブネットが4つある すべてのサブネットにルートテーブルを作成する必要がある(ただし複数のサブネットで同じルートテーブルを共有することは可能これを踏まえ今回作成するのは以下3つ
- パブリックルートテーブル:パブリックサブネット1,2共有
- 外部とはIGW経由、プライベートサブネットとはLocalターゲットとしてアクセス
- プライベートルートテーブル1:プライベートサブネット1用
- プライベートルートテーブル2:プライベートサブネット2用
- 外部とはNATGW経由、パブリックサブネットとはLocalターゲットとしてアクセス
セキュリティグループ
今のままだとインターネットを通じてどんなアクセスもできてしまう
VPC内のリソースを守るため、外部からのアクセス制限をつける必要がある
→セキュリティグループという機能を用いて実現するセキュリティグループでは、外部からのアクセスを次の2つの概念で制御できる
- ポート番号
- サービスのアクセスで使われる80番(HTTP)、443番(HTTPS)、
- 管理者としてサーバ接続時に使われる22番(SSHなど)
- IPアドレス
今回は以下2つのセキュリティグループが必要
- 全てのリソースに接続するための入り口となる「踏み台サーバ」
- インバウンドルール:SSH接続(22番)のみ
- リクエストや処理を分散する「ロードバランサー」
- インバウンドルール:HTTP(80番)とHTTPS(443番)接続のみ
EC2(踏み台サーバ)
今回は機器の冗長化、負荷分散のために2つのアベイラビリティゾーンにサブネットを構築した
しかしリソースを増やすということはそれだけ侵入の経路が増えセキュリティ上のリスクが高まることになる
そこで全てのリソースに接続するための入り口となる踏み台(bastion)サーバを用意し、
このサーバ経由でないと各リソースに接続できないようにする踏み台サーバは目的のリソースへの通り道となる以外の用途がないため、低スペックでもOK
EC2インスタンス上に構築する
踏み台サーバはパブリックサブネット1に設置し、先程作成した踏み台サーバ用のセキュリティグループを適用する(=SSH接続のみ許可する。秘密鍵はローカルマシンで保持)EC2(Webサーバ・アプリケーションサーバ)
実際のアプリケーションを稼働させるEC2インスタンス
この中にWebサーバとしての役割を果たすNginx、Rackアプリケーション用サーバとしての役割を果たすUnicorn、アプリ本体であるRailsをインストールする
プライベートサブネット1,2にそれぞれ設置こちらもssh認証の秘密鍵はローカルで持ち、ssh-agentを利用してセキュアに多段接続する(踏み台サーバには秘密鍵をアップしない)
ここで踏み台サーバとWebサーバの違いを整理する
項目 踏み台 Webサーバ 設置サブネット パブリック プライベート パブリックIP 必要 不要 セキュリティグループ デフォルト+SSH接続 デフォルト+HTTP, HTTPS接続 誰がいつ接続する? 管理者が必要に応じて Webサービスのユーザが常時 接続形態 直接接続 間接接続(ロードバランサ経由) ロードバランサー
ユーザが増えてくると1台のWebサーバではリクエストを捌き切れなくなる可能性がある
スケールアウトの方法として用いられるのがロードバランサーで、リクエストの分散を実現できる設定項目は以下
- アベイラビリティゾーン:インターネットゲートウェイへの経路があるサブネット(今回はパブリックサブネット1,2)があるゾーンを指定する必要がある
- セキュリティグループ:デフォルト+HTTP, HTTPS接続(内部向けと外部向け)
- ターゲットグループ:Webサーバ1, 2、プロトコルはHTTP
データベース
AWSのサービスの一つであるRDSを用いて実現
RDSは以下の4点で構成される
データベースエンジン
→ 今回はMySQLを使用)パラメータグループ
→ 主にデータベースエンジン固有の設定を行うためのものオプショングループ
→ 主にRDS固有の設定を行うためのものサブネットグループ
→ データベースサーバーを複数のアベイラビリティーゾーンに分散させて配置させる時に使われる設定
RDSはサブネットグループを指定することで自動的に複数のアベイラビリティーゾーンにデータベースを作成し、
耐障害性を上げてくれる(マルチAZ)S3
今回のアプリではユーザのアイコンや投稿機能で画像をアップロードすることが可能
その画像を保管する場所として、AWSのストレージサービスであるS3を利用する
S3はVPCの外に配置するものである
そのためIAMでS3にアクセスするためのロールを作成し、EC2に適用することでアプリからS3へのアクセスを実現するAmazon Route 53
Webアプリを公開するためには分かりやすくて覚えやすいドメインが必要
AWSではドメインを取得することができるRoute 53というサービスがある
こちらで希望のドメインを取得することで公開用のURLを発行することができる
(費用は$10~15/年 前後)また、Route 53では他に以下の設定を行う
SSLサーバー証明書の発行(Certificate Manager)
→ ドメインの正しさを保証する証明書の発行を行う。
今回はドメイン検証済み(DV)証明書を使う
発行した証明書を使い、HTTPSのリスナーをロードバランサーに追加するパブリックDNSの設定
→ 踏み台サーバとロードバランサーに対し設定するプライベートDNSの設定
→ 踏み台サーバ、Webサーバ1,2、DBサーバに対し設定するAmazon SES
SES(Simple Email Service)はメールの送受信行う機能を提供するAWSのサービス
今回のアプリではパスワードを忘れた際に再発行するためにメールの送信を行うため用意する
受信に関する設定は不要のため行わない通常のメール送受信においては、以下プロトコルが利用される
SMTP(Simple Mail Transfer Protocol)
→ 送信用のプロトコルPOP3(Post Office Protocol Version 3)
→ 受信用のプロトコル。メールは受信者のローカルに取り込まれるIMAP4(Internet Message Access Protocol Version 4)
→ 受信用のプロトコル。メールはサーバ上に保管されるためインターネットアクセスが必要アプリからメールを送るには、メール送信用のIAMユーザを作成し、Amazon SES API または Amazon SES SMTPインターフェイスで認証する必要がある
今回は通常のメールサーバと同じ方法で実装ができるAmazon SES SMTPインターフェイスを利用しているCloudWatch
AWSで提供されている、各種サーバ等の運用状況を監視するツール
今回はWebサーバ・アプリサーバを包含するEC2インスタンスの死活監視、CPU使用率監視を行っているおわりに
CircleCI/CDを使った自動テスト・自動デプロイも導入したいと考えています。
導入できたらこちらも更新しようと思います。
- 投稿日:2021-03-14T12:29:50+09:00
【Ruby on Rails6】gem "devise" のviewsのpasswordsだけが編集できない。
【Rails6】gem "devise" のviewsのpasswordsだけが編集できない。
↑記事で解決できました。
しかし、私の場合passwordsのviewsだけhtml.erb編集が反映されなかったので事象を記録します。環境 Ruby 2.6.6p146/Rails 6.0.3
通常通りdeviseを導入後、viewを編集。なぜかpasswordsだけ編集できずググって解決。
結論
config/initializers/devise.rb
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 = falseのコメントアウト箇所…
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
config.scoped_views = true
にしてください。
そのままのコメントアウトだとconfig.scoped_views = false
なので注意です。その後ターミナルで
rails s
でサーバー再起動も忘れずに。
※僕は再起動しないと反映されませんでした。これにて解決。
ここからが??な事象です。もし、謎を知っている方がいたら教えていただきたいです。
反映されない該当箇所をブラウザリロードをすると、ターミナル表示が
Started GET "/users/password/new" for ::1 at 2021-03-14 11:49:26 +0900 Processing by Devise::PasswordsController#new as HTML Rendering /Users/sxkx/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/devise-i18n-1.9.2/app/views/devise/passwords/new.html.erb within layouts/application Rendered /Users/sxkx/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/devise-i18n-1.9.2/app/views/devise/shared/_error_messages.html.erb (Duration: 0.1ms | Allocations: 15) Rendered /Users/sxkx/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/devise-i18n-1.9.2/app/views/devise/shared/_links.html.erb (Duration: 0.4ms | Allocations: 246) Rendered /Users/sxkx/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/devise-i18n-1.9.2/app/views/devise/passwords/new.html.erb within layouts/application (Duration: 2.5ms | Allocations: 863) [Webpacker] Everything's up-to-date. Nothing to do Rendered layouts/_header.html.erb (Duration: 0.3ms | Allocations: 119) Rendered layouts/_sidemenu.html.erb (Duration: 0.2ms | Allocations: 135) [Webpacker] Everything's up-to-date. Nothing to do Completed 200 OK in 28ms (Views: 26.4ms | ActiveRecord: 0.0ms | Allocations: 11089)
Rendering /Users/sxkx/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/devise-i18n-1.9.2/app/views/devise/passwords/new.html.erb within layouts/application
??なんだか長い…。 devise-i18n…. 日本語化のために入れたgemが悪さをしている??devise.rbconfig.scoped_views = trueをすると
Started GET "/users/password/new" for ::1 at 2021-03-14 11:54:00 +0900 Processing by Devise::PasswordsController#new as HTML Rendering users/passwords/new.html.erb within layouts/application Rendered users/shared/_error_messages.html.erb (Duration: 0.1ms | Allocations: 15) Rendered users/shared/_links.html.erb (Duration: 0.9ms | Allocations: 225) Rendered users/passwords/new.html.erb within layouts/application (Duration: 4.7ms | Allocations: 1058) [Webpacker] Everything's up-to-date. Nothing to do Rendered layouts/_header.html.erb (Duration: 0.5ms | Allocations: 119) Rendered layouts/_sidemenu.html.erb (Duration: 0.3ms | Allocations: 135) [Webpacker] Everything's up-to-date. Nothing to do Completed 200 OK in 41ms (Views: 37.7ms | ActiveRecord: 0.0ms | Allocations: 12092)
Rendering users/passwords/new.html.erb within layouts/application
うん。なんかちゃんと狙いのファイルを拾ってくれていそう。スコープを指定したから、しっかりできた感じなんですね。
ただなぜpasswordsだけがそうなったのかは謎です。以上。
- 投稿日:2021-03-14T12:28:26+09:00
複数タグ投稿機能の実装(formオブジェクト使用)
投稿に複数のタグを紐付ける
現在作成中のポートフォリオの中で複数のタグをつけることができる機能をつけたかったのでそれを実践した際のコードの記述についてまとめておこうと思います。またポートフォリオ完成した際には別記事で解説したいと思います。
それでは実際の実装を書いていきます。。conntrollerの記述
def new @room = RoomsTag.new end def create @room = RoomsTag.new(room_params) tag_list = params[:room][:name].split(",") if @room.valid? @room.save(tag_list) redirect_to root_path else render "new" end end def edit @form = RoomsTag.new(room: @room) end def update @form = RoomsTag.new(room_params, room: @room) tag_list = params[:room][:name].split(",") if @form.valid? @form.save(tag_list) redirect_to room_path(@room) else render :edit end end private def room_params params.require(:room).permit(:image, :title, :content, :place_id, :floor_id, :style_id, :name).merge(user_id: current_user.id) endformオブジェクトの記述
class RoomsTag include ActiveModel::Model attr_accessor :image, :title, :content, :place_id, :floor_id, :style_id, :name, :user_id # バリデーションをカットしています。 delegate :persisted?, to: :room def initialize(attributes = nil, room: Room.new) @room = room attributes ||= default_attributes super(attributes) end def save(tag_list) ActiveRecord::Base.transaction do @room.update(image: image, title: title, content: content, place_id: place_id, floor_id: floor_id, style_id: style_id, user_id: user_id) @room.room_tag_relations.each do |tag| tag.delete end tag_list.each do |tag_name| tag = Tag.where(name: tag_name).first_or_initialize tag.save room_tag = RoomTagRelation.where(room_id: @room.id, tag_id: tag.id).first_or_initialize room_tag.update(room_id: @room.id, tag_id: tag.id) end end end def to_model room end private attr_reader :room def default_attributes { image: room.image, title: room.title, content: room.content, place_id: room.place_id, floor_id: room.floor_id, style_id: room.style_id, name: room.tags.pluck(:name).join(',') } end endまずデータベースに保存できるところまでを記述していきます。
controller内の記述について
tag_list = params[:room][:name].split(",")上記の記述はsplitメソッドを用いて , で区切って、配列に変換しています。
その後formオブジェクトへ送ります。formオブジェクト内の記述について
delegate :persisted?, to: :roomこちらの記述から新規作成か更新かを判別して、formのメソッドをPUTとPATCHで分けている。
def initialize(attributes = nil, room: Room.new) @room = room attributes ||= default_attributes super(attributes) endformオブジェクトパターンで編集機能を実装する際は、編集画面でそれぞれのデータを表示させておくには、今回私の実装で行くところのRoomsTagのインスタンスを生成して、それをformに持っていかなくてはいけません。上記のinitializeメソッドの定義によってnewアクションの時は中身は空で、editアクションの時は中身がある状態でformに持っていくことができます。
super(attributes)の記述がないとinitializeメソッドが機能しないようです。続いてtag_listを引数にとっているsaveメソッド内について。
ActiveRecord::Base.transaction do endこの間に囲まれた部分で何か処理の失敗が起きた時、do~end間の処理を全てなかったことにするものです。
@room.update(image: image, title: title, content: content, place_id: place_id, floor_id: floor_id, style_id: style_id, user_id: user_id) @room.room_tag_relations.each do |tag| tag.delete end@room.updateにてデータの更新をします。
その下の記述で、このroomに紐付く全てのタグを一旦削除しています。tag_list.each do |tags| tag = Tag.where(name: tags).first_or_initialize tag.save room_tag = RoomTagRelation.where(room_id: @room.id, tag_id: tag.id).first_or_initialize room_tag.update(room_id: @room.id, tag_id: tag.id) end配列で運ばれてきたtag_listをeachメソッドを使って、一つずつ取り出し、whereメソッドとfirst_or_initializeメソッドで今までに保存したことがないタグだけ保存しています。その後中間テーブルのものを更新しています。
ここまででsave(tag_list)内の記述は終了です。def to_model room endcreateかupdateか適切なコントローラーへのパスをしてあげる記述みたいです、、
最後、privateメソッド内の記述でこれで読み取り専用のメソッドが定義されるようです。以上で複数タグの投稿機能の実装を行いました。かなり手探りで色々な記事を参考にしながらのものだったので拙い表現となっているかと思います。ご了承ください。。
- 投稿日:2021-03-14T12:17:34+09:00
【クソサイト】世界一情報量が多いサイト【個人開発】
はじめに
情報量が多い画像、情報量が多い動画などはたくさんあるのに「情報量が多いサイト」というのは見つからなかったので作ってみました。
概要
為替
ニュース
名言
じゃんけん
投稿
情報量の多い画像
日付け
イカれた鳥
などの情報が載ってます。あと隠しページがありますので暇な人は探してみてください。そこそこむずいです。
工夫した点
APIを大量に使う
大量と言っても3つですが
NewsAPI...ニュース
exchangeratesapi...為替
名言教えるよ...名言を使いました。
色使い
原色、反対色、相性の悪い色
をあえて大量に使いまとまりをなくしました。画像
あえてリサイズをせずサイズをバラバラにしてみました。
動き
スライドショーやイカれた鳥を使い動きを持たせさらに混乱するようにしました。
まとめ
深夜テンションで決めたネタを続投しないよう法が良い
ツイッターやってます
https://twitter.com/yamada1531追記
皆さん、隠しページ見つけるの早いですね...。公開して3日くらいは絶対見つからないと思ってたのにもう1、2人...。
もうちょっと難しくしようかなーなんて思ってます。
- 投稿日:2021-03-14T12:16:33+09:00
【Heroku】Failed to install gems via Bundler.の解決方法
仮想環境構築中、いっそのこと新しいバージョンにできるものは
バージョンアップした結果、bundlerがHerokuに対応していないバージョンになったためだったと思います。解決策
bundlerを2.1.4に戻したら、デプロイできました。
# バージョン確認 $ bundler -v Bundler version 2.2.11 $ gem list bundler gem list bundler bundler (2.2.11) capistrano-bundler (2.0.1)capistranoは自動デプロイツールのようです。
下記、参考サイトを見ながらbundlerの入れ直しを行いました。
# bundler削除 $ gem uninstall bundler Successfully uninstalled bundler-2.2.11 # 2.1.4インストール $ install bundler -v 2.1.4 Successfully installed bundler-2.1.4 # バージョン確認 $ bundler -v Bundler version 2.1.4 # 再度bundle $ bundle installこれでgit pushしてから、
git push herokuで正常にデプロイできました。新しいバージョンだからいいってもんでは、
やっぱりないのですね(o'∀')ノ参考サイト
- 投稿日:2021-03-14T11:47:44+09:00
【Rails6】親モデルと一緒に複数の子レコードを一括で登録/編集するフォームの作り方
Ruby on Railsで複数のモデル、複数のレコードを1つのフォームで一括登録/編集したい、Javascriptはあまり書かずに...という機会があったので、その実装について今回まとめてみました。
完成イメージ
- レシピのレコードと材料のレコードを同時に作成/編集できること
- 材料レコードは一括で複数作成/編集できること
この記事を終えると、こんなアプリが出来るようになります。
前提
バージョン
- ruby 2.7.2
- rails 6.1
扱うモデル
- Recipe(レシピ) Model
- Ingredient(材料) Model
レシピは複数の材料を持っている
このような構成はすでに出来ている状態から始めます。具体的なmodelは以下を参考。
app/models/recipe.rb
# == Schema Information # # Table name: recipes # # id :integer not null, primary key # name :string not null # created_at :datetime not null # updated_at :datetime not null # class Recipe < ApplicationRecord has_many :ingredients, dependent: :destroy endapp/models/ingredient.rb
# == Schema Information # # Table name: ingredients # # id :integer not null, primary key # recipe_id :integer not null # name :string not null # created_at :datetime not null # updated_at :datetime not null # # Indexes # # index_ingredients_on_recipe_id (recipe_id) # # Foreign Keys # # recipe_id (recipe_id => recipes.id) # class Ingredient < ApplicationRecord belongs_to :recipe end手順
さてここから、完成を目指して一つずつ手順を記していきます
1. accepts_nested_attributes_forを追加
app/models/recipe.rbにaccepts_nested_attributes_forを追加します。
class Recipe < ApplicationRecord has_many :ingredients, dependent: :destroy # ↓追加 accepts_nested_attributes_for :ingredients, allow_destroy: true, reject_if: :all_blank end参考:ActiveRecord::NestedAttributes::ClassMethods
2. cocoonを追加
cocoonとは、複数レコードをインタラクティブに追加、編集、削除を簡単に行うことが出来るようにするGem, Packageです
Gemのcocoonをインストール
↓のようにGemfileにcocoonを追加して
bundle install
しますgem "cocoon"cocoonのpackage追加
yarn add @nathanvda/cocoonapp/javascripts/packs/application.js の編集
import Rails from "@rails/ujs" import Turbolinks from "turbolinks" import * as ActiveStorage from "@rails/activestorage" import "channels" // これを追加 import "@nathanvda/cocoon" Rails.start() Turbolinks.start() ActiveStorage.start()3. jqueryを使えるようにする
config/webpack/plugins/jquery.jsを作成
$ bundle exec rails webpack:install $ mkdir config/webpack/plugins/ $ touch config/webpack/plugins/jquery.jsconfig/webpack/plugins/jquery.jsに以下を記述
const { environment } = require('@rails/webpacker') const jquery = require('./plugins/jquery') environment.plugins.prepend('jquery', jquery) module.exports = environmentconfig/webpack/environment.js でjqueryを読み込む
const { environment } = require('@rails/webpacker') const jquery = require('./plugins/jquery') environment.plugins.prepend('jquery', jquery) module.exports = environment4. RecipeController作成とRoutesを設定
bundle exec rails g controller recipes
でController作成$ bundle exec rails g controller recipes Running via Spring preloader in process 38240 create app/controllers/recipes_controller.rb invoke erb create app/views/recipes invoke test_unit create test/controllers/recipes_controller_test.rb invoke helper create app/helpers/recipes_helper.rb invoke test_unit invoke assets invoke scss create app/assets/stylesheets/recipes.scssconfig/routes.rbにrecipeのpathを追加
Rails.application.routes.draw do root "recipes#index" resources :recipes, only: [:index, :new, :create] end5. Recipeの一覧ページを作成
app/controller/recipes_controller.rbにindexアクション追加
class RecipesController < ApplicationController def index @recipes = Recipe.all end endRecipeの一覧ページを作成
$ touch app/views/recipes/index.html.erb<h1>レシピ一覧</h1> <ul> <% @recipes.each do |recipe| %> <li><%= link_to recipe.name, edit_recipe_path(recipe) %></li> <ul> <% recipe.ingredients.each do |ingredient| %> <li><%= ingredient.name %></li> <% end %> </ul> <% end %> </ul> <%= link_to "レシピの新規作成", new_recipe_path %>するとこんなページが出来上がります
6. Recipeの新規作成ページを作成
app/controller/recipes_controller.rbにnewアクション追加
class RecipesController < ApplicationController ~~ 省略 ~~~ def new @recipe = Recipe.new end endapp/views/recipes/new.html.erb を作成
$ touch app/views/recipes/new.html.erb<h1>レシピの新規作成</h1> <%= render "recipes/form", model: @recipe %>app/views/recipes/_form.html.erbを作成
$ touch app/views/recipes/_form.html.erb<%= form_with model: model do |form| %> <div> <%= form.label :name, "レシピ名" %> <%= form.text_field :name %> </div> <div id="ingredients"> <%= link_to_add_association '材料の追加', form, :ingredients, data: { association_insertion_method: 'append' } %> <%= form.fields_for :ingredients do |ingredient| %>] <%# ファイル名はingredient_fieldsにしてください %> <%= render "recipes/ingredient_fields", f: ingredient %> <% end %> </div> <%= form.submit "作成する" %> <% end %>app/views/recipes/_ingredient_fields.html.erbを作成
$ touch app/views/recipes/_ingredient_fields.html.erb<div class="nested-fields"> <%= f.label :name, "材料名" %> <%= f.text_field :name %> <%= link_to_remove_association "削除", f %> </div>ここまで行うと、以下のような画面になります
7. レシピ、材料の作成(Create)アクションを作成
class RecipesController < ApplicationController def new @recipe = Recipe.new end # ここから def create @recipe = Recipe.new if @recipe.update(recipe_params) redirect_to root_path else render :new end end private def recipe_params # :idがないと毎回新しくレコードが作られてしまいます # :_destroyがないと削除ができないです params.require(:recipe).permit(:name, ingredients_attributes: [:id, :name, :_destroy]) end # ここまで追加 endcreateアクションの追加が完了すると、レシピと材料を一括で作成してDBに保存する処理まで出来るようになります!
8. レシピの編集ページを作成
app/views/recipes/edit.html.erbの作成
$ touch app/views/recipes/edit.html.erbapp/views/recipes/edit.html.erbの編集
<h1><%= @recipe.name %>の編集</h1> <%= render "recipes/form", model: @recipe %>app/controllers/recipes_controller.rbの編集
class RecipesController < ApplicationController ~~ 省略 ~~ def edit @recipe = Recipe.find(params[:id]) end ~~ 省略 ~~ end9. レシピのupdateアクションを作成
app/controllers/recipes_controller.rbにupdateアクションを追加
class RecipesController < ApplicationController ~~ 省略 ~~ def update @recipe = Recipe.find(params[:id]) if @recipe.update(recipe_params) redirect_to root_path else render :edit end end ~~ 省略 ~~ endこれで一括の更新もできるようになりました!
終わりに
長かったですが、これで以上になります。
あまりJavaScriptを書かずに動的にフォームの追加や削除をしたいということがあったので、今回のような実装について書きました。同じようなことがあればぜひご参考ください!
- 投稿日:2021-03-14T11:00:11+09:00
[WIP]Rails6.1ハンズオン(4)~6.0の機能を触る
はじめに
※この記事は執筆途中です。5-1までで力尽きたので5-2以降はまた今度書きます...
Rails6.1ハンズオン(3)の続きです。
6.1ハンズオンと言いながら、実は6.0をまともに触っていない筆者。。。
ですので今回は6.0で追加された機能を触ってみます。
コードはGithubにあげています。章ごとにコミットしてますので、参考にしていただければ幸いです。やること
個人的に気になった(面白そうor仕事で使いそう)機能をピックアップして、現在のRails_6.1_hands_onプロジェクトに追加してみます。
- Action Text
- Action Mailbox
- ActiveRecord::Relation#pick
参考:Ruby on Rails 6の主要な新機能・機能追加・変更点 - Qiita
5-1. Action Text
5-1-1. Action Text, Active Storageの導入
まずはActiveStorageが必要なので、
rails active_storage:installをしたが、
rails aborted! Don't know how to build task 'active_storage:install' (See the list of available tasks with `rails --tasks`)と出た。ActiveStorageのREADMEをみると、
Run bin/rails active_storage:install to copy over active_storage migrations.
NOTE: If the task cannot be found, verify that require "active_storage/engine" is present in config/application.rb.らしいので、config/application.rbをみると、
require "rails" # Pick the frameworks you want: require "active_model/railtie" require "active_job/railtie" require "active_record/railtie" # require "active_storage/engine" require "action_controller/railtie" # require "action_mailer/railtie" # require "action_mailbox/engine" # require "action_text/engine" require "action_view/railtie" require "action_cable/engine" require "sprockets/railtie"となっていた。rails new で色々無効にしていたのでコメントアウトされていたらしい。
action_text/engine
も無効化されているのでこちらも有効にしておく。そしてやっと、
rails active_storage:install rails action_text:installmigrationファイルができているので、忘れずに
rails db:migrate
する。config/storage.ymlを作る(最初からあるイメージだが、今回は無かった)
local: service: Disk root: <%= Rails.root.join("storage") %> test: service: Disk root: <%= Rails.root.join("tmp/storage") %>config/environments/development.rb(test.rbも任意で、このハンズオンでテスト書くのかな...)に追加:
config.active_storage.service = :local5-1-2. ActionTextをモデルに適用する
Commentのcontentがtextになっていたのを、Action Textに置き換える。
rails g migration RemoveContentInCommentclass RemoveContentInComment < ActiveRecord::Migration[6.1] def change remove_column :comments, :content, :text end end
rails db:migrate
して、app/models/comment.rb
class Comment < ApplicationRecord belongs_to :community has_rich_text :content end
has_rich_text
でcontentカラムを作ったかのように振る舞ってくれるようになる。app/views/comments/new.html.haml(一部)
= form.label :content, class: 'form-label' = form.rich_text_area :content, class: 'form-control mb-4'app/views/communities/show.html.haml
= comment.contentsimple_formatを外した。
すると以下のようになる。
思ってたのと違う。cssが読み込まれていない。action_text:installしたときに新しく生成されたcssは、app/assets/stylesheets/actiontext.scss。内容がこちら
// // Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and // the trix-editor content (whether displayed or under editing). Feel free to incorporate this // inclusion directly in any other asset bundle and remove this file. // //= require trix/dist/trix // We need to override trix.css’s image gallery styles to accommodate the // <action-text-attachment> element we wrap around attachments. Otherwise, // images in galleries will be squished by the max-width: 33%; rule. .trix-content { .attachment-gallery { > action-text-attachment, > .attachment { flex: 1 0 33%; padding: 0 0.5em; max-width: 33%; } // (以下省略)trix/dist/trixをrequireしていて、更に添付ファイルの表示に関するスタイルを上書きしているらしい。
app/views/layouts/application.html.hamlでは、
= stylesheet_link_tag 'application'
を消して、= stylesheet_pack_tag 'application'
に変えているので、上記ファイルは読み込まれない。
= stylesheet_link_tag 'application'
を復活させれば動く(動作確認済み)。だが、せっかくなので、webpackerに寄せる。
app/javascript/stylesheets/application.scss
@import "~@fortawesome/fontawesome-free/scss/fontawesome"; $primary: #9400d9; @import "bootstrap"; @import "trix/dist/trix"; @import "actiontext.scss";app/javascript/stylesheets/actiontext.scssではsprocket用のコードを外して記述。
// We need to override trix.css’s image gallery styles to accommodate the // <action-text-attachment> element we wrap around attachments. Otherwise, // images in galleries will be squished by the max-width: 33%; rule. .trix-content { .attachment-gallery { > action-text-attachment, > .attachment { flex: 1 0 33%; padding: 0 0.5em; max-width: 33%; } &.attachment-gallery--2, &.attachment-gallery--4 { > action-text-attachment, > .attachment { flex-basis: 50%; max-width: 50%; } } } action-text-attachment { .attachment { padding: 0 !important; max-width: 100% !important; } } }すると、
できた。
こんな感じで入力できる。
それで投稿すると
画像が表示されない。
chromeのconsoleを見ると、
GET http://localhost:3000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--dce7eada98f7015981b311d8495658640b895ad9/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFJQUJHa0NBQU09IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--9a6414b9afa153c394bf23eb32d0e368370e8b60/Screenshot_20210304-220735.png 500 (Internal Server Error)読み込めてない。500なので内部的なエラー。
したがってrailsのコンソールを見ると、
LoadError (Generating image variants require the image_processing gem. Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.)ちゃんと書いてあった。Gemfileに
gem 'image_processing', '~> 1.2'
を追加。bundle install。表示されました。
5-2. Action Mailbox
(執筆中です)
- 投稿日:2021-03-14T10:47:12+09:00
JST 時刻文字列から UTC 時刻型に変換する
Time.parse(jst_start_date_string).in_time_zone.utc
- 投稿日:2021-03-14T10:45:51+09:00
マイグレーションファイルを変更する方法(Rails)
マイグレーションファイル変更する方法
参考にしたサイト Railsチュートリアル(5.1)
$ rails db:rollback $ rails db:migrate VERSION=0 $ rails db:rollback STEP=○
- 1つ前の状態に戻します。
- 最初の状態に戻したいときは
VERSION=0
つける- 最後のマイグレーションファイルから数えて戻したい数だけ戻せる
$ rails db:rollback $ rails db:migrate:status Status Migration ID Migration Name -------------------------------------------------- up 20200830060821 Devise create users down 20200830062141 Create books
$ rails db:migrate:status
を使うことで変更できるファイルの一覧を確認できます。
downが変更可能でupが変更できないファイルです。
$ rails db:rollback
を使うとdownに変更されていると思います。$ rails g migration add_index_to_users_emailusersテーブルの中にemailカラムをを追加するコマンド
おまけ
$ rails db:migrate $ bundle exec rake db:migrate
- Rails 5以降のコマンド
- Rails 4以前のコマンド
- 投稿日:2021-03-14T09:43:48+09:00
HerokuでMySQLサーバーを使う方法
Heroku CLIをインストールしてから、ClearDBの導入から、Heroku apps:infoまでの手順を紹介します!(学習記録)
Heorkuのインストールからアプリ作成
# heroku cliのインストール % brew tap heroku/brew && brew install heroku # herokuのversionを確認 % heroku --version # herokuにログインする % heroku login --interactive #パスワードとメールアドレスを入力してログイン # heroku createで本番環境用のアプリを作成 cd ~/(アプリまでの相対パス) heroku create (アプリの名前) # アプリが正常に作られていることを確認 % git config --list | grep herokuHerokuにMySQLを導入
# ClearDBを導入 % heroku addons:add cleardb # Railsに対応させる heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL` # データベースのURLの再設定 heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5}Heroku上で非公開の値を管理
# masterkeyを確認 % cd ~/(アプリまでの相対パス) % EDITOR="code --wait" bin/rails credentials:edit # heroku上に環境変数を設定 % heroku config:set RAILS_MASTER_KEY=`cat config/master.key` # 正しく反映されているか確認 % heroku configHerokuにpush
# herokuに自分のアプリを追加 % git push heroku master # Rubyのバージョンによっては必要なコマンド heroku stack:set heroku-18 -a (アプリの名前) # migrateを実行 heroku run rails db:migrate # heroku apps:infoで確認 heroku apps:info以上がHerokuでMySQLサーバーを使う流れになります。参考までにどうぞ!!
- 投稿日:2021-03-14T09:43:48+09:00
HerokuでMySQLサーバーを使う方法(Rails)
Heroku CLIをインストールしてから、ClearDBの導入、Heroku apps:infoまでの手順を紹介します!(学習記録)
Heorkuのインストールからアプリ作成
# heroku cliのインストール % brew tap heroku/brew && brew install heroku # herokuのversionを確認 % heroku --version # herokuにログインする % heroku login --interactive #パスワードとメールアドレスを入力してログイン # heroku createで本番環境用のアプリを作成 cd ~/(アプリまでの相対パス) heroku create (アプリの名前) # アプリが正常に作られていることを確認 % git config --list | grep herokuHerokuにMySQLを導入
# ClearDBを導入 % heroku addons:add cleardb # Railsに対応させる heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL` # データベースのURLの再設定 heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5}Heroku上で非公開の値を管理
# masterkeyを確認 % cd ~/(アプリまでの相対パス) % EDITOR="code --wait" bin/rails credentials:edit # heroku上に環境変数を設定 % heroku config:set RAILS_MASTER_KEY=`cat config/master.key` # 正しく反映されているか確認 % heroku configHerokuにpush
# herokuに自分のアプリを追加 % git push heroku master # Rubyのバージョンによっては必要なコマンド heroku stack:set heroku-18 -a (アプリの名前) # migrateを実行 heroku run rails db:migrate # heroku apps:infoで確認 heroku apps:info #最後にHerokuのMySQLサーバーをリセットするコマンド % heroku run DISABLE_DATABASE_ENVIRONMENT_CHECK=1 rails db:drop db:create db:migrate以上がHerokuでMySQLサーバーを使う流れになります。参考までにどうぞ!!
- 投稿日:2021-03-14T00:31:41+09:00
【Ruby】クラス、メソッドの定義とインスタンス変数について
はじめに
この記事は、Ruby/Railsの学習期間約1ヶ月の著者が書いたものです。
初投稿となりますので、誤った解釈があるかもしれません。その場合は、コメント等頂けますと幸いですm(_ _)m今回は、Ruby学習中によく分からなくなりがちな、クラス、メソッドの定義、インスタンス変数について説明します。
独自のクラスを定義する
まず、Rubyではクラスを定義します。
クラスとは、設計図のようなもので、class
から始まり、end
で終わります。クラス名は、
ClassName
というように、単語の先頭を大文字で定義します。
ラクダのコブのようであることから、CamelCase
と表現します。class.rb#クラスの定義 class ClassName endクラスにメソッドを定義する
クラスの設計図の中にメソッドを定義することで、そのクラス独自が持っているメソッドとして認識されます。
メソッドとは、何らかの処理を示しており、処理内容を
def
とend
で囲みます。
メソッド名は、method_name
のように2単語目以降は、アンダーバーで示しますので注意しましょう。
(このことを蛇に例えて、snake_case
と呼びます。)method.rb#メソッドの定義 class ClassName def method_name # メソッドの処理を書く end end #クラスのインスタンスを作成し、メソッドで呼び出す instance = ClassName.new instance.method_nameRubyのオブジェクトは何らかのメソッドを持っていて、そのメソッドを定義することで、処理を返してくれるようになります。
ローカル変数とインスタンス変数について
次に、ローカル変数とインスタンス変数の違いを見てみましょう。
ローカル変数
ローカル変数は、使い回しできる範囲が他の変数よりも限られています。
例えば、メソッド内で定義した場合は、そのメソッド内でしか使えず、同じクラス内であっても使い回しができません。試しに、以下のローカル変数
message
を、say_morning
のメソッドの処理として定義し、
say_good_morning
のメソッド内で使い回してみましょう。local_variable.rbclass Greeting def say_morning message = "morning!" puts message end def say_good_morning puts "Good #{message}" end end instance = Greeting.new instance.say_morning instance.say_good_morning以下のようなエラーが出てしまいました。
`say_good_morning': undefined local variable or method `message' for #<Greeting:0x00007fe5b1984038> (NameError)つまり、
say_good_morning
のメソッドの中で、message
というローカル変数やメソッドは定義されてませんよ、と言っているわけです。
このように、ローカル変数は、あるメソッド内で定義した変数は、他のメソッド内で使い回すことができません。インスタンス変数
一方、インスタンス変数とは、同じオブジェクト内(同クラス内)で使い回せる変数のことをいいます。
変数の先頭に@をつけて定義します。先程のローカル変数
message
を、インスタンス変数@message
に置き換えて見てみましょう。instance_variable.rbclass Greeting def say_morning @message = "morning!" puts @message end def say_good_morning puts "Good #{@message}" end end instance = Greeting.new instance.say_morning instance.say_good_morning結果は以下の通り。
morning! Good morning!欲しい結果が出ましたね!
つまり、インスタンス変数とは、同じクラス内であれば、異なるメソッド内であっても何度でも使い回せる変数である、ということが証明されました。おわりに
今回はRubyの基本であるクラス、メソッドの定義とローカル変数とインスタンス変数の違いについて、説明しました。
今後も、Ruby/Railsを学習する上で、皆様に有益な情報を発信できればと思いますので、良ければLGTMお願いします。参考記事
- 投稿日:2021-03-14T00:09:43+09:00
【Rails】Cloud9で送信メールのプレビュー時の自分のホスト名と指定のURLの指定方法【Railsチュートリアル11章】
自分がつまづいた時の状況
Railsチュートリアル第11章アカウントの有効化でHTMLメールとテキストメールのプレビューをする際に、アプリケーションのdevelopment環境の設定ファイルの設定と、Railsサーバー起動時のURLが分からず、送信メールのプレビューが表示できませんでした。
・クラウドIDE:AWS Cloud9
・Rails:v6.0.3
・OS:Winconfig/environments/development.rbRails.application.configure do . . . config.action_mailer.raise_delivery_errors = false host = 'example.com' # ここの指定方法が分からなかった(自分のホスト名を指定する) config.action_mailer.default_url_options = { host: host, protocol: 'https' } . . . end自分のホスト名の調べ方
まず、自分のホスト名を調べる方法ですが、
Cloud9のPreview PreviewRunning Applicationを押下し、アドレスバーがついているpreviewタブを表示させます。
その後、そのアドレスバーに表示されているlocalhost以下の文字列が自分のアドレスになります。その自分のホスト名の中でもhttpsから.vsfの間の文字列を先ほどのdevelop環境の設定ファイルに反映します。
config/environments/development.rbconfig.action_mailer.raise_delivery_errors = false host = 'hoge.vfs.cloud9.us-east-2.amazonaws.com' # クラウドIDE config.action_mailer.default_url_options = { host: host, protocol: 'https' }設定ファイルを変更したら、Railsサーバーは再起動してください。
送信メールのプレビューのURL
URLですが、下記でプレビューが表示されました。
ホストの後ろに「/rails/mailers/user_mailer」とつけるだけでした。https://hogehoge.vfs.cloud9.ap-northeast-1.amazonaws.com/rails/mailers/user_mailer参考
Railsチュートリアル第11章アカウントの有効化
rails tutorial にて 送信メールのプレビューができない
rails チュートリアルの11章の2・2にてクラウドIDEのホスト名がわからない
- 投稿日:2021-03-14T00:08:37+09:00
PG::ConnectionBad (FATAL: role "postgres" does not exist )の対処法 | PostgreSQLでRoleを作成する
はじめに
railsで環境構築していたところ
bin/rails db:create
で下記のエラーが発生した。解決に少し手間取ったのでやり方をまとめておく。PG::ConnectionBad (FATAL: role "postgres" does not exist );原因
まずは原因から。このエラーが発生しているのは、postgresというロールが存在しないから。
postgresというロールはPostgreSQLを最初に起動すると自動で作られるらしいが、自分の場合は作られていなかったらしい。
ロールというのはユーザやグループに対してアクセス制限をかけることができるものらしい。AWSのIAMロールと同じだと思う。
例)Role:AはSUPERUSERでなんでもOK
Role:Bはデータベースの作成のみOK解決策
postgresというロールを作成して権限を与えてあげればOK
ロールを作成する手順
DBに接続する。
$ psql postgrespsqlのバージョンとヘルプが返ってきたら接続成功。
psql (13.1) Type "help" for help.\duで存在するロールの一覧が取得できる。
postgres=# \duRole nameにpostgresが存在していないと思うので作成する。
CREATE ROLE ロール名 属性;
またはCREATE ROLE ロール名;
で作成できる。postgres=# CREATE ROLE postgres SUPERUSER;ロールを確認する。
postgres=# \duRole nameにpostgresが作成されているはず。
Role name | Attributes postgres | Superuser, Cannot login属性にCannot loginがついてしまっているのでloginの権限を与える。
ALTER ROLE ロール名 属性;
postgres=# ALTER ROLE postgres LOGIN;もう一度ロールを確認し、下記のようになっていたらOK。
postgres=# \du Role name | Attributes postgres | Superuserdbから抜ける。
postgres=# \q自分の環境でDBを作成する。(自分の場合はrails)
$ bin/rails db:create $ bin/rails db:migarte補足
・
psql postgres
でDBに接続して作業にpostgres=#
ではなく、postgres-#
と表示されたときpostgres=# ALTER ROLE postgres LOGIN postgres-#
postgres-#
は分割入力をわかりやすく区別するためのもの。意図して分割していない場合、SQL文が完了していないと思う。postgres=# ALTER ROLE postgres LOGIN postgres-# ; 最後の';'を忘れるのはよくある・postgresにSuperuserを適用している件について
今回postgresに適用したSuperuserは一番強い権限でなんでもできるやつ。しかし、基本的にアクセス権限は無闇に強い権限を与えず、必要最小限の権限を与えることが推奨されている。
だからSuperuserを与えるのは良くないんじゃないのと思うかもしれないが、そもそもpostgresが自動で作られた場合、Superuserの権限を持っているらしい。postgresから他のロールを作成してアクセス権限を与えてロールを管理するのが通常らしい。そのため今回はpostgresにSuperuserの権限を与えている。
余談だけど"PG::ConnectionBad (FATAL: role "postgres" does not exist )"で検索しても記事が全然ヒットしなかったけど、"PostgreSQL Role 作成"で検索したら記事がたくさんヒットしたから検索の仕方は大事だなと思った。