20210413のRailsに関する記事は22件です。

herokuでマイグレートしようとしたらMysql2::Error: Table already existsが起きたときの解決策

起こったこと Railsでポートフォリオを作成中、デプロイした後にマイグレートしようとしたら、 Mysql2::Error: Table 'users' already exists が吐かれてマイグレートできなくなりました。 原因 原因ははっきりとしていて開発環境でテーブルを増やしてアソシエーションを組んだときに、マイグレーションファイルの作成順序が誤っていて(最初からちゃんとやっておけば良い話)マイグレートに失敗し、マイグレーションファイルの作成日時を手動で変更したことが原因でした。 試してみたこと まず、heroku run rails db:migrate:statusで本番環境のデータベースの状態を確認してみました。 予想通り、マイグレーションのステータスがおかしなことになっています。 本来、テーブルは4つありますが、下から2つ目のファイルは NO FILE となっています。 これが手動でマイグレーションファイルの作成日時を変更した弊害です。 この後、heroku run rails db:migrate:resetでデータベースのリセットを試みましたが、別のエラーが起こりうまくいきませんでした。 解決策 heroku run DISABLE_DATABASE_ENVIRONMENT_CHECK=1 rails db:drop db:create db:migrate を実行します。 このコマンドはデータベースを一度壊して作り直し、マイグレートするのを一度に実行するものです。 これでもう一度heroku run rails db:migrate:statusを実行し、ステータスを確認してみます。 ステータスが正常に戻りました。 他にも方法はあると思いますが私のケースではこのコマンド一発で解決できました。 エラーを通じて 本来、データベースをしょっちゅうresetしたりdropするということ自体、あまり好ましくないものだと思います。設計の甘さで今回のようなことになってしまったので、今後作成していく個人アプリケーションでは後々のことも考えてテーブル設計、実装順序の組み立てをしていこうという学びになりました。 同じようなエラーにあたった方の、助けになれれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】rails console が起動できない場合

rails consoleを実行するとターミナルが固まってしまい立ち上がらないという現象が起こったため、解決法を忘れないように備忘録として残しておきます。 結論 springを止めたら起動できるようになります! 手順 ターミナル # 裏で動いているプロセスがないか確認 $ ps aux | grep rails ユーザー名 9005 0.0 0.0 4268284 680 s000 S+ 10:20PM 0:00.01 grep rail # 特になかった。 ターミナル # spring(裏で動いてアプリを快適にしてくれるもの)を確認 $ ps aux | grep spring ユーザー名 6544 0.0 0.3 4375700 24804 ?? Rs 9:48PM 0:01.36 spring app | my_portfolio4 | started 0 secs ago | development mode ユーザー名 6543 0.0 0.3 4374676 24876 ?? Rs 9:48PM 0:01.37 spring app | my_portfolio4 | started 0 secs ago | test mode ユーザー名 931 0.0 0.1 4372216 11024 ?? S 日02PM 0:01.01 spring server | my_portfolio4 | started 55 hours ago ユーザー名 9064 0.0 0.0 4297404 576 s000 R+ 10:21PM 0:00.00 grep spring # startedと書かれているし、動いていそうな雰囲気。。。 ターミナル # 止めてみる。 $ bundle exec spring stop Spring stopped. ターミナル # 再度コマンドの実行をしてみる。 $ rails console Running via Spring preloader in process 9252 Loading development environment (Rails 6.0.3) [1] pry(main)> 無事に起動しました!! 【参考記事】 https://qiita.com/YumaFuu/items/aa10290e8c48613089a6
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】CSRFが引き起こすエラー

CSRF CSRF(クロスサイトリクエストフォージェリ)とは、Webアプリケーションの脆弱性を利用したサイバー攻撃の一種 詳しくは、https://qiita.com/wanko5296/items/142b5b82485b0196a2da CSRFが引き起こすエラー CSRF対策として、サーバはクライアントに対してトークンを設定します。 リクエストが発動した際に、クライアントのトークンとリクエストのトークンを確認し、攻撃者からのリクエストを防ぐことができます。 Railsの場合、 protect_from_forgery with: :exception で、アプリ内のフォームに対してトークンを発行しています。 しかし何らかのエラーでトークンが一致しない場合、リクエストが正常に動作しない場合があります。 (以下のようなエラーが起きる) Can't verify CSRF token authenticity. 対応 メソッドのあるコントローラー内に、以下の記述を追記します。 skip_before_action :verify_authenticity_token この記述で、トークンの比較自体がスキップされるのでリクエストは動作するはずです。 しかし、トークンの比較をスキップするとのことなのでセキュリティ的にはどうなのかなとは思いますが、、 (詳しい方がいれば教えていただけると嬉しいです)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Uncaught Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): の解決方法

注意 今回の内容はコマンドの実行内容を完全に理解できていないように思います。場当たり的な解決できた参考例である事を前提として、実行の際には自己責任でお願いいたします。 環境 ・AWS Cloud9 (Ubuntu 18.04.5 LTS, Bionic Beaver)1 ・Node.js2 エラー内容 Terminal Uncaught Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): 解決策 以下を実行。 Terminal npm install --save-dev --unsafe-perm node-sass 経緯 検索した結果で最頻の方法を試すも解決しなかった。以下のコマンド。 Terminal npm rebuild node-sass エラー原因・背景など ・原因:EC2のデフォルトのnode-sassが古かった?(導入:2021/2/14時点) https://stackoverflow.com/questions/54354644/error-in-module-build-failed-from-node-modules-sass-loader-lib-loader-js ・コマンド背景参考URL https://qastack.jp/programming/48147896/error-in-cannot-find-module-node-sass 2021/2/14時点 ↩ バージョンは失念しました...すみません ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

before_actionで同じコードをメソッドにまとめる方法

before_actionとは使用すると、コントローラで定義されたアクションが実行される前に、 共通の処理を行うことができるメソッド 修正前 def show @item = Item.find(params[:id]) end def edit @item = Item.find(params[:id]) unless @item.user_id == current_user.id redirect_to action: :index end end def update @item = Item.find(params[:id]) if @item.update(item_params) redirect_to item_path else render :edit end end showアクション、editアクション、updateアクションに同じ@item = Item.find(params[:id])が 連続して記述してある また、editアクションにあるunless @item.user_id == current_user.idをアプリの安全性を高めるため、updateアクションでも使えるようにしたい 修正後 before_action :set_item, only: [:edit, :update, :show] before_action :move_to_index, only: [:edit, :update] def show end def edit end def update if @item.update(item_params) redirect_to item_path else render :edit end end private def set_item @item = Item.find(params[:id]) end def move_to_index unless @item.user_id == current_user.id redirect_to action: :index end before_action :set_item, only:[:edit, :update, :show]と記述することでeditアクションと updateアクションとshowアクションとが実行される前に、set_itemに定義されている処理が実行される set_itemはプライベートメソッド内に記述 またunless @item.user_id == current_user.idも同じようにbefore_actionで記述して プライベートメソッド内でmove_to_indexと定義することでeditアクション、updateアクションで使用できる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】ActiveStorageによる画像アップロード

はじめに  現在勉強の一環で作成中の音楽レビューアプリでActiveStorageによる画像アップロードを使用したのでメモ。 アプリについて ・作成中のアプリは音楽アルバムのレビューアプリ。 ・ActiveStorageを利用し、Recordモデルに画像を :jacket として 紐づける。 ActiveStorageのインストール 以下のコマンドでインストールする。 rails active_storage:install Recordモデルを編集 次にRecordモデル編集し、一つの画像を持つようにする。 今回はアルバムのジャケットとして :jacket とする。 app/models/record.rb class Record < ApplicationRecord has_one_attached :jacket  #以下省略 end    Recordにジャケットを紐づける 次にRecord新規作成(編集)のページにファイルアップロードをするタグを追加。 app/views/records/_form.html.erb <%= form_with(model: @record, local: true) do |f| %> <div class="form-group"> <%= f.label :jacket %> <%= f.file_field :jacket %> </div> <%# 省略 %> <%= f.submit "Create Record", class: "btn btn-info" %> <% end %>    次はフォームを正しく受け取れるように records_controllerのストロングパラメータに :jacket を追加する。 app/controller/records_controller.rb class RecordsController < ApplicationController #省略 private def record_params params.require(:record).permit(:name, :artist, :release_year, :genre_id, :jacket) end end ビューで表示 ビューでは<%= image_tag record.jacket %>のように書くことで画像を表示できるが、このままだとアップロードされた画像サイズがバラバラなのでリサイズできるようにする。 mini_magickのインストール まずはGemfileにMiniMagickを追加。 Gemfile gem 'mini_magick' そしてbundle installする。    実際に表示する 先ほどMiniMagickを追加したことにより、 <%= image_tag record.jacket.variant(resize:'120x120') %> のようにして画像をリサイズして表示できる。 app/views/records/index.html.erb <% @records.each do |record| %> <div class="album text-center"> <%= link_to record do %> <div class="mb-2">       <%# ここに表示 %> <%= image_tag record.jacket.variant(resize:'120x120') %> </div> <h5 class="mb-0 font-italic"><%= record.name %></h5> <% end %> <small><%= link_to record.genre.name, records_path(genre: record.genre.name) %></small> <p class="text-bold"><%= record.artist %></p> </div> <% end %> これで画像が表示される。 N+1問題 これまでで画像の表示は完了したがN+1問題が発生していたので修正する。 Records/index ページで一覧を表示する時に、:jacket画像を読み込むためRecordの数だけDBに問い合わせているのが問題。 これを解決するためにはrecords_controllerを編集する。 app/controller/records_controller.rb class RecordsController < ApplicationController def index #省略   @records = Record.all.includes(jacket_attachment: :blob) #省略 end end Record.allと書いていたところを、 Record.all.includes(jacket_attachment: :blob) とすることで解決した。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】画像をasset pipelineを使わずに静的に表示する方法

Railsで画像をasset pipeline(image_tag)を使わずに静的に表示する方法について。 対処法 app > public配下に画像を保存し、src属性にpublic配下のパスを記述する。 ・<img src="public配下の画像パス" alt=""> ▼例 public/assets/imgs/bear-icon-blue.svg を読み込む場合 <img src="assets/imgs/bear-icon-white.svg" alt=""> ▼ブラウザの表示 画像の読み込みに成功。 注意点 assets配下の画像をsrc属性で指定して読み込むことはできない。NotFoundのエラーが出る。(昔のRailsでは読み込めたらしい) asset pipelineで読み込む方法 より一般的なasset pipelineを使う場合は、asset > images配下に画像を保存し、image_tagヘルパを使う。 ・<%= image_tag 'asset > images配下の画像パス' %> ▼実例 <%= image_tag 'bear-icon-white.svg' %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】画像をasset pipelineを使わずに静的に表示する方法 & image_tagでid/クラス/altを指定する方法。

Railsで画像をasset pipeline(image_tag)を使わずに静的に表示する方法について。 対処法 app > public配下に画像を保存し、src属性にpublic配下のパスを記述する。 ・<img src="public配下の画像パス" alt=""> ▼例 public/assets/imgs/bear-icon-blue.svg を読み込む場合 <img src="assets/imgs/bear-icon-white.svg" alt=""> ▼ブラウザの表示 画像の読み込みに成功。 注意点 assets配下の画像をsrc属性で指定して読み込むことはできない。NotFoundのエラーが出る。(昔のRailsでは読み込めたらしい) asset pipelineで読み込む方法 Railsでより一般的なのはasset pipelineを使う方法。 この場合、asset > images配下と、public配下のどちらでも読み込むことができる。 ・asset > images配下: パスの冒頭に/不要。 ・public配下: パスの冒頭に/が必要。 1. asset > images配下の場合 ・<%= image_tag 'asset > images配下の画像パス' %> ▼実例 <%= image_tag 'bear-icon-white.svg' %> 2. public配下の場合 ・<%= image_tag '/public配下の画像パス' %> ▼実例 <%= image_tag '/assets/imgs/bear-icon-white.svg' %> images_tagでid, class, altを指定する方法 属性名: "値"で記述する。 <%= image_tag "画像パス", id: "id名", class: "クラス名", alt: "alt名", width: "幅" , height: "高さ" %> ▼実例 <%= image_tag 'bear-icon-white.svg', class: "icon-mage", alt: "白熊" %> :の後ろにスペースがなくても機能する。 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】コンパイルした時にクラス属性の値が更新されない時の対処法

Railsのアプリケーションでファイルを更新してコンパイルしたのにブラウザに表示されるクラス属性が変わらない状況が発生したので、その対処法。 事象 クラス属性のアンダーバーをハイフンに変更し、コンパイル後リロードしたがブラウザに反映されなかった。 index.html.erb <div class="top_wrapper"> ↓ index.html.erb <div class="top-wrapper"> 対処法 -と_はコンパイル時に差分とみなされていない様子。 アルファベットを変更してリロードし、そこから変更したいクラス名に変更すると反映される。 index.html.erb <div class="top_wrapper"> ↓ ダミーの変更を挟み、リロード index.html.erb <div class="top"> ↓ 本来変更したいクラス名に変更 index.html.erb <div class="top-wrapper"> 無事変更が反映された。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】WebpackerでSCSSがコンパイルできない時の対処法|Uncaught Error: Cannot find module '../stylesheets/application'

RailsにWebpackerを使ってBootstrapを導入しコンパイルしようとすると以下のようなエラーが発生。この対処法について。 エラー内容 Uncaught Error: Cannot find module '../stylesheets/application' at webpackMissingModule (application.js:1) at Module../app/javascript/packs/application.js (application.js:1) at webpack_require (bootstrap:19) at bootstrap:83 at bootstrap:83 やりたいことは、app配下のstylesheets > application.scssを packs > application.jsの中にimportしてコンパイルしたい。 対処法 結論からいうと、node-sassのバージョンが高すぎて互換性がなかったのが原因。 node-sassを削除して、バージョンを下げて再インストールしたら直った。 yarn remove node-sass yarn add node-sass@4.14.1 npmを使っている場合は以下 npm uninstall node-sass npm install node-sass@4.14.1 Webpackerの動作確認方法 jsファイルをコンパイルできているか確認 scssファイルを移動してコンパイルしてみる 1. jsファイルをコンパイルできているか確認 そもそもwebpackerが動いているか確認する必要があるため、検証用のjsファイルを作成しimportしてみる。 app > javascript > javascripts > test.js を作成する。 test.js console.log('hello') app > javascript > packs > application.jsの中で読み込む。 application.js import Rails from "@rails/ujs" import Turbolinks from "turbolinks" import * as ActiveStorage from "@rails/activestorage" import "channels" import 'bootstrap' //追記 import '../javascripts/test' Rails.start() Turbolinks.start() ActiveStorage.start() この状態でブラウザをリロードし、consoleを確認する。 正しく表示されているため、webpackerで外部のjsファイルをコンパイルできていることがわかる。 とすると、問題はsassのコンパイルができていないこと。 2. scssファイルを移動してコンパイルしてみる コンパイルエラーが発生していた、app > javascript > stylesheets > application.scssを、コンパイルが成功したjavascripts配下に移動して読み込んでみる。 application.js import Rails from "@rails/ujs" import Turbolinks from "turbolinks" import * as ActiveStorage from "@rails/activestorage" import "channels" import 'bootstrap' import '../javascripts/test' //追記 import '../javascripts/application' Rails.start() Turbolinks.start() ActiveStorage.start() ブラウザで確認すると新たなエラーが発生。 ブラウザのコンソール ModuleBuildError: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): Error: Node Sass version 5.0.0 is incompatible with ^4.0.0. Node Sassのバージョン5を使っているが、コンパイルに必要な4系と互換性がないとのエラー。 sass-loaderを動かすためにはnode-sassが必須。このバージョンが適合していなかった。 現在のバージョンを削除してダウングレードする。 yarn remove node-sass yarn add node-sass@4.14.1 Docker上でアプリケーションを動かしている場合は、コンテナの中で実行する必要がある。 Dockerコンテナの場合 $ docker exec -it コンテナ名 sh /app # yarn remove node-sass /app # yarn add node-sass@4.14.1 ブラウザを再ロードするとエラーが消えCSSが適用された。 3. sassファイルを本来のディレクトリに移動 application.sassファイルを stylesheets配下に移動し、コンパイル用のファイルを変更。 application.js import Rails from "@rails/ujs" import Turbolinks from "turbolinks" import * as ActiveStorage from "@rails/activestorage" import "channels" import 'bootstrap' import '../javascripts/test' //追記 import '../stylesheets/application' Rails.start() Turbolinks.start() ActiveStorage.start() 再度ブラウザを読み込む。 sassのコンパイルに成功し、bootstrapが適用された。 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails チュートリアル第二章

rakeコマンドとrailsコマンド Rails 4以前ではRakeを使っているため、 古いRailsアプリケーションを扱うためにはRakeについて学ぶ必要があります。 おそらくもっとも頻繁に使われていたRakeコマンドは、データベースのデータモデルを更新するためのrake db:migrateコマンドと、自動化されたテストスイートを実行するためのrake testコマンドの2つでしょう。 また、Rails 4以前のアプリケーションでは、rakeコマンドのバージョンをGemfileで定義しているため、Bundlerのbundler execコマンドを通して実行する必要があります。 したがって、例えばRails 5におけるマイグレーションコマンドは次のようになりますが、 $ rails db:migrate Rails 4以前では、次のように実行する必要があります。 $ bundle exec rake db:migrate Rakeの特徴 Rubyで記述ができる Rakefileというファイルに一連の処理を定義する (この処理のまとまりを「タスク」と呼ぶ) bundle exec ・bundle execをつけないと、Gemfile.lockに基づかずにgemのバージョンが決定される ・bundle execをつけると、Gemfile.lockに書かれているバージョンのgemが動く ・railsコマンドだけはbinを省略してもbin/railsと解釈される なので、railsコマンドは何も付けずに実行 ・binディレクトリにファイルが存在するコマンドはbin/◯◯ ・それ以外のコマンドはbundle exec ◯◯ 参考文献 bundle execって必要なの? https://qiita.com/d0ne1s/items/fa2dafcee02e963fe997 Railsのrakeってなんぞ? https://qiita.com/SuguruOoki/items/e736b15bbb80eacf66d7#dsldomain-specific-language%E3%81%A8%E3%81%AF RESTfulな設計 まずRESTについて簡単に解説しておきます。RESTとはアプリケーションの設計方法の1つで、操作の対象となるリソースをURLを使って表し、それに対してHTTPメソッドの「GET」「POST」「DELETE」「PUT」を使って操作を行なうというものです。あるURLへGETを使ってアクセスすれば、結果を取得でき、同じURLへPUTでアクセスすれば新しく作成されるよう設定を行ないます。 Railsのアプリケーションの場合、対象となるリソースはデータベースのテーブルがほとんどです。このリソースを表すURLが例えば「http://localhost:3000/sample」だったとします。RESTの考え方に従えば、この1つのURLに対してHTTPメソッドのGETでアクセスすればデータを取得し、PUTでアクセスすればデータの作成が行われるようにします。 Railsでは利用者からのリクエストで呼び出されるのはアクションですので、URLとHTTPメソッドの組み合わせ毎に呼び出されるアクションをルーティングとして設定すればいいことになります。 引用 rails rutes詳細 引用 Active Record 引用 https://railsguides.jp/active_record_basics.html resources resources :photos 上の記述により、アプリケーション内に以下の7つのルーティングが作成され、いずれもPhotosコントローラに対応付けられます。 引用
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bootstrapが適用されない時の解決方法

前提・使用環境 AWS Cloud9 (Ubuntu) Ruby 2.6.6 Rails 6.1.2.1 エラー内容 以下を元に設定するも、自環境ではBootstrapのドロップダウンのリンクが表示されなかった。 Rails 6にjQueryとBootstrapを入れる(webpackerで入れる場合で進めた) ・実行したコマンド Terminal yarn install bootstrap@next jquery popper.js ※ Bootstrapに関してはリンク先と異なるコマンドを実行している1 ・ブラウザーに表示されたエラー Browser Uncaught Error: Cannot find module '@popperjs/core' 解決策 Teminal yarn install @popperjs/core エラーの原因・背景など エラー認識の経緯 Bootstrapのドロップダウンが表示されなかったので、ブラウザの検証で確認。以下のエラーが発生していた。 Browser Uncaught Error: Cannot find module '@popperjs/core' 意訳:"@popperjs/core moduleが見つけられないよ" ということだったのでYarnで上記モジュールをインストールした。(popperjs/popper-core - GitHub) 参照先 Bootstrap 5.0 - Download - Yarn あとがき 今回がQiita初投稿です。至らぬ点・ご指摘ございましたらコメントいただけますと幸いです。 Bootstrapのバージョンによって変わるらしい。導入予定のBootstrapのバージョンのドキュメントに合わせた。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails6] Bootstrapが適用されない時の解決方法

前提・使用環境 AWS Cloud9 (Ubuntu) Ruby 2.6.6 Rails 6.1.2.1 エラー内容 以下を元に設定するも、自環境ではBootstrapのドロップダウンのリンクが表示されなかった。 Rails 6にjQueryとBootstrapを入れる(webpackerで入れる場合で進めた) ・実行したコマンド Terminal yarn install bootstrap@next jquery popper.js ※ Bootstrapに関してはリンク先と異なるコマンドを実行している1 ・ブラウザーに表示されたエラー Browser Uncaught Error: Cannot find module '@popperjs/core' 解決策 Teminal yarn install @popperjs/core エラーの原因・背景など エラー認識の経緯 Bootstrapのドロップダウンが表示されなかったので、ブラウザの検証で確認。以下のエラーが発生していた。 Browser Uncaught Error: Cannot find module '@popperjs/core' 意訳:"@popperjs/core moduleが見つけられないよ" ということだったのでYarnで上記モジュールをインストールした。(popperjs/popper-core - GitHub) 参照先 Bootstrap 5.0 - Download - Yarn あとがき 今回がQiita初投稿です。至らぬ点・ご指摘ございましたらコメントいただけますと幸いです。 Bootstrapのバージョンによって変わるらしい。導入予定のBootstrapのバージョンのドキュメントに合わせた。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

04/17(土)用 Windows10にDockerでRails6+MySQLの環境構築

環境構築の手順書です。 山岡楓が制作しています。 Railsのデフォルトホーム画面を表示させるところまでです。 環境 Windows10 Ruby2.7.1 Rails6.0.3.1 MySQL8.0 1.Docker for Windowsをインストール https://hub.docker.com/editions/community/docker-ce-desktop-windows/ 上記リンクにアクセスしてインストールする。 インストールが出来たら一度再起動して、Dockerを起動させてください。 2.必要ファイルの作成 どこまでもいいので任意の場所にディレクトリを作成する。デスクトップとか。 ここではRails_appというディレクトリを作成します。(名前はなんでもいい) で、VScodeでフォルダを開いてください。 (エディターはなんでもいいですが、VScodeがいいと思います。) インストールがまだの場合は以下リンクからインストール https://azure.microsoft.com/ja-jp/products/visual-studio-code/ Dockerfileの作成 VScodeでディレクトリを開けたらディレクトリ内にDockerfileを作成し、 以下のように記述します。 FROM ruby:2.7 RUN apt-get update -qq && apt-get install -y nodejs yarnpkg RUN ln -s /usr/bin/yarnpkg /usr/bin/yarn RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install COPY . /app # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"] Gemfileの作成 次に同じ階層にGemfileを作成し、以下のように記述します。 source 'https://rubygems.org' gem 'rails', '~>6' もう一つGemfile.lockというファイルを作成します。 中身は空でOKです。 entrypoint.shの作成 次にDockerfileの中でENTRYPOINTとして定義しているentrypoint.shの作成をします。 entrypoint.sh #!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /app/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@" docker-compose.ymlの作成 最後にdocker-compose.ymlというファイルを同階層に作成し、以下を記述してください。 docker-compose.yml version: '3' services: db: image: mysql:8.0 volumes: - ./tmp/db:/var/lib/mysql environment: - MYSQL_ALLOW_EMPTY_PASSWORD=1 web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/app ports: - "3000:3000" depends_on: - db MYSQL_ALLOW_EMPTY_PASSWORDを設定することでpasswordが空でもrootで接続できるようにしておきます。 3.Rails new / docker-compose buildを行う VScodeのターミナルを開き、作成したディレクトリの階層にいる事を確認したら、 以下を実行してください。 $ docker-compose run web bundle exec rails new . --force --database=mysql --forceは既存のファイルを上書き、--databaseでMySQLを指定しています。 実行完了したらビルドを行います。 $ docker-compose build 4、DBのホスト名の変更 / docker-compose upを行う 現状だとデータベースへの接続が出来ないので、 最初に作成したディレクトリ内に出来たconfig/database.ymlのhostの値をdbという値に置き換えてください。 ※全部コピペはしないでください。 database.yml default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: host: db development: <<: *default database: app_development test: <<: *default database: app_test ここのdbがコンテナ名になります。 これが出来たら再度docker-compose buildを行ってからdocker-compose upを行います。 この状態でlocalhost:3000に接続するとおそらくエラーが出ます。 Webコンテナがmysql8.0のcaching_sha2_password認証方式に対応していない為です。 次の手順で認証方式をmysql_native_passwordに変更します。 5.MySQLの認証方式の変更 まずDBコンテナに入ります。 (VScodeでdocker-compose upを行っていると思うので、powershellを使って該当のディレクトリを開いてください。) そこでbashを起動します。 docker-compose exec db bash その後、mysqlコマンドで接続します。 mysql -u root 場所がmysql>になっている事を確認したら、 下記のクエリを実行してみてください。 mysql> select User,Host,plugin from mysql.user; すると以下のようなユーザ一覧と認証方式が出てきます。 +------------------+-----------+-----------------------+ | User | Host | plugin | +------------------+-----------+-----------------------+ | root | % | caching_sha2_password | | mysql.infoschema | localhost | caching_sha2_password | | mysql.session | localhost | caching_sha2_password | | mysql.sys | localhost | caching_sha2_password | | root | localhost | caching_sha2_password | +------------------+-----------+-----------------------+ 5 rows in set (0.00 sec) 上記で出てきたrootのpluginにあるcaching_sha2_passwordをmysql_native_passwordに変更します。 今回対象のroot@%のユーザ設定をALTER USERを使って変更します。 下記のクエリを実行してください。 ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY ''; 実行出来たら改めてselect User,Host,plugin from mysql.user;を実行すると、 +------------------+-----------+-----------------------+ | User | Host | plugin | +------------------+-----------+-----------------------+ | root | % | mysql_native_password | | mysql.infoschema | localhost | caching_sha2_password | | mysql.session | localhost | caching_sha2_password | | mysql.sys | localhost | caching_sha2_password | | root | localhost | caching_sha2_password | +------------------+-----------+-----------------------+ 5 rows in set (0.00 sec) 上記のような表示に変わっていると思います。 その後exitを2回実行して、元いた階層に戻ります。 で、階層が作成したディレクトリなのを確認出来たら以下を実行します。 $ docker-compose exec web bundle exec rails db:prepare 6.Railsのホーム画面にアクセス DBを作成し、localhost:3000すると、Railsのホーム画面にアクセスできるようになります。 環境構築は以上です。 お疲れ様でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Your bundle is locked to mimemagic (0.3.5), but that version could not be foundでbundle installが上手くいかない時の対処法

Railsのmimemagicが原因でbundle installが動かない Your bundle is locked to mimemagic (0.3.5), but that version could not be found Railsと間接的に依存関係にあるmimemagicがソフトウェアのライセンスの問題で、公開停止をしているため、該当するGemが見つからない状態になっていた。 解決方法 mimemagicをアップデートさせることにより、対応が可能 $ bundle update mimemagic 今回のライセンス問題で出てきたライセンスの種類と概要 mimemagic自体ははMITというライセンス、mimemagicの一部コードでGPLというライセンスが使用されているとのこと。 MITとは Massachusetts Institute of Technology Licenseの略で、マサチューセッツ工科大学で作成された寛容型オープンソースライセンスの一つとのことです。 特徴 MITライセンスを用いて公開されたプログラムを改変したり、自らのプログラムに組み込んだ派生的な著作物は、ソースコードを公開せずに販売・配布が可能になるとのことです。 ライセンスの制限が少ないため、他のライセンスと組み合わせて共存することも多いとのこと。 GPL2.0とは GNU General Public Licenseの略で、OSSやフリーソフトウェアでも採用されているもの 特徴 ソースコードの公開を原則として、誰でも入手、使用、改変再配布することを認めているとのこと。 GPLで公開されているプログラムを改変したり、自らのプログラムの一部として組み込んだ場合など、派生的・二次的な著作物を作成した場合には、これにもGPLを適用して公開しなければならないと定めているらしい。 個人的な感想 今回のmimemagicの件は、ライセンスの特徴の違いによって発生した問題だったとのことで、こういった法律関係に関しては完全に専門外ですので、概要を掴むことがギリギリでした。 どの業種においても、ルールや法律は存在するので、今回のRails開発をする際に、システムに関する法律に対する興味を持ついいきっかけになりました。 ※本ページ記載の内容は、あくまで私の理解になります。。ライセンス関連は、解釈の仕方や見解の相違によってトラブルになることが多いため、不利益を被っても保証や責任を取れません。詳しくは法律の専門家に相談してください。 参考 https://wa3.i-3-i.info/word13200.html https://yamory.io/blog/about-mit-License/ https://yamory.io/blog/about-gpl-license/ https://e-words.jp/w/GPL.html https://licenses.opensource.jp/GPL-2.0/GPL-2.0.html 原文 https://opensource.org/licenses/GPL-2.0 https://opensource.org/licenses/MIT
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fakersでダミーの注文データを作る(Ruby on Rails、seeds)

やりたいこと 社内研修で、ウォーターサーバーの替えのボトルの在庫を見張って自動発注するというプチIoTシステムをRailsで開発しています。 Railsで注文データのダミーを作りたかったのですが、調べても意外にそれっぽいハウツーに出会えなかったのでメモ。(自分のぐぐり力不足?) ちなみに、注文モデル(order.rb)には以下のカラムがあります。 カラム データ型 delivery_date date 納品日 qty integer 注文数 order_date date 注文した日付 status integer 0: 注文済み、1: 納品済み created_at datetime updated_at datetime item_id bigint 注文アイテムの外部キー user_id bigint 注文したユーザーの外部キー ちなみに、環境。 Rails6.0.3, macOS Catalina 10.15.7 実装! 今回はFakerというgemを活用して、ダミーの注文データを30個生成します。 (gemとは?=>Railsをさらに便利にしてくれるライブラリ(=拡張機能=オプション機能=カスタマイズ機能)みたいなやつ) Fakerそのもののインストールの仕方に関してはネットにたくさん記事があるので割愛します。 seeds.rb # 最新の注文は個別で作る order_date = Faker::Date.between(from: 5.days.ago, to: Date.today) Order.create( order_date: order_date, qty: Faker::Number.between(from: 1, to: 10), delivery_date: order_date + 3, # 納品日は注文日の3日後としておく。下に詳述。 status: 0, user_id: 2, item_id: 1) # 残り29個はランダムで生成してもらう 29.times do order_date = Faker::Date.between(from: 1.year.ago, to: 10.days.ago) Order.create( order_date: order_date, qty: Faker::Number.between(from: 1, to: 10), delivery_date: order_date + 3, status: 1, user_id: Faker::Number.between(from: 1, to: 2), item_id: Faker::Number.between(from: 1, to: 3) ) end ↑コードを見れば何をしたいか大体わかると思うので、解説は加えません。 これ以外の実装がしたい場合は、Fakerはその辺のチュートリアル記事よりも、本家に当たるのが吉でしたのでこちらを参照すると良いと思います。 Fakerドキュメント https://github.com/faker-ruby/faker#faker Faker::Date https://github.com/faker-ruby/faker/blob/master/doc/default/date.md#fakerdate ちなみに、当初、delivery_dateもFakerしようと思ったのですが、そうすると、delivery_date(納期)がorder_date(注文日)よりも前になってしまうなど整合性が取れないデータが発生することを懸念し、order_date + 3 という計算にしました。 余談ながら、この計算方法(文法)で合っているかは、ターミナルでrails db:seedする前に、rails consoleで確認できることを発見!↓ % rails console Running via Spring preloader in process 48085 Loading development environment (Rails 6.0.3) irb(main):001:0> order_date = Faker::Date.between(from: 1.year.ago, to: 10.days.ago) => Tue, 09 Jun 2020 irb(main):002:0> order_date + 15 => Wed, 24 Jun 2020 irb(main):003:0> 生成! ターミナルでrails db:seedすると下記のようにデータが生成されました!↓ 最後に 何かご指摘やもっといい方法などあればぜひ教えてください。 ありがとうございます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Docker】エラー対処法:Error: Missing binding /usr/src/app/node_modules/node-sass/

Dockerでイメージをbuild後にコンテナを起動した時に、sass-loaderを現在のOS環境にバインドできないとのエラーが発生した。 エラー内容 bootstrap.js:4427 Uncaught Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): Error: Missing binding /usr/src/app/node_modules/node-sass/vendor/linux_musl-x64-83/binding.node Node Sass could not find a binding for your current environment: Linux/musl 64-bit with Node.js 14.x Found bindings for the following environments: - OS X 64-bit with Node.js 12.x This usually happens because your environment has changed since running npm install. Run npm rebuild node-sass to download the binding for your current environment. Dockerの中で使っているのでlinux用のsass-loaderをインストールしなければいけないが、OS-X用のsass-loaderしかない状況。 以下対応をしたがエラーは直らず ・npm installはnpm rebuild node-sassを実行済み。 ・.dockerignoreにnode_modules/*を記載済み。 ・volumeでapp/node_modulesをコンテナ内のボリュームのみ割り当て、ホスト側とは同期しない。 発生原因 本来、コンテナの中でbootstrap関連のパッケージをインストールしなければいけないところ、ローカルで実施したためOS X用のsass-loaderしかインストールされなかった。 対処法 コンテナに入ってbootstrap関連のパッケージをインストールする。 #コンテナ名を確認 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 88e9777dd7de ruby2.7.2 "bin/rails s -p 3000…" 4 minutes ago Up 4 minutes 0.0.0.0:3001->3000/tcp rails-app #コンテナに入る(laravelの場合はbash) $ docker exec -it rails-app sh /app # #コンテナの中でbootstrap関連パッケージをインストール /app # yarn add jquery bootstrap popper.js コンテナを再起動したところエラーが直った。 Dockerfileでyarnをインストールしているのでyarnを使用したが、npmのときはyarnをnpmに変更する。 まとめ docker上のプロジェクトでnpmのパッケージをインストールする場合はコンテナの中に入ってインストールする。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

each_with_indexとeach.with_index(数字)で繰り返し処理

0. はじめに rubyには、「繰り返し処理」というものが存在します。具体的なメソッドで言うと、eachやtimes, whileなどです。 この記事では、私がこれまでによく利用していたeachメソッドの、少し応用系である2つのメソッドを記していきます。あと追加で、私が開発中に作って没になった「好きな場所で繰り返しを止める」メソッドを紹介します。また、具体例では以下の配列を扱います。 array = ["一郎", "二郎", "三郎"] 1. each_with_index 【Ruby each_with_indexの書き方】 配列名.each_with_index do |item, i| # 処理 end array.each_with_index do |item, i| puts "#{item}は#{i}番目です" end => 一郎は0番目です 二郎は1番目です 三郎は2番目です each_with_indexは、配列の要素地震が何番目なのか、そしてその番号(index)も処理で同時に扱うことができるメソッドです。indexを扱うことができる繰り返し処理で、処理内で一意性を与えたい時に特に用いていました。 しかし、配列のindexは0から始まります。数え初めが1である人間にとっては、少し馴染みにくい印象があります。そこで便利なのが、次のeach.with_index(数字)メソッドです。 2. each.with_index(数字) 【Ruby each.with_indexの書き方】 配列名.each.with_index(開始させたい値) do |item, i| # 処理 end array.each_with_index(1) do |item, i| puts "#{item}は#{i}番目です" end => 一郎は1番目です 二郎は2番目です 三郎は3番目です each.with_index(数字)では、始まりの数字を指定できます。(上記例では始まりが"1") このメソッドは自由度があり、意味もいくらか伝わりやすい気もします。使い分けが重要ですね。 3. 好きな場所で繰り返しを止める 具体例から示します。 例えば、5つの要素を持つ配列が存在するとします。この配列を2回繰り返し、3回目は要素の2つ目で繰り返しを止めたい、といった場合に使います(笑)。以下は例です。 配列:①②③④⑤ ①②③④⑤ | ①②③④⑤ | ①② ↑ココマデ! この場面で用いるのが、2個のtimesメソッドと、繰返しを中断させるbreakです。 3.times do |i| 5.times do |ii| puts "#{ii}" break if i==2 && ii==1 end end # 実際の結果 3.times do |i| 5.times do |ii| puts "#{ii}" break if i==2 && ii==1 end end => 0 1 2 3 4 0 1 2 3 4 0 1 timesメソッドは、指定回数繰り返しを行います。 breakメソッドは、条件分岐により途中で繰返しを中断します。 これらを組み合わせて「好きな場所で繰り返しを止める」メソッドを開発しました。 まぁ、こんなメソッドいつ使うんだよ、って感じですけどね(笑)。 4. さいごに 最後までお読みいただきありがとうございました。 今回の記事のようにこれまで学習した内容について、徐々にアウトプットしていきたい。 自分がアウトプットした内容が誰かの役に立てば幸いです。 余談ですが... 今回この記事で扱った「繰り返し処理」というのは、プログラミング技術発達の過程で生まれた「構造化プログラミング」から優位的に導入されました。プログラムを遂行する三大ロジックのうちの一つです。 「繰り返し処理」の他には、「順次進行」「条件分岐」があります。 「順次進行」は命令を上から実行するロジック、基本的なロジックですね。 「条件分岐」は何らかの判定を行い、その結果で次の命令を決めるロジック。rubyだとif文とかですね。 命令を実行するロジックという切り口では、「繰り返し処理」は中々重要な立ち位置にあるそうですね。 参考 「オブジェクト指向でなぜつくるのか」/ 平澤 章 / 日経BP社 / 2004年6月初版
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

シンプルでセキュアなRails on ECSのTerraformによる実装

TL;DR 3行で。 シンプルで セキュアな Ruby on Rails on ECS by Terraform を作りました。 目次 はじめに 本構成のテーマ リポジトリ アーキテクチャ Terraform側のTips Rails側のTips さらなるカスタマイズ はじめに ここ数年、業務やプライベートで様々なパターンのRuby on Rails + AWS ECS構成を構築してきました。 例えば、構築したことがるパターンを要素ごとに列挙するとざっと以下のようになります。 フロント部分 ALBのみ CloudFront -> ALB assetsのみCloudFront ECS 起動タイプEC2 起動タイプFargate 2種の混在 デプロイパイプライン 全部GitHub Actions Capistrano + ecs-cli CodePipeline + CodeBuild CodePipeline + CodeBuild + CodeDeploy 他にも、 「本番環境/テスト環境でAWSアカウントが分離しているかどうか」、 「ログにfirelens使うかCloudWatch Logs使うか」、 「ECSタスクとIAM Roleをどう対応させるか」、 「ECRへの接続にVPC Endpointを使うか」等、 細かい点を上げるといろんな構成があります。 これらの組み合わせは無限大ですが、いくつも構築してみて、やっとしっくり来る組み合わせに行き着いたので、その構成とTerraformによる実装を紹介したいと思います。 本構成のテーマ テーマは シンプル & セキュア です。 しっくり来る組み合わせに行き着いたと言っても、サービスの要件次第では構成は微妙に変えていかなければいけません。 なので、要件に合わせて柔軟にカスタマイズできる構成であることと、構築にかかるコストをなるべく小さくしたいことから、シンプルな構成であることを目指して検討しました。 また、せっかくきちんと設計し実装を公開する以上、「○○やってみた」レベルの実装ではなく、実際に本番環境での運用に耐えうるレベルの設計・実装を目指しました。 具体的には、Well-Architected FrameworkやSecurity Hubで紹介されるベストプラクティスに則り設計しています。(いちおう筆者は AWS Certified Solutions Architect - Professional と AWS Certified Security - Specialty を保有してます) リポジトリ 実装したものは以下のリポジトリで公開しています。 リポジトリはRails側とTerraform側(インフラ)で分けています。 これはおそらく最も一般的なリポジトリ分割パターンかなと思います。 担当エンジニア、リリースサイクル、CIなどの観点から自然とこの粒度のリポジトリになるでしょう。 余談: ぶっちゃけ、この実装があればフリーランスの案件普通にこなせるんじゃないかなと思ってる。 よく「サービスとCI/CDパイプラインの構築」という案件目にするので。 Terraform Rails アーキテクチャ この構成のアーキテクチャに関して図を交えながらいくつかの観点で紹介していきます。 全体概要 CloudFront, ALB, ECS, Auroraを利用したシンプルなアーキテクチャです。 CloudFrontにはWAFを設定しXSSやSQLi等の攻撃をブロックします。 CloudFront, ALBのドメインはRoute53で管理しています。 いずれもMulti-AZレベルの可用性をもつように設計しているため、任意のAZで障害が発生してもサービスは継続できる構成です。 ECSは当然起動タイプFargate一択です。 令和の時代にサーバー管理はしたくないんじゃ。 え? rails c したい? Execute Command でOK! ネットワーク VPC内のSubnetは上記のように用途ごとに細かく分けています。 Security Group Security Groupも用途ごとに分けた設計になっています。 Security Group間のインバウンドルールのソースに別Security Groupを指定することで、最低限の通信しか許可していません。 また、VPC Endpointを設定することでインターネットを経由せずにS3やECRと通信できるように設定しています。 CDパイプライン CDパイプラインは上記のようにECRへのイメージプッシュをトリガーに、CodePipelineで実行されます。 ECRへのプッシュをトリガーにすることで、Railsアプリケーション開発者とインフラエンジニアの自然な責任境界を実現できます。 参考:ECS用のCDパイプラインに対する考察 イメージビルドはGitHub Actions側で行います。 rails db:migrate をCodeBuildで実行します。 デプロイはCodeDeployを採用しており、全トラフィックを一括で新バージョンに切り替えることで、デプロイ時にassets参照が404エラーにならないように配慮しています。 Terraform側のTips モジュール利用 これはTipsというより設計方針です。 Terraformでは公開されているものや自作のモジュールを使うことで実装を簡潔にできたりします。 特にAWS公式が提供しているとても便利で、短い記述でAWSのリソースを作成することができます。 https://registry.terraform.io/namespaces/terraform-aws-modules ですが、モジュール側が対応していないため、TerraformやProviderのバージョンを更新できないといった場面に出くわすこともあります。 また、モジュールでは制御できないが、普通にaws providerを使った実装の場合は設定可能な属性がある場合もあります。 aws providerの最新バージョンで追加されたリソースをいち早く使いたいケースなんかもあります。 ですので、多少工数はかかりますが、メンテナンスコスト等を加味してAWS公式モジュール等は利用せずに、aws providerをそのまま使った実装としています。 もちろん簡潔な記述や細かい知識不要で構築できる公式モジュールにも大きなメリットはあるので、「モジュール利用は駄目!」などと言うつもりはなく、使い分けだと思います。 セキュリティ対策 Well-Architected FrameworkやSecurity Hubによるベストプラクティスなどに従い、下記のセキュリティ対策を実施しています。 CloudFrontを前段に配置することによるDDoS対策 WAFのマネージドルールによるXSSやSQLi等の攻撃のブロック RDSの暗号化設定 SSMパラメータストアを利用した秘匿情報の管理 ECRによる脆弱性スキャン tfsecによるTerraformコード静的解析によるセキュリティ指摘 ロギング 以下のログ設定を行っています。 可能な限りログは取得するべきです。(このリポジトリではWAFのログは取得していませんが) CloudFront ALB VPC Flow Log S3 Access Log このリポジトリではあくまでRails on ECS部分の構成がメインなので、AWSアカウントを作成した際に設定すべき、下記のセキュリティ設定は含んでいません。 GuardDuty CloudTrail IAMパスワードポリシー EBSのデフォルト暗号化 証明書とDNSレコード このリポジトリでは、route53.tf の実装のようにRoute53で取得したドメインを利用しています。 もし、ドメインを別の方法で取得している場合でも、Route53から設定できるように移譲するのが良いでしょう。 Route53でドメインを設定できると、acm.tf のようにACMの検証などもスムーズに実装できます。 ACM証明書とその検証DNSレコードの実装: resource "aws_acm_certificate" "main" { domain_name = local.domain subject_alternative_names = ["*.${local.domain}"] validation_method = "DNS" } resource "aws_acm_certificate_validation" "main" { certificate_arn = aws_acm_certificate.main.arn validation_record_fqdns = [for record in aws_route53_record.acm : record.fqdn] } カスタムヘッダーによるALBへのリクエストをCloudFront限定にする CloudFrontで以下のカスタムヘッダーを設定します。 resource "aws_cloudfront_distribution" "main" { origin { custom_header { name = "x-pre-shared-key" value = data.aws_kms_secrets.secrets.plaintext["cloudfront_shared_key"] } # ... } # ... } ALBのリスナールールで、上記ヘッダーが付与されたリクエストのみ、ECSへ流すように設定します。 resource "aws_lb_listener_rule" "app_from_cloudfront" { listener_arn = aws_lb_listener.app.arn priority = 100 action { type = "forward" target_group_arn = aws_lb_target_group.app["blue"].arn } condition { http_header { http_header_name = "x-pre-shared-key" values = [data.aws_kms_secrets.secrets.plaintext["cloudfront_shared_key"]] } } # NOTE: Ignore target group switch lifecycle { ignore_changes = [action] } } このように設定することで、CloudFrontを経由せずに直接ALBへリクエストする経路を塞ぐことができます。 CodeDeployによりローリングアップデートを回避する 複数台のサーバーにRailsをローリングアップデートでデプロイすると、一部のjsやcss等のassets系ファイルの参照が404エラーになる瞬間が発生します。 参考: メドピア AWS勉強会 ECS編 これは、新サーバーからhtmlを取得し、その中のjsを旧サーバーへ取得しにいくと発生します。 この問題を回避する方法は様々ありますが、ここではシンプルにCodeDeployを利用しローリングアップデートを実施しないことで回避しています。 deployment_config_name = "CodeDeployDefault.ECSAllAtOnce" を指定することで、全リクエストを同時に新バージョンへ切り替えることができます。 初期構築時のダミー用ECSタスク定義 プロダクトの構築初期段階など、まだRailsアプリが用意できていない場合に最低限ヘルスチェックにだけ合格する軽量イメージとそれを利用したECSタスク定義が欲しくなります。 medpeer/health_check イメージを使うことで指定したパスのヘルスチェックに合格するだけのECSタスク定義が作成できます。 resource "aws_ecs_task_definition" "app" { # ... # NOTE: Dummy containers for initial. container_definitions = <<CONTAINERS [ { "name": "web", "image": "medpeer/health_check:latest", "portMappings": [ { "hostPort": 3000, "containerPort": 3000 } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "${aws_cloudwatch_log_group.app.name}", "awslogs-region": "${local.region}", "awslogs-stream-prefix": "web" } }, "environment": [ { "name": "NGINX_PORT", "value": "3000" }, { "name": "HEALTH_CHECK_PATH", "value": "/health_checks" } ] } ] CONTAINERS } Availability Zoneの繰り返しにはfor_eachを使う 将来、AZが追加されるケースを考慮して、Subnet等AZの数だけ作成するリソースは for_each を利用します。 例えばpublic subnetは以下のように作成します。 resource "aws_subnet" "public" { for_each = local.availability_zones vpc_id = aws_vpc.main.id availability_zone = each.key cidr_block = cidrsubnet(local.vpc_cidr, 8, local.az_conf[each.key].index) tags = { Name = "${local.name}-public-${local.az_conf[each.key].short_name}" } } ここでは、以下のように az_conf という変数を定義しています。 locals { az_conf = { "ap-northeast-1a" = { index = 1 short_name = "1a" } "ap-northeast-1c" = { index = 2 short_name = "1c" } "ap-northeast-1d" = { index = 3 short_name = "1d" } } } cidrsubnet関数 を利用することで、一定ルールでのCIDRの管理を楽に実現できます。 Subnetのidのリストが欲しい場合は以下のコードで生成できます。 values(aws_subnet.public)[*].id KMSを利用した秘匿情報管理 秘匿情報はKMSで暗号化し、このリポジトリに含めることでシンプルに管理できます。 aws_kms_secrets を利用することで、複合はTerraformがplan時apply時に行います。 利用方法は以下のとおりです。 KMSを使い秘匿情報を暗号化する。 $ aws kms encrypt --key alias/terraform --plaintext "secret_value" --output text --query CiphertextBlob aws_kms_secrets データリソースを作成する。 locals { secrets = { foo = "encrypted_value" } } # NOTE: register all secrets data "aws_kms_secrets" "secrets" { dynamic "secret" { for_each = local.secrets content { name = secret.key payload = secret.value } } } SSMパラメータストアやDBのパスワード等、復号した値を利用したい部分に以下のコードを記述する。 data.aws_kms_secrets.secrets.plaintext["foo"] # set decrypted value Rails側のTips イメージビルド GitHub Actionsでイメージビルドを行います。 docker/build-push-action を利用することで、イメージビルドとプッシュを簡潔に記述できます。 Dockerfile の実装のようにマルチステージビルドを利用する実装になっています。 最終的なイメージ以外にbuilderステージもECRをプッシュし、 --cache-from オプションに指定することで、次のビルド時にキャッシュとして利用できるようにしています。 # cache-from に前回の builder ステージのイメージを指定 - name: Build and Push uses: docker/build-push-action@v2 env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} with: push: true cache-from: | type=registry,ref=${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:builder tags: | ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }} ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:latest # 今回のbuilderイメージを保存 - name: Save builder cache uses: docker/build-push-action@v2 env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} with: target: builder push: true build-args: | BUILDKIT_INLINE_CACHE=1 tags: | ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:builder ヘルスチェック okcomputer gem を利用することで、ヘルスチェックをシンプルに設定できます。 /health_checks/all にアクセスすることで、追加で指定したDB等へのヘルスチェックも行えるため疎通確認や障害の調査などで非常に便利です。 環境変数から読み込む 以下は環境変数から読み込むようにすることで、特定のインフラとの密結合を避けています。 データベースの接続情報: DATABASE_URL データベースの接続情報(リードレプリカ): READER_DATABASE_URL また、秘匿情報をセキュアに管理するため、 RAILS_MASTER_KEY も環境変数で受け取るようにしています。 さらなるカスタマイズ 今回はさまざまな構成のベースにできるようなシンプルなアーキテクチャとしました。 要件次第ではいろいろとカスタマイズが必要でしょう。 例えば以下のようなカスタマイズはよく要求としてあがってきます。 RedisとSidekiqを利用したWorkerサービスの追加 GitHub Deployment APIを用いたデプロイの管理 CodeDeployによるカナリアリリースの導入 イメージ軽量化によるビルド・デプロイ時間とスケール速度の改善 RailsのレスポンスをCloudFrontでキャッシュして高速化 フロントエンドをSPA構成で配信し、RailsはAPIに徹する どの構成も基本は今回紹介したアーキテクチャをベースに実現できるはずです。 (もちろん今回のアーキテクチャ以外をベースにしたほうが構築が楽なケースも当然あります) さいごに シンプルでセキュアなRails on ECS構成、参考になりましたでしょうか? 特に個人的には、この構成のCDパイプラインの境界とシンプルさが気に入っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]CSRF tokenでエラー対応方法

[Rails]CSRF tokenでエラー対応方法 CSRF tokenでエラー Can't verify CSRF token authenticity. エラー対応方法 ①CSRF tokenをHTMLソースコードに追加する。 <!DOCTYPE html> <html> <head> <title> タイトル </title> <%= csrf_meta_tags %> <<==これを追加する。 <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> </head> ②ajaxPrefilterにcsrf-tokenを格納する。 $.ajaxPrefilter(function(options, originalOptions, jqXHR) { var token; if (!options.crossDomain) { token = $('meta[name="csrf-token"]').attr('content'); if (token) { return jqXHR.setRequestHeader('X-CSRF-Token', token); } } }); ③headersにcsrf-tokenを格納する。 $(function() { $.ajax({ type: 'PATCH', url: '/comments/' + commentId, data: { comment: { body: body }, }, headers: { 'X-CSRF-Token' : $('meta[name="csrf-token"]').attr('content') }, dataType: 'json', }).done(function (data) { }).fail(function () { }); }) });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの:(コロン)の種類

Rubyの:(コロン)の種類について Rubyを学習していて当たり前のように使っていた:(コロン)。 たくさんの種類がある事を知らずに何となくで使用していました。 基本を押さえておけば、後々の学習にも役立つと思いまとめました。 この記事はUdemyの以下の講座を参考にまとめさせて頂きました。 無料で公開されているなので、気になる方は是非。 Udemy :(コロン)の種類 シンボル qiita.rb :asdf 文字列の先頭にコロンがある場合は、シンボルのコロン。 シンボルは文字列の上位版。 シンボルは重複を許さず、シンボルの中身が同じであれば、コンピュータ内部では1箇所に保存され、処理速度がちょっと速くなったりするらしい。 文字列の場合は、中身が同じであってもコンピュータ内部では、別々の保存領域が必要。 シンボルをキーにしたハッシュの定義 まずはハッシュがどういうものかというと、 qiita.rb hash1 = { キー1 => バリュー1, キー2 => バリュー2, キー3 => バリュー3 } キーとバリューがセットになっているのがハッシュ。 ちなみに配列は重複した値を[]内に入れる事ができますが、ハッシュは重複(duplicated)したキーで違う値を定義することはできません。 ここまでの内容でわかったことは、 ・ハッシュのキーはキー同士の重複を許さない ・シンボルもコンピュータの内部上、重複を許さない この重複を許さない性質をもつハッシュとシンボルは非常に相性がいい。 そしてハッシュは次のように定義ができる。 qiita.rb hash1 = { "key1" => "value1", "key2" => "value2", "key3" => "value3" } hash2 = { :key => "value" } /これが正式な書き方/ hash2 = { key: "value" } /これはシンボルをキーにした省略系の書き方/ つまり、{}の中の後ろコロンは、シンボルをキーにしたハッシュのコロンというわけです。 メソッドのキーワード引数 メソッドとは、よく自動販売機に例えられます。 自動販売機は、お金を入れてボタンを押すとジュースが出てきます。 メソッドは、引数を渡してメソッドを実行すると戻り値が返ってきます。 何かを入れて、何を実行すると、何かが返ってくるものがメソッド。 キーワード引数を使用したメソッドは以下のようになります。 qiita.rb /普通の引数とメソッド/ def add(hikisu1, hikisu2) kotae = hikisu1 + hikisu2 return kotae end puts add(3, 4) /キーワード引数とメソッド/ def add(hikisu1:, hikisu2:) kotae = hikisu1 + hikisu2 return kotae end puts add(hikisu: 3, hikisu2: 4) そしてキーワード引数には以下の特徴があります。 引数に見出し(タイトルをつける事ができる) キーワード引数は順番が入れ替わってもOK 通常の引数と一緒に使える メソッドの呼び出しは括弧を省略できる railsを使っているとこんなコードをよく見かけます。 qiita.rb render ("users/user", user: @user) /()は省略可/ 1つ目は普通の引数ですが、user: @userの部分はキーワード引数です。 メソッドを呼び出す時の引数として後ろコロンが使われていたら、それはキーワード引数。 よく見るコードの解説 ここまで理解ができればきっと次のコロンもどんなものかがわかるはず。 qiita.rb validates :content, presence: true, length: { maximum: 50 } validatesというメソッドがあって、それに対する引数が記述されています。 以下がvalidatesメソッドに渡されている引数たち。 qiita.rb :content →先頭にコロンがあるのでシンボル! presence: true →validatesメソッドの引数部分で後ろコロンなのでキーワード引数 presence:というキーワード引数は、trueですよって渡してる(presenceという見出しをつけてる) length: →validatesメソッドの引数部分で後ろコロンなのでキーワード引数 { maximum: 50 } →波括弧があるからこれはハッシュ ハッシュの内側で後ろコロンは、シンボルをキーにしたハッシュ。 続いてこちら qiita.rb <%= link_to '削除する', @post, method: :delete, data: { confirm: '本当に削除してOK?'} %> 細かく分解していくと、 qiita.rb link_to →これがまずメソッドになります。そしてlink_to以下はメソッドに与えられた引数。 '削除する' @post →それぞれ第一引数、第二引数。 method: →キーワード引数 :delete →シンボル :data →キーワード引数 { confirm: '本当に削除してOK?'} →波括弧はハッシュ。 ハッシュの内側に後ろコロンがあるから、シンボルをキーにしたハッシュ。 まとめ 先頭にコロンがあったらシンボル ハッシュの内側に後ろコロンが会ったら、シンボルをキーにしたハッシュ メソッドの引数部分に後ろコロンがあったら、メソッドのキーワード引数
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails6】ゲストユーザー機能の実装例

エンジニア転職でポートフォリオとなるアプリを開発する際、企業の方がアプリを確認しやすいようにユーザー登録しなくてもよいゲストユーザー機能の実装が望ましいという話をよく聞きます。 そこで今回は、ぼくが自分のポートフォリオで実装したゲストユーザー機能の例を紹介します。 前提 deviseを使ってユーザー登録機能が実装されている Rails 6.0.3.5 動作確認 「ゲストユーザーとしてログインする」ボタンをクリックすると、ゲストユーザーでアプリにログインできます。 ルーティングの設定 以下のようにパスとアクションを組み合わせを定義します guest_log_inアクションはこのあとコントローラーで自作で定義するアクションです。 config/routes.rb devise_scope :user do post 'users/guest_log_in', to: 'users/sessions#guest_log_in' end アクションの定義 以下のようにguest_log_inアクションを定義します。 app/controllers/users/sessions_controller.rb class Users::SessionsController < Devise::SessionsController def guest_log_in user = User.guest sign_in user redirect_to calendars_path, notice: 'Logged in as a guest' end end アクションの中で使用しているsign_inメソッドは、deviseが提供する機能で、サインイン状態にします。 ちなみにdeviseの公式GitHubのdevise/app/controllers/devise/sessions_controller.rb内のDevise::SessionsController#createがsign_inメソッドです。※調べたことを書いているのですが、もし間違っていたらご指摘ください。ソースコードはこちら。 またguestメソッドはこのあとモデルで定義します。 モデルの定義 モデルにはクラスメソッドであるguestメソッドを定義します。 app/models/user.rb class User < ApplicationRecord def self.guest find_or_create_by!(email: 'guest@guest.mail') do |user| user.name = 'ゲストユーザー' user.password = SecureRandom.urlsafe_base64 end end end find_or_create_by!メソッドはRailsに用意されているメソッドで、「条件を指定して初めの1件を取得し、1件もなければ作成」します。 この場合、emailカラムがguest@guest.mailである初めのレコードを取得し、なければnameカラムが「ゲストユーザー」、passwordはランダムな値を生成してくれるRubyモジュールであるSecureRandomを使って生成し、新しいレコードを作成します。 ぼくのアプリではusersテーブルにnameカラムがあるため、nameの値を指定しています。 SecureRandomについては以下を参考にしてください。 Ruby 3.0.0 リファレンスマニュアル SecureRandomモジュール リンクボタンの作成 リンクボタンは任意の場所に以下のように記述します。 <%= link_to "Log in as a guest", users_guest_log_in_path, method: :post %> ゲストユーザーの編集ができないようにするアクション ポートフォリオを公開した時にゲストユーザーを編集可能な状態していると、イタズラを受けてしまう危険性もあるので、編集できないようにするアクションを定義します。 app/controllers/users/registrations_controller.rb before_action :ensure_normal_user, only: :edit def ensure_normal_user if resource.email == 'guest@guest.mail' redirect_to user_path(@user.id), notice: 'You can not edit guest user' end end 参考資料 deviseの公式GitHub Railsドキュメント find_or_create_by Ruby 3.0.0 リファレンスマニュアル SecureRandomモジュール 【Qiita】簡単ログイン・ゲストログイン機能の実装方法(ポートフォリオ用)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む