- 投稿日:2020-03-28T23:50:35+09:00
【Ruby on Rails】Ajax通信で特定の要素を変更する
はじめに
RailsのAjax通信使って特定の要素を編集・更新する方法について記載します。
例で簡単なカテゴリー一覧画面の1つのカテゴリーの名前を更新する処理を使います。バージョン
- ruby 2.6.3
- rails 5.2.1
画面概要
この右のほうにある鉛筆マークの編集アイコンボタンを押すと、該当の行のカテゴリー名がフォームに変わります。
変更後のカテゴリー名にデータが更新され、表示を非同期で変更します。
例では、「交通」→「交通費」に変更します。
処理概要
要素にAjax通信するオプションを追加
link_toにremote: true
オプションをつけます。Ajax通信するためのルーティングを追加
コントローラやモデルでオブジェクトの取得または更新
追加したアクションのためのJavaScript用のviewファイルを用意し、画面の該当の要素を変更するためのJavaScriptを記載
実装前
実装前の主要なコード部分は以下になります。
app/controllers/categories_controller.rbdef index @categories = Category.where(user: current_user).order(:created_at) endapp/views/categories/index.html.erb<ul class="todo-list" id="own-categories"> <% @categories.each do |category| %> <li id="category-list-id-<%= category.id %>"> <%= render 'a_category_list', category: category %> </li> <% end %> </ul>app/views/categories/_a_category_list.html.erb<span class="handle ui-sortable-handle"> <i class="fa fa-ellipsis-v"></i> <i class="fa fa-ellipsis-v"></i> </span> <span class="text"><%= category.name %></span> <%= category.common_mark %> <div class="tools"> <%= link_to edit_category_path(category), class: "text-redpepper space-left" do %> <i class="fa fa-lg fa-edit"></i> <% end %> <%= category.common_btn %> </div>カテゴリー名を編集するフォームに変更するAjax処理の実装
鉛筆マークの編集アイコンボタンを押下して、カテゴリー名の要素をフォーム要素に変更する処理を記載していきます。
※注意:説明しないclass属性やid属性などがありますが、Ajax通信処理とは関係ありません。この処理には
gem rails-ujs
が必要なので追加してbundle install
します。Gemfilegem 'rails-ujs'1. Ajax通信するオプションを追加
編集アイコンボタンにremote: trueを仕込む。
app/views/categories/_a_category_list.html.erb<%= link_to edit_category_path(category), remote: true, class: "text-redpepper space-left" do %> <i class="fa fa-lg fa-edit"></i> <% end %>2. Ajax通信するためのルーティングを追加
Ajax通信するときでも、ルーティングのresourcesメソッドが柔軟に対応してくれます。
ここでは、editアクションを追加します。config/routes.rbresources :categories, only: [:index, :edit]3. コントローラでオブジェクトの取得
app/controllers/categories_controller.rbdef edit @category = Category.find(params[:id]) end4. 追加したアクションのためのJavaScript用のviewファイルを用意し、画面の該当の要素を変更するためのJavaScriptを記載
editアクションなので、
app/views/categories/edit.js.erb
を作成します。app/views/categories/edit.js.erbid = "<%= @category.id %>"; target = document.querySelector(`#category-list-id-${id}`); html = "<%= j(render partial: 'form', locals: { category: @category }) %>"; target.innerHTML = html;app/views/categories/_form.html.erb<%= form_with(model: category) do |form| %> <div class="row"> <div class="col-xs-10"> <div class="input-group input-group-sm"> <%= form.text_field :name, value: category.name, required: true, class:"form-control", max: 15 %> <span class="input-group-btn"> <%= form.submit submit_btn_letters, class: "btn btn-brown" %> </span> </div> </div> <div class="col-xs-1"> <%= category.cancel_btn %> </div> </div> <% end %>これで、カテゴリー名を編集するためのフォームが表示できます。
カテゴリー名を更新するAjax処理の実装
編集フォームに変更できたので、次に、変更したい文字「交通費」に変更して、
更新ボタンを押下し、更新後のカテゴリー名を表示する処理を記載していきます。1. Ajax通信するオプションを追加
form_withに
remote: true
オプションを追加します。
(明示的に記載しなくてもデフォルトでremote: trueにはなっている。)app/views/categories/_form.html.erb<%= form_with(model: category, remote: true) do |form| %> <!-- 省略 --> <% end %>2. Ajax通信するためのルーティングを追加
updateアクションを追加
config/routes.rbresources :categories, only[:index, :edit, :update]3. コントローラでオブジェクトの更新
カテゴリー名を「交通費」で更新します。
app/controllers/categories_controller.rbdef update @category = Category.find(params[:id]) @category.update(category_params) end private def category_params params.require(:category).permit(:name, :is_common) end4. 追加したアクションのためのJavaScript用のviewファイルを用意し、画面の該当の要素を変更するためのJavaScriptを記載
編集フォームを更新したカテゴリー名に変更する処理を行います。
updateアクションなのでapp/views/categories/update.js.erb
を作成します。app/views/categories/update.js.erbid = "<%= @category.id %>"; target = document.querySelector(`#category-list-id-${id}`); html = "<%= j(render partial: 'categories/a_category_list', locals: { category: @category }) %>"; target.innerHTML = html;おわりに
JavaScriptでAjax処理を書いてもいいのですが、シンプルなAjax通信はRailsであれば簡単にできます。
ただ、〇〇.js.erb
の書き方は癖が強いので要注意です。
- 投稿日:2020-03-28T23:00:55+09:00
Docker上のRailsアプリにWebpackerを導入してみる
はじめに
この記事では、既存のRailsアプリにWebpackerを導入する手順を書いていきます。
WebpackerやWebpackを使用したことがない方向けです。開発環境
Rails 5.2.3
ruby 2.5
mariadb 10.1
dockerWebpackerとは
Railsでwebpackを使用するためのgemパッケージです。
複数のjs,css,画像などを一つにまとめることにより、
ファイルの取得にかかる時間を短縮できるというメリットがあります。実装
gemのインストール
まずアプリにgemパッケージをインストールします。
Gemfilegem 'webpacker', '~> 4.x'Gemfileを更新したら、
bundle install
します。yarnのインストール
Webpackerは
yarn
というnode.jsのパッケージマネージャを使用するそうなので、
Dockerにyarnをインストールするためのコマンドを追記します。DockerfileRUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get update && apt-get install yarnWebpackerの起動
docker-compose.ymlwebpacker: build: . environment: NODE_ENV: development RAILS_ENV: development WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 command: ./bin/webpack-dev-server ports: - '3035:3035'アプリ用のコンテナにWebpackerホスト名の環境変数を追加します。
docker-compose.ymlweb: build: context: . dockerfile: Dockerfile command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" tty: true stdin_open: true depends_on: - db ports: - '3000:3000' volumes: - .:/usr/src/app # 以下を追記 environment: WEBPACKER_DEV_SERVER_HOST: webpackerこれで
docker-compose up --build
をすると、Webpackerが起動してくれます。
このあたりは、公式ドキュメントにわかりやすく記載されています。基本的な使い方
Webpackerをインストールすると、
config/webpacker.yml
が作成されますconfig/webpacker.ymldefault: &default source_path: app/javascript source_entry_path: packs # この配下のファイルをエントリーポイントとして読み込む ...構成app/javascript: ├── packs: │ # ここに置いたファイルが読み込まれる │ └── application.js └── javascripts: │ └── application.js └── stylesheets: └── application.scss
javascripts/application.js
,stylesheets/application.css
を
アプリケーション全体に適用させたい場合は以下のように書きます。packs/application.jsimport '../javascripts/application.js' import '../stylesheets/application.scss'app/views/layouts/application.html.erb<%= javascript_pack_tag 'applocation' %> <%= stylesheet_pack_tag 'application' %>これでビューに反映することができます。
ちなみに、今回はWebpackerの実行コマンドに
./bin/webpack-dev-server
を指定しているため、
jsファイルの中身を更新するとブラウザをリロードすることなく自動的に変更を反映してくれます。
ただ、ファイル自体を追加するなど、構成を変更した場合はrailsを再起動する必要があります。ページ毎に適用するファイルを分ける場合
構成app/javascript: ├── packs: │ └── application.js └── javascripts: │ └── application.js │ └── tasks: │ └── application.js │ # 実際の処理が書かれているjavascriptファイル │ └── index.js │ └── show.js └── stylesheets: └── application.scssエントリーポイントである
pakcs
配下にjsファイルを読み込むためのエントリーファイルを作成します。構成app/javascript: ├── packs: │ └── application.js │ # 追加 │ └── tasks: │ └── index.js │ └── show.js └── javascripts: │ └── application.js │ └── tasks: │ └── index.js │ └── show.js └── stylesheets: └── application.scss追加したファイルにインポート文を記述します。
packs/tasks/index.jsimport '../../javascripts/tasks/index.js'packs/tasks/show.jsimport '../../javascripts/tasks/show.js'それぞれのページでjavascriptを呼び出します。
app/views/tasks/index.html.erb<%= javascript_pack_tag 'tasks/index.js' %>app/views/tasks/show.html.erb<%= javascript_pack_tag 'tasks/show.js' %>そのほか
Bootstrap4を適用させる
Webpackerを導入したところ、Bootstrapが効かなくなりました。
こちらのサイトを参考に設定。
https://qiita.com/rhistoba/items/f724dae231d7e28bf477confirmを動作させる
Webpackerを導入したところ、confirmダイアログが効かなくなりました。
こちらのサイトを参考に設定。
https://qiita.com/mokuo/items/a50a27a83c3328c116a7Github ActionsでWebpackerを動作させる
Github Actionsで用意されているUbuntuにはyarnがデフォルトで入っているようですが、
そのままだとエラーが出てしまいました。
yarn upgrade
を追記することで動作するようになりました。.github/workflows/ruby.yml- name: Compile with Webpacker run: | yarn upgrade bundle exec rake webpacker:compile env: NODE_ENV: test最後に
ご指摘、コメントお待ちしております。
- 投稿日:2020-03-28T20:11:06+09:00
指定したエリアだけを印刷する処理
こんな場面で使えます。
例えば、ユーザーに何らかのアンケートに回答をしてもらうサイトで
ユーザーが自分の回答結果の部分だけを印刷したい場合などに使えます。
普通の印刷では余計な箇所も入ってくるので、必要なエリアだけを印刷できるようにします。
※実際の挙動が確認できるwebアプリのURLを一番下に載せてますので、そちらもご確認ください。
実装の流れ
1:印刷ボタンを作成
2:プリントしたいエリアの取得の指定
3:2のエリア全体のコピーを作成
4:3以外を非表示にする処理を作成
5:印刷ダイアログの呼び出し➡︎印刷する
6:3で作成した印刷用エリアを削除 ・ 4で非表示にした要素を表示する実装スタート
1:印刷ボタンを作成
print.html<button class="print-btn"> <span>結果だけを印刷する</span> </button>2:プリントしたいエリアの取得
プリントしたい範囲を以下の「divタグ」で囲む。
print.html<div class="print-area"> <!-- この中に要素を入れる。印刷しないものは、後で非表示にする処理で消します。 --> </div>3:2のエリア全体のコピーを作成
1:プリントしたい範囲を取得
2:プリント用の要素「#print」を作成。
3:2の子要素に1を入れる。print.js$(function(){ //個別印刷ボタンをクリックした時 $('.print-btn').on('click', function(){ //プリントしたいエリアの取得 var printArea = document.getElementsByClassName("print-area"); //プリント用の要素「#print」を作成し、上で取得したprintAreaをその子要素に入れる。 $('body').append('<div id="print" class="printBc"></div>'); $(printArea).clone().appendTo('#print'); //この下に、以降の処理が入ります。 }); });4:3以外を非表示にする処理を作成
print.js//プリントしたいエリア意外に、非表示のcssが付与されたclassを追加 $('body > :not(#print)').addClass('print-off');print.css.print-off { display: none; }5:印刷ダイアログの呼び出し➡︎印刷する
print.jswindow.print();6:3で作成した印刷用エリアを削除 ・ 4で非表示にした要素を表示して終了です。
print.js//window.print()の実行後、作成した「#print」と、非表示用のclass「print-off」を削除 $('#print').remove(); $('.print-off').removeClass('print-off');終わりに。
いかがでしょうか。
印刷したいエリアだけを取り出せるのでユーザーにとって不要なエリアが無くなり使い勝手の良いページが作成できると思います。
自作の家庭用備蓄をチェックできるアプリでも、チェックした備蓄情報を印刷できるようにしてありますので
良かったら作業の合間にそちらを見ていただけると具体的な挙動を確認していただけます。
https://saku2stockcheck.herokuapp.com/では、最後まで見ていただいてありがとうございました!
- 投稿日:2020-03-28T19:48:57+09:00
AWS SNS から送られてくる POST リクエストは text/plain なので body パラメーターをパースするときは注意する
概要
AWS SNS から来る POST リクエストの body パラメーターをパースしようとしたときに、上手く行かなくてはまったので、解決策を共有します。
解決策
AWS SNS は HTTPS POST リクエストでアプリケーションにメッセージを送信してくれます。
参考記事: 受信者が HTTP/S エンドポイントの場合のシステム間メッセージングに Amazon SNS を使用するその際に、body パラメーターにメッセージの内容が入っているのですが、Rails の params メソッドを使用してもパラメーターの中身を取得できませんでした。
class SnsController < ApplicationController def message params # メッセージがない end endリクエストの中身を調べたところ、content-type に text/plain が設定されていたことが原因でした。
もし content-type に application/json が設定されていれば、パラメーターを params メソッドで取得できたはずです。なので、下記のようにすることででパラメーターを取得できます。
class SnsController < ApplicationController def message raw_body = request.body.read body_params = JSON.parse(raw_body) end endメッセージの文章はあくまで単純なテキストで、そのテキストが json の形式になっていることもあれば XML かもしれないし、あるいは何の形式にも当てはまらないテキストかもしれない、というのが AWS SNS の考え方なのだと思います。
考えてみれば AWS SNS はメッセージを送信するのが役割で、そのメッセージ文字列の形式を固定するのは責務の対象外なんだなと納得しました。
- 投稿日:2020-03-28T18:35:06+09:00
【Ruby on Rails】Rails超入門1!マジで凄い!scaffoldで恐竜登録アプリ作成1
1.scaffoldとは
Webアプリケーションに必要なビュー(画面)、コントローラー(制御部分)、モデル(データベース)をコマンド一つで提供してくれるコマンド。
さらにwebアプリに共通するCRUDを提供してくれる。2.scaffoldによるアプリの作成
(1)rails new 作成したいアプリ名
でアプリケーションを作成する。(2)下記コマンドをアプリケーションに移動して入力する。
rails generate scaffold モデル名 カラム名1:データ型1 カラム名2:データ型 2 …
例)
rails generate scaffold dinosor2 dname0:string dtype0:string sname1:string stype1:string sname2:string stype2:string hflg:booleanここで、dname0:作成恐竜名,dtype0:作成恐竜タイプ
sname1:生成元恐竜名1,stype1:生成元恐竜タイプ1,sname1:生成元恐竜名2,stype1:生成元恐竜タイプ2,hflg:ハイブリッドフラグを持つdinosor2テーブルを作成している。下記コマンドを入力すると、scaffoldアプリ実行に必要な様々なファイルが作られる。
c:\RailsInstaller\Ruby2.3.3\rails_app2>rails generate scaffold dinosor2 dname0:string dtype0:string sname1:string stype1:string sname2:string stype2:string hflg:boolean invoke active_record create db/migrate/20200320132820_create_dinosor2s.rb create app/models/dinosor2.rb invoke test_unit create test/models/dinosor2_test.rb create test/fixtures/dinosor2s.yml invoke resource_route route resources :dinosor2s invoke scaffold_controller create app/controllers/dinosor2s_controller.rb invoke erb create app/views/dinosor2s create app/views/dinosor2s/index.html.erb create app/views/dinosor2s/edit.html.erb create app/views/dinosor2s/show.html.erb create app/views/dinosor2s/new.html.erb create app/views/dinosor2s/_form.html.erb invoke test_unit create test/controllers/dinosor2s_controller_test.rb invoke helper create app/helpers/dinosor2s_helper.rb invoke test_unit invoke jbuilder create app/views/dinosor2s/index.json.jbuilder create app/views/dinosor2s/show.json.jbuilder create app/views/dinosor2s/_dinosor2.json.jbuilder invoke test_unit create test/system/dinosor2s_test.rb invoke assets(3)アプリケーションディレクトリに移動してテーブル作成をするために下記コマンドを入力する。
rake db:migrate3.scaffoldアプリの実行(自動生成された画面達)
rails serverでサーバーの起動を確認する。
(1)http://localhost:3000/モデル名s
を入力すると、一覧画面が表示される。(2)[New モデル名]リンクを押すと新規作成画面が表示される。
なんとboolean型のhflgがチェックボックスになっていて、他の入力項目はテキストボックスになっている!!!
(3)データを登録して編集画面を表示する。
4.ルーティングの確認
アプリケーションディレクトリ内で下記コマンドを入力する。
rake routes「erbファイルで記載されているpath(prefix)とURLパターン、contollerでのメソッド
の対応一覧のようなものが確認できる。」(と思う)c:\RailsInstaller\Ruby2.3.3\rails_app2>rake routes Prefix Verb URI Pattern Controller#Action dinosor2s GET /dinosor2s(.:format) dinosor2s#index POST /dinosor2s(.:format) dinosor2s#create new_dinosor2 GET /dinosor2s/new(.:format) dinosor2s#new edit_dinosor2 GET /dinosor2s/:id/edit(.:format) dinosor2s#edit dinosor2 GET /dinosor2s/:id(.:format) dinosor2s#show PATCH /dinosor2s/:id(.:format) dinosor2s#update PUT /dinosor2s/:id(.:format) dinosor2s#update DELETE /dinosor2s/:id(.:format) dinosor2s#destroy c:\RailsInstaller\Ruby2.3.3\rails_app2>5.自動生成されたcontrollerのコード
「各画面のボタンやリンクをクリックしたときに、どのようなDB処理やページを読みこむかを記述してあるコード」
dinosor2_controller.rbclass Dinosor2sController < ApplicationController before_action :set_dinosor2, only: [:show, :edit, :update, :destroy] # GET /dinosor2s # GET /dinosor2s.json def index @dinosor2s = Dinosor2.all end # GET /dinosor2s/1 # GET /dinosor2s/1.json def show end # GET /dinosor2s/new def new @dinosor2 = Dinosor2.new end # GET /dinosor2s/1/edit def edit end # POST /dinosor2s # POST /dinosor2s.json def create @dinosor2 = Dinosor2.new(dinosor2_params) respond_to do |format| if @dinosor2.save format.html { redirect_to @dinosor2, notice: 'Dinosor2 was successfully created.' } format.json { render :show, status: :created, location: @dinosor2 } else format.html { render :new } format.json { render json: @dinosor2.errors, status: :unprocessable_entity } end end end # PATCH/PUT /dinosor2s/1 # PATCH/PUT /dinosor2s/1.json def update respond_to do |format| if @dinosor2.update(dinosor2_params) format.html { redirect_to @dinosor2, notice: 'Dinosor2 was successfully updated.' } format.json { render :show, status: :ok, location: @dinosor2 } else format.html { render :edit } format.json { render json: @dinosor2.errors, status: :unprocessable_entity } end end end # DELETE /dinosor2s/1 # DELETE /dinosor2s/1.json def destroy @dinosor2.destroy respond_to do |format| format.html { redirect_to dinosor2s_url, notice: 'Dinosor2 was successfully destroyed.' } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_dinosor2 @dinosor2 = Dinosor2.find(params[:id]) end # Only allow a list of trusted parameters through. def dinosor2_params params.require(:dinosor2).permit(:dname0, :dtype0, :sname1, :stype1, :sname2, :stype2, :hflg) end end6.自動生成されたviewのコード
(1)新規作成画面
form.html.erbを読みこんでいる。
backリンクで一覧画面に戻る。new.html.erb<h1>New Dinosor2</h1> <%= render 'form', dinosor2: @dinosor2 %> <%= link_to 'Back', dinosor2s_path %>(2)詳細画面
指定した恐竜の情報を表示する画面。
editリンクで編集画面へ、backリンクで一覧画面へ遷移。show.html.erb<p id="notice"><%= notice %></p> <p> <strong>Dname0:</strong> <%= @dinosor2.dname0 %> </p> <p> <strong>Dtype0:</strong> <%= @dinosor2.dtype0 %> </p> <p> <strong>Sname1:</strong> <%= @dinosor2.sname1 %> </p> <p> <strong>Stype1:</strong> <%= @dinosor2.stype1 %> </p> <p> <strong>Sname2:</strong> <%= @dinosor2.sname2 %> </p> <p> <strong>Stype2:</strong> <%= @dinosor2.stype2 %> </p> <p> <strong>Hflg:</strong> <%= @dinosor2.hflg %> </p> <%= link_to 'Edit', edit_dinosor2_path(@dinosor2) %> | <%= link_to 'Back', dinosor2s_path %>(3)編集画面
_form.html.erbを読みこんで表示。
showリンクで詳細画面へ、backリンクで一覧画面へ遷移。edit.html.erb<h1>Editing Dinosor2</h1> <%= render 'form', dinosor2: @dinosor2 %> <%= link_to 'Show', @dinosor2 %> | <%= link_to 'Back', dinosor2s_path %>index.html.erb<p id="notice"><%= notice %></p> <h1>Dinosor2s</h1> <table> <thead> <tr> <th>Dname0</th> <th>Dtype0</th> <th>Sname1</th> <th>Stype1</th> <th>Sname2</th> <th>Stype2</th> <th>Hflg</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @dinosor2s.each do |dinosor2| %> <tr> <td><%= dinosor2.dname0 %></td> <td><%= dinosor2.dtype0 %></td> <td><%= dinosor2.sname1 %></td> <td><%= dinosor2.stype1 %></td> <td><%= dinosor2.sname2 %></td> <td><%= dinosor2.stype2 %></td> <td><%= dinosor2.hflg %></td> <td><%= link_to 'Show', dinosor2 %></td> <td><%= link_to 'Edit', edit_dinosor2_path(dinosor2) %></td> <td><%= link_to 'Destroy', dinosor2, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to 'New Dinosor2', new_dinosor2_path %>(3)_form.html.erb
下記のコードが新規作成、編集画面の元となる_form.html.erbだ。
ここでdname0,dtype0,sname1,stype1,sname2,stype2の入力がテキストボックスに、
hflgの入力部分がチェックボックスになっていることが確認できる。_form.html.erb<%= form_with(model: dinosor2, local: true) do |form| %> <% if dinosor2.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(dinosor2.errors.count, "error") %> prohibited this dinosor2 from being saved:</h2> <ul> <% dinosor2.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= form.label :dname0 %> <%= form.text_field :dname0, id: :dinosor2_dname0 %> </div> <div class="field"> <%= form.label :dtype0 %> <%= form.text_field :dtype0, id: :dinosor2_dtype0 %> </div> <div class="field"> <%= form.label :sname1 %> <%= form.text_field :sname1, id: :dinosor2_sname1 %> </div> <div class="field"> <%= form.label :stype1 %> <%= form.text_field :stype1, id: :dinosor2_stype1 %> </div> <div class="field"> <%= form.label :sname2 %> <%= form.text_field :sname2, id: :dinosor2_sname2 %> </div> <div class="field"> <%= form.label :stype2 %> <%= form.text_field :stype2, id: :dinosor2_stype2 %> </div> <div class="field"> <%= form.label :hflg %> <%= form.check_box :hflg, id: :dinosor2_hflg %> </div> <div class="actions"> <%= form.submit %> </div> <% end %>上記画面を見るとわかるのだが、データを入れるテーブルのコマンド一つでCRUDアプリが本当に簡単に作れるという事だ。(色々な課題はあるけど)
- 投稿日:2020-03-28T18:33:00+09:00
応用 Lesson2 検索力についてのまとめ
応用カリキュラム Lesson2対応 リンク集
エンジニアにとって検索力がとても重要なことを学び、
テキストにあるワードを検索してみました。初投稿
まずQiitaの使い方からよく分かりませんので、その辺は徐々に。
とりあえず自身のアウトプットとして書きます。
どなたかの何かのお役に立てたら光栄です。では、ワードごとにリンクを貼っていきます。
あえて、ワードに対する答えは載せません。
あくまでリンク集です。(自分のためにも…)【リンク集 ~Link Page〜】
1. jQueryのメリット・デメリットは簡単に言うと?
<検索ワード>
jQuery メリット<参考サイト>
AtoOne 様
https://www.atoone.co.jp/column/10176/2. Queryは最近のトレンドの中でどのような立ち位置か?
<検索ワード>
jQuery トレンド 立ち位置<参考サイト>
Hideout der Eremit 様
https://dereremit.jp/archives/2603. deviseとは簡単に言うと?
<検索ワード>
deviceとは<参考サイト>
キツネの惑星 様
https://kitsune.blog/rails-devise4. deviseと同様な機能で他によく使われるものは何か?違いは何か?なぜdevise以外にそれが生まれたか?
これ、めちゃくちゃ苦戦しました。
もっと分かりやすい、的確なサイトがあると思います。<検索ワード>
rails 認証 gem トレンド …他多数?<参考サイト>
Tech Blog 様
https://www.for-engineer.life/entry/rails-sorcery/5. unicornとは簡単に言うと何か?
6. unicornと同様な機能で他によく使われるものは何か?違いは何か?なぜunicorn以外にそれが生まれたか?
<検索ワード>
unicornとは<参考サイト>
@takahiro1127 様
https://qiita.com/takahiro1127/items/fcb81753eaf381b4b33c〜気づき〜
検索力にはある程度自信を持っていましたが、
信憑性のある、確からしい情報を得るためには
テクニックがあることを知りました。同時に自分の無力さに愕然(笑)
今後も学習のノート代わり、アウトプットとして活用させていただきます。
- 投稿日:2020-03-28T18:26:59+09:00
Rails × Docker ログに「Cannot render console from Allowed networks」と出力される対処法
ログに「Cannot render console from Allowed networks」と出力される対処法
環境
ruby 2.5.1
rails 5.2.4
docker 19.03.8Ruby on Railsの開発環境で、DockerかVagrantを使っていて、”Cannot render console from 172.20.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255″ 「このIPアドレスではレンダリングできない」と言われた場合
うぉおおん?
Started GET "/" for 172.20.0.1 at 2020-03-28 09:15:23 +0000 web_1 | Cannot render console from 172.20.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255対処法は簡単だった
Webコンソールの設定で172.20.0.1ネットワークをホワイトリストに登録する
config/environments/development.rb
にconfig.web_console.whitelisted_ips = ‘172.20.0.1’
を追加(IPアドレスは自身のログに書いてあるIPアドレスを記述)config/environments/development.rbconfig.web_console.whitelisted_ips ='172.20.0.1'これで解決しました
docker → kubernetesに関してこれから投稿していきます。
docker公式
- 投稿日:2020-03-28T18:20:38+09:00
#Rails / Controller 以外で ActiveModelSerializers の動作確認、JSON出力をする ( rails console とか )
モデルのインスタンスを Serializer に与えて to_json するだけで良さげ
Serializer
class UserSerializer < ActiveModel::Serializer attributes :id, :name end動作
空のインスタンス
user = User.new # <User:0x000055cfd1dcf6f8 # id: nil, # name: nil, # email: nil, # created_at: nil, # updated_at: nil> serialized_user = UserSerializer.new(user) serialized_user.to_json # => "{\"id\":null,\"name\":null}"DBからインスタンスを得る例
user = User.last # #<User:0x000055cfd1ea0730 # id: 19, # name: "吾輩", # created_at: Fri, 20 Mar 2020 01:02:10 UTC +00:00, # updated_at: Fri, 20 Mar 2020 01:02:10 UTC +00:00> serialized_user = UserSerializer.new(user) serialized_user.to_json # => => "{\"id\":19,\"name\":\"吾輩\"}"Original by Github issue
- 投稿日:2020-03-28T16:22:21+09:00
【ruby,rails】環境変数から読み込んだ秘密鍵の文字列の改行コードがエスケープされていて秘密鍵としてパースできないときの対処法
概要
環境変数から読み込んだ秘密鍵の文字列の改行コードがエスケープされてしまうことによって、秘密鍵としてパースできないときの文字列処理を共有します。
解決策
たとえば下記のような秘密鍵があったとき、
-----BEGIN RSA PRIVATE KEY----- MIGrAgEAAiEAvnrd8LBnzAGxCW+i7KtVQSiTsssMtbwcs5styeKsn2kCAwEAAQIh AKBF8glb5Xqa0cQG0ygg4hIFdipmvEJhiCuhX93krDCBAhEA51bAM0gFPvxyk9Xe ioIOBQIRANLJEv4Xw7MwT7EEEARL5RUCEBa8bu1bUbCsDPK8nT+NoqUCEQCIzFCU MY4j7BW8N3vBnhPlAhBgs4tSfe6RbpertixmCygk -----END RSA PRIVATE KEY-----環境変数に設定するときは下記のように改行コードを使って、文字列を1行にして設定すると思います。
-----BEGIN RSA PRIVATE KEY-----\nMIGrAgEAAiEAvnrd8LBnzAGxCW+i7KtVQSiTsssMtbwcs5styeKsn2kCAwEAAQIh\nAKBF8glb5Xqa0cQG0ygg4hIFdipmvEJhiCuhX93krDCBAhEA51bAM0gFPvxyk9Xe\nioIOBQIRANLJEv4Xw7MwT7EEEARL5RUCEBa8bu1bUbCsDPK8nT+NoqUCEQCIzFCU\nMY4j7BW8N3vBnhPlAhBgs4tSfe6RbpertixmCygk\n-----END RSA PRIVATE KEY-----\nこの1行にした秘密鍵をアプリケーション内で読み込むと、下記のようにバックスラッシュが2つになってしまいます。
"-----BEGIN RSA PRIVATE KEY-----\\nMIGrAgEAAiEAvnrd8LBnzAGxCW+i7KtVQSiTsssMtbwcs5styeKsn2kCAwEAAQIh\\nAKBF8glb5Xqa0cQG0ygg4hIFdipmvEJhiCuhX93krDCBAhEA51bAM0gFPvxyk9Xe\\nioIOBQIRANLJEv4Xw7MwT7EEEARL5RUCEBa8bu1bUbCsDPK8nT+NoqUCEQCIzFCU\\nMY4j7BW8N3vBnhPlAhBgs4tSfe6RbpertixmCygk\\n-----END RSA PRIVATE KEY-----\\n"このまま秘密鍵オブジェクトを作成しようとすると、エラーになります。
OpenSSL::PKey::RSA.new(str) => OpenSSL::PKey::RSAError: Neither PUB key nor PRIV key: nested asn1 errorそこで、この2つのバックスラッシュを1つのバックスラッシュに置換することで、秘密鍵オブジェクを作成することができるようになります。
str.gsub(/\\n/, "\n") => "-----BEGIN RSA PRIVATE KEY-----\nMIGrAgEAAiEAvnrd8LBnzAGxCW+i7KtVQSiTsssMtbwcs5styeKsn2kCAwEAAQIh\nAKBF8glb5Xqa0cQG0ygg4hIFdipmvEJhiCuhX93krDCBAhEA51bAM0gFPvxyk9Xe\nioIOBQIRANLJEv4Xw7MwT7EEEARL5RUCEBa8bu1bUbCsDPK8nT+NoqUCEQCIzFCU\nMY4j7BW8N3vBnhPlAhBgs4tSfe6RbpertixmCygk\n-----END RSA PRIVATE KEY-----\n" OpenSSL::PKey::RSA.new(str.gsub(/\\n/, "\n")) => #<OpenSSL::PKey::RSA:0x00007f9d7dd734e0>もちろん、秘密鍵だけではなく、改行コードのバックスラッシュがエスケープされたすべての文字列に対しても同様の解決策が有効です。
- 投稿日:2020-03-28T16:15:10+09:00
ユーザー管理に必須!Gemの『devise』を使おう!
Railsにおいてユーザーを管理する際に必要となるのが『devise』と呼ばれるGemです。
deciseの設定を忘れてしまわないように備忘録として記述します。
deviseの機能
このGemを使用することによって、ユーザーを管理する機能を追加する際に簡単に管理を行うことができるようになります。
deviseの設定
1.Gemfileへの記述
# 省略 gem 'devise' # 最終行に追記2.ターミナルにてbundle install
$ bundle installbundle installでGemを更新・反映する。
3.deviseの設定ファイルを作成
$ rails g devise:install4.deviseの機能を持ったUserモデルの作成
注意したいのがここ!通常のrails g model 〇〇ではないという点です。
ここでは下記のように記述してUserモデルを作成します。
# deviseコマンドでモデルを作成 $ rails g devise userこれでdevise機能を持ったUserモデルが作成されました。
またroutes.rbには自動的に下記のコードが追加されます。
config.routes.rbRails.application.routes.draw do devise_for :users #以下略devise_for :usersの記述によりユーザーのログイン・ログアウト、新規登録で必要なルーティングが作成されます。
5.rails db:migrateを行う
ターミナル上でrails db:migrateを行います。
# 作成されたマイグレーションファイルを実行 $ rails db:migrate上記を行うことでデータベース上にusersテーブルが作成されます。
まとめ
deviseの設定をスムーズに行えるようになりました。ただしユーザーのログイン・ログアウト・新規登録に関するバックエンドの実装に関しては曖昧な部分もあるため、追記で記述しアウトプットの機会を持ちたいと考えています。
- 投稿日:2020-03-28T16:09:43+09:00
dockerでよく使うコマンド一覧【Rails】
土台$ docker-compose run web ~MySQLを使う場合
$ docker-compose run web rails new . --force --database=mysql $ docker-compose buildmodel作成
docker-compose run web bundle exec rails g model (モデル名) (カラム名)バックグラウンドでコンテナ起動
docker-compose up -dコンテナ再起動
docker-compose restartGemfile変更時(基本的には)
$ docker-compose buildDBへテストデータ投入
$ docker-compose run web bundle exec rake db:seedDBへテストデータを一から入れ直したい時
(テーブル削除、作成、マイグレーション)$ docker-compose run web bundle exec rake db:drop $ docker-compose run web bundle exec rake db:create $ docker-compose run web bundle exec rake db:migrate $ docker-compose run web bundle exec rake db:seeddockerコンテナ、イメージ削除
$ docker rm $(docker ps -q -a) $ docker rmi $(docker images -q)補足: kaminari設定ファイル生成
$ docker-compose run web bundle exec rails g kaminari:config補足: kaminari(bootstrap4)ファイル生成(bootstrap4)
$ docker-compose run web bundle exec rails g kaminari:views bootstrap4補足: kaminariページネーションファイル生成
$ docker-compose run web bundle exec rails g kaminari:configRspecを使う場合(Gemfileにはrspecの記述をする)
$ docker-compose run web bundle exec rails g rspec:install
- 投稿日:2020-03-28T16:08:29+09:00
dockerでよく使うコマンド一覧
MySQLを使う場合
$ docker-compose run web rails new . --force --database=mysql $ docker-compose buildmodel作成
docker-compose run web bundle exec rails g model (モデル名) (カラム名) バックグラウンドでコンテナ起動docker-compose up -d
コンテナ再起動docker-compose restartGemfile変更時(基本的には)
$ docker-compose buildDBへテストデータ投入
$ docker-compose run web bundle exec rake db:seedDBへテストデータを一から入れ直したい時
(テーブル削除、作成、マイグレーション)$ docker-compose run web bundle exec rake db:drop $ docker-compose run web bundle exec rake db:create $ docker-compose run web bundle exec rake db:migrate $ docker-compose run web bundle exec rake db:seeddockerコンテナ、イメージ削除
$ docker rm $(docker ps -q -a) $ docker rmi $(docker images -q)補足: kaminari設定ファイル生成
$ docker-compose run web bundle exec rails g kaminari:config補足: kaminari(bootstrap4)ファイル生成(bootstrap4)
$ docker-compose run web bundle exec rails g kaminari:views bootstrap4補足: kaminariページネーションファイル生成
$ docker-compose run web bundle exec rails g kaminari:configRspecを使う場合(Gemfileにはrspecの記述をする)
$ docker-compose run web bundle exec rails g rspec:install
- 投稿日:2020-03-28T15:46:47+09:00
Railsチュートリアル〜6章〜ついにモデル作成へ突入
userモデルの作成
5章を足早に終わらせて続いて6章に入っていきます。
ここではユーザー登録ページを作成していく。
・ユーザー用のデータモデルの作成
・データを保存する手段の確保について学ぶ。Railsではデータモデルとして扱うデフォルトのデータ構造のことをモデルと呼ぶ。Railsでは、データを永続化するデフォルトの解決策としてデータベースを使ってデータを長期間保存する。データベースとやり取りをするデフォルトのライブラリはActive Recordという。
Active Recordはデータオブジェクトの作成・保存・検索のためのメソッドを持っている。データベースの移行
今回の目的は永続性のあるモデルを構築することである。
Railsでユーザーをモデリングする時は属性を明示的に識別する必要がなく、デフォルトでリレーショナルデータベースを使用する。リレーショナルデータベースはデータ行で構成されるテーブルからなり、各行はデータ属性のカラム(列)を持つ。nameとemailのカラムをもつusersテーブルを作成する。Userモデルを生成する $ rails generate model User name:string email:string invoke active_record create db/migrate/20160523010738_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.ymlgenerateコマンドの新しい結果の一つとして、マイグレーションと呼ばれるファイルが生成される。マイグレーションはデータベースの構造をインクリメンタルに変更する手段を提供する。(インクリメンタル=次第に増やす)要するにマイグレーションはデータベースの構造増やす手段を提供すること。
マイグレーション自体は、データベースに与える変更を定義したchangeメソッドの集まりである。changeメソッドはcreate_tableというRailsのメソッドを呼びユーザーを保存するためのテーブルをデータベースに作成する。
このメソッドはブロック変数を1つ持つブロックを受け取る。このブロックの中でnameとemailカラムをデータベースに作る。
モデル名は単数形(User)だがテーブル名は複数型(users)である。
ブロックの最後の行t.timestampsは特別なコマンドで、created_atとupdated_atという2つの「マジックカラム (Magic Columns)」を作成します。これらは、あるユーザーが作成または更新されたときに、その時刻を自動的に記録するタイムスタンプである。マイグレーションは、次のようにdb:migrateコマンドを使い実行することができる。これを「マイグレーションの適応」と呼ぶ。
$ rails db:migratedb:migrateが実行されると、db/development.sqlite3という名前のファイルが生成されます。これはSQLite5の実体である。development.sqlite3ファイルを開くためのDB Browser for SQLiteというツールを使うと、データベースの構造を見ることができる。
ほぼすべてのマイグレーションは、元に戻すことが可能
元に戻すことを「ロールバック (rollback)と呼び、Railsではdb:rollbackというコマンドで実現できます。$ rails db:rollbackmodelファイル
ユーザーオブジェクトの検索
User.find(1) User.findにユーザーのidを渡し,その結果Active Recordはそのidのユーザーを返します。 一般的なfindメソッド以外に、Active Recordには特定の属性でユーザーを検索する方法もあります。 User.find_by(email: "mhartl@example.com") >> User.first => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-05-23 19:05:58", updated_at: "2016-05-23 19:05:58"> firstはデータベースの最初のユーザーを返す。 >> User.all => #<ActiveRecord::Relation [#<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-05-23 19:05:58", updated_at: "2016-05-23 19:05:58">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2016-05-23 19:18:46", updated_at: "2016-05-23 19:18:46">]> User.allでデータベースのすべてのUserオブジェクトが返ってくることがわかります。また、返ってきたオブジェクトのクラスがActiveRecord::Relationとなっています。これは、各オブジェクトを配列として効率的にまとめてくれるクラスである。ユーザーオブジェクトの更新
基本的な更新の方法は2つである。
>> user # userオブジェクトが持つ情報のおさらい => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-05-23 19:05:58", updated_at: "2016-05-23 19:05:58"> >> user.email = "mhartl@example.net" => "mhartl@example.net" >> user.save => true変更をデータベースに保存するために最後にsaveを実行する必要があることを忘れない。
保存を行わずにreloadを実行すると、データベースの情報を元にオブジェクトを再読み込みするので失敗する。属性を更新するもう1つの方法は、update_attributesを使うケース
>> user.update_attributes(name: "The Dude", email: "dude@abides.org") => true >> user.name => "The Dude" >> user.email => "dude@abides.org"update_attributesメソッドは属性のハッシュを受け取り、成功時には更新と保存を続けて同時に行う。
特定の属性のみを更新したい場合は、次のようにupdate_attributeを使います。このupdate_attributeには、検証を回避するといった効果もある。>> user.update_attribute(:name, "El Duderino") => true >> user.name => "El Duderino"ユーザーの検証
UserモデルにNameとemailの属性を与えた。ここに制限を加えてデータを登録する際に適切な管理が行われるようにする。(具体的には名前を空にしない、emailはアドレスのフォーマットに従う。メールアドレスがデータベース内で重複しない。等)
Active Recordでは検証(バリデーション)という機能を通して、こういった制約を課すことができる。
よく使われるケース
存在性(presence)
長さ(length)
フォーマット(format)
一意性(uniqueness)
確認(confirmation)
の検証である。有効性の検証
有効なモデルのオブジェクトを作成しその属性のうちの一つを有効でない属性に意図的に変更する。→バリデーション機能で失敗するかどうかテストする。
デフォルトのUserテストrequire 'test_helper' class UserTest < ActiveSupport::TestCase # test "the truth" do # assert true # end end有効なオブジェクトに対してテストを書くためにsetupという特殊なメソッドを使って有効なUserオブジェクト(@user)を作成する。setupメソッド内に書かれた処理は各テストが走る直前に実行される。@userはインスタンス変数だが、setupメソッド内で宣言しておけばすべてのテスト内でインスタンス変数が使用できる。
require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com") end test "should be valid" do assert @user.valid? end endassertメソッドを使ってテストする。
@user.valid?がtrueを返すと成功し、falseを返すと失敗する。$ rails test:models modelに関するテストはこのコマンドを使用する。存在性の検証
最も基本的なバリデーションである。
渡された属性が存在することを検証する。ここではユーザーがデータベースに保存される前にname emailフィールドの両方が存在することを保証する。require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com") end test "should be valid" do assert @user.valid? end test "name should be present" do @user.name = " " assert_not @user.valid? end test "email should be present" do @user.email = " " assert_not @user.valid? end endname属性の存在性に関するテストを追加します。具体的には@user変数のname属性に対して空白の文字列をセット
assert_notメソッドを使って Userオブジェクトが有効でなくなったことを確認する。name属性の存在性を検証する。
class User < ApplicationRecord validates :name, presence: true end長さの検証
ここではユーザーモデル上の名前の長さを50字を上限とし、51文字以上の名前
emailの文字を255字を上限とするように長さの制限をかける。
nameの長さの検証に用いるテストrequire 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com") end . . . test "name should not be too long" do @user.name = "a" * 51 assert_not @user.valid? end test "email should not be too long" do @user.email = "a" * 244 + "@example.com" assert_not @user.valid? end end長さの検証を追加する
class User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 } endフォーマットの検証
name属性の検証には、空文字でない、名前が51文字未満であるという最小限の制約しかいまのところない。
email属性の場合は、有効なメールアドレスかどうかを判定するために、もっと厳重な要求を満たさなければなりません。ここではメールアドレスにおなじみのパターンuser@example.comに合っているかどうかも確認する。(なお今回ここで使うテストや検証は、形式があからさまに無効なものを拒否するだけであり、すべての有効なメールアドレスを受け入れられるものではない点に注意してください。)>> %w[foo bar baz] => ["foo", "bar", "baz"] >> addresses = %w[USER@foo.COM THE_US-ER@foo.bar.org first.last@foo.jp] => ["USER@foo.COM", "THE_US-ER@foo.bar.org", "first.last@foo.jp"] >> addresses.each do |address| ?> puts address >> end USER@foo.COM THE_US-ER@foo.bar.org first.last@foo.jp``メールアドレスのバリデーションは扱いが難しく、エラーが発生しやすい部分なので、有効なメールアドレスと無効なメールアドレスをいくつか用意して、バリデーション内のエラーを検知していきます。具体的には、user@example,comのような無効なメールアドレスが弾かれることと、user@example.comのような有効なメールアドレスが通ることを確認しながら、バリデーションを実装していきます (ちなみに今の状態では、空でない文字列はすべてメールアドレスとして通ってしまいます) 。
require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com") end . . . test "email validation should accept valid addresses" do valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org first.last@foo.jp alice+bob@baz.cn] valid_addresses.each do |valid_address| @user.email = valid_address assert @user.valid?, "#{valid_address.inspect} should be valid" end end endここでは、assertメソッドの第2引数にエラーメッセージを追加していることに注目してください。これによって、どのメールアドレスでテストが失敗したのかを特定できるようになります。 assert @user.valid?, "#{valid_address.inspect} should be valid次に、user@example,com (ドットではなくカンマになっている) やuser_at_foo.org (アットマーク ‘@’ がない) といった無効なメールアドレスを使って 「無効性 (Invalidity)」についてテストしていく。
require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com") end . . . test "email validation should reject invalid addresses" do invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. foo@bar_baz.com foo@bar+baz.com] invalid_addresses.each do |invalid_address| @user.email = invalid_address assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" end end endメールフォーマットを正規表現で検証する。
class User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX } end一意性を検証する
メールアドレスの一意性を強制するために (ユーザー名として使うために)、validatesメソッドの:uniqueオプションを使う。
今回のテストではRubyのオブジェクトを作るだけでなく、実際にメモリ上にレコードをデータベースに登録する必要がある。重複するメールアドレス拒否のテスト
require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com") end . . . test "email addresses should be unique" do duplicate_user = @user.dup duplicate_user.email = @user.email.upcase @user.save assert_not duplicate_user.valid? end end上のコードでは、@userと同じメールアドレスのユーザーは作成できないことを、@user.dupを使ってテストしています。dupは、同じ属性を持つデータを複製するためのメソッドです。@userを保存した後では、複製されたユーザーのメールアドレスが既にデータベース内に存在するため、ユーザの作成は無効になるはずである。
メールアドレスの一意性を検証する
emailのバリデーションにuniqueness: trueというオプションの追加class User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: true endメールアドレスの大文字小文字を無視した一意性の検証
app/models/user.rb class User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } end trueをcase_sensitive: falseに置き換えデータベースのインデックス
データベースにカラムを作成するとき、そのカラムでレコードを検索する (find) 必要があるかどうかを考えることは重要です。例えば、リスト 6.2のマイグレーションによって作成された email属性について考えてみましょう。第7章ではユーザーをサンプルアプリにログインできるようにしますが、このとき、送信されたものと一致するメールアドレスのユーザーのレコードをデータベースの中から探しだす必要があります。
インデックスなどの機能を持たない素朴なデータモデルにおいてユーザーをメールアドレスで検索するには、データベースのひとりひとりのユーザーの行を端から順に読み出し、そのemail属性と渡されたメールアドレスを比較するという非効率的な方法しかない。emailカラムにインデックスを追加することで、この問題を解決することができます。(インデックスは本の索引機能のようなもの)
emailインデックスを追加すると、データモデリングの変更が必要になります。Railsではマイグレーションでインデックスを追加します。Userモデルを生成すると自動的に新しいマイグレーションが作成されたことを思い出してください。
今回の場合は、既に存在するモデルに構造を追加するので、次のようにmigrationジェネレーターを使ってマイグレーションを直接作成する必要があります。$ rails generate migration add_index_to_users_emailユーザー用のマイグレーションと異なり、メールアドレスの一意性のマイグレーションは未定義になっています。リスト 6.29のように定義を記述する必要があります16。
メールアドレスの一意性を強制するためのマイグレーション
db/migrate/[timestamp]_add_index_to_users_email.rb class AddIndexToUsersEmail < ActiveRecord::Migration[5.0] def change add_index :users, :email, unique: true end end上のコードでは、usersテーブルのemailカラムにインデックスを追加するためにadd_indexというRailsのメソッドを使っています。インデックス自体は一意性を強制しませんが、オプションでunique: trueを指定することで強制できるようになります。
最後に、データベースをマイグレートします。
$ rails db:migrateこの時点では、テストDB用のサンプルデータが含まれているfixtures内で一意性の制限が保たれていないため、テストは red になります。つまり、リスト 6.1でユーザー用のfixtureが自動的に生成されていましたが、ここのメールアドレスが一意になっていないことが原因です (リスト 6.30) (実はこのデータはいずれも有効ではありませんが、fixture内のサンプルデータはバリデーションを通っていなかったので今まで問題にはなっていなかっただけでした)。
リスト 6.30: Userのデフォルトfixture red test/fixtures/users.yml # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/ # FixtureSet.htmlこれで1つの問題が解決されましたが、メールアドレスの一意性を保証するためには、もう1つやらなければならないことがあります。それは、いくつかのデータベースのアダプタが、常に大文字小文字を区別するインデックス を使っているとは限らない問題への対処です。例えば、Foo@ExAMPle.Comとfoo@example.comが別々の文字列だと解釈してしまうデータベースがありますが、私達のアプリケーションではこれらの文字列は同一であると解釈されるべきです。この問題を避けるために、今回は「データベースに保存される直前にすべての文字列を小文字に変換する」という対策を採ります。例えば"Foo@ExAMPle.CoM"という文字列が渡されたら、保存する直前に"foo@example.com"に変換してしまいます。これを実装するためにActive Recordのコールバック (callback) メソッドを利用します。このメソッドは、ある特定の時点で呼び出されるメソッドです。今回の場合は、オブジェクトが保存される時点で処理を実行したいので、before_saveというコールバックを使います。これを使って、ユーザーをデータベースに保存する前にemail属性を強制的に小文字に変換します17。作成したコードをリスト 6.32に示します。(本チュートリアルで初めて紹介したテクニックですが、このテクニックについては11.1でもう一度取り上げます。そこではコールバックを定義するときにメソッドを参照するという慣習について説明します。)
email属性を小文字に変換してメールアドレスの一意性を保証する
app/models/user.rb class User < ApplicationRecord before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } endbefore_saveコールバックにブロックを渡してユーザーのメールアドレスを設定します。設定されるメールアドレスは、現在の値をStringクラスのdowncaseメソッドを使って小文字バージョンにしたもの。
セキュアなパスワードを追加する
セキュアパスワードという手法では、各ユーザーにパスワードとパスワードの確認を入力させ、それを (そのままではなく) ハッシュ化したものをデータベースに保存します。要するにハッシュ関数を使って、入力されたデータを元に戻せない (不可逆な) データにする処理を指す。
ユーザーの認証は、パスワードの送信、ハッシュ化、データベース内のハッシュ化された値との比較、という手順で進んでいきます。比較の結果が一致すれば、送信されたパスワードは正しいと認識され、そのユーザーは認証されます。。こうすることで、生のパスワードをデータベースに保存するという危険なことをしなくてもユーザー登録できる。
ハッシュ化されたパスワード
セキュアなパスワードの実装はclass User < ApplicationRecord . . . has_secure_password end上のようにモデルにこのメソッドを追加すると、次のような機能が使えるようになる。
・セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
・2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになる。存在性と値が一致するかどうかのバリデーションも追加される。
・authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド) 。has_secure_password機能を使えるようにするには、モデル内にpassword_digestという属性を含める。
password_digestカラム用の適切なマイグレーションを生成。
マイグレーション名は自由に指定できる。add_password_digest_to_usersというマイグレーションファイルを生成するためには、次のコマンドを実行します。$ rails generate migration add_password_digest_to_users password_digest:string上のコマンドではpassword_digest:stringという引数を与えて、今回必要になる属性名と型情報を渡している。
password_digestカラムを追加するマイグレーション
db/migrate/[timestamp]_add_password_digest_to_users.rb class AddPasswordDigestToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :password_digest, :string end endリスト 6.35では、add_columnメソッドを使ってusersテーブルpassword_digestカラムを追加しています。これを適用させるには、データベースでマイグレーションを実行します。
$ rails db:migrateまた、has_secure_passwordを使ってパスワードをハッシュ化するためには、最先端のハッシュ関数であるbcryptが必要になります。パスワードを適切にハッシュ化することで、たとえ攻撃者によってデータベースからパスワードが漏れてしまった場合でも、Webサイトにログインされないようにできます。サンプルアプリケーションでbcryptを使うために、bcrypt gemをGemfileに追加します。
リスト 6.36: bcryptをGemfileに追加する
source 'https://rubygems.org' gem 'rails', '5.1.6' gem 'bcrypt', '3.1.12' . . .最後にbundle installを実行
6.3.2 ユーザーがセキュアなパスワードを持っている
Userモデルにpassword_digest属性を追加し、Gemfileにbcryptを追加したことで、ようやくUserモデル内でhas_secure_passwordが使えるようになりましたUserモデルにhas_secure_passwordを追加する
app/models/user.rb class User < ApplicationRecord before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } has_secure_password end次にユーザーモデルにパスワードとパスワード確認を追加する。
def setup @user = User.new(name: "Example User", email: "user@example.com") endテストをパスさせるために、パスワードとパスワード確認の値を追加します。
リスト 6.39: パスワードとパスワード確認を追加する
test/models/user_test.rb require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") end . . . endリスト 6.41: パスワードの最小文字数をテストする red
test/models/user_test.rb require 'test_helper' class UserTest < ActiveSupport::TestCase def setup @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") end . . . test "password should be present (nonblank)" do @user.password = @user.password_confirmation = " " * 6 assert_not @user.valid? end test "password should have a minimum length" do @user.password = @user.password_confirmation = "a" * 5 assert_not @user.valid? end endここで、次のような多重代入 (Multiple Assignment) を使っていることに注目してください。 @user.password = @user.password_confirmation = "a" * 5これはpasswordとpasswordconfirmationに対して同時に代入をしています
リスト 6.16ではmaximumを使ってユーザー名の最大文字数を制限していましたが、これと似たような形式のminimumというオプションを使って、最小文字数のバリデーションを実装することができます。
validates :password, length: { minimum: 6 }また、空のパスワードを入力させないために、存在性のバリデーションも一緒に追加します。結果として、Userモデルのコードはリスト 6.42のようになります。ちなみにhas_secure_passwordメソッドは存在性のバリデーションもしてくれるのですが、これは新しくレコードが追加されたときだけに適用される性質を持っています。したがって、例えばユーザーが ' ' (6文字分の空白スペース) といった文字列をパスワード欄に入力して更新しようとすると、バリデーションが適用されずに更新されてしまう問題が発生してしまうのです。
セキュアパスワードの完全な実装
app/models/user.rb class User < ApplicationRecord before_save { self.email = email.downcase } validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } has_secure_password validates :password, presence: true, length: { minimum: 6 } endこの時点では、テストは greenになるはずです。
ユーザーの作成と認証
以上でUserモデルの基本部分が完了しましたので、今度は7.1でユーザー情報表示ページを作成するときに備えて、データベースに新規ユーザーを1人作成しましょう。また、Userモデルにhas_secure_passwordを追加した効果についても (例えばauthenticateメソッドの効果なども) 見ていきましょう。ただしWebからのユーザー登録はまだできない (第7章で完成させます) ので、今回はRailsコンソールを使ってユーザーを手動で作成することにしましょう。6.1.3で説明したcreateを使いますが、後々実際のユーザーを作成する必要が出てくるので、今回はサンドボックス環境は使いません。したがって、今回作成したユーザーを保存すると、データベースに反映されます。それでは、まずrails consoleコマンドを実行してセッションを開始し、次に有効な名前・メールアドレス・パスワード・パスワード確認を渡してユーザーを作成してみましょう。
$ rails console >> User.create(name: "Michael Hartl", email: "mhartl@example.com", ?> password: "foobar", password_confirmation: "foobar") => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-05-23 20:36:46", updated_at: "2016-05-23 20:36:46", password_digest: "$2a$10$xxucoRlMp06RLJSfWpZ8hO8Dt9AZXlGRi3usP3njQg3...">うまくデータベースに保存されたかどうかを確認するために、開発環境用のデータベースをDB Browser for SQLiteで開き、usersテーブルの中身を見てみましょう (図 6.9)21。もしクラウドIDEを使っている場合は、データベースのファイルをダウンロードして開いてください (図 6.5)。このとき、先ほど定義したUserモデルの属性 (図 6.8) に対応したカラムがあることにも注目しておいてください。
images/figures/sqlite_user_row_with_password_4th_edition 図 6.9: SQLiteデータベースdb/development.sqlite3に登録されたユーザーの行 コンソールに戻ってpassword_digest属性を参照してみると、リスト 6.42のhas_secure_passwordの効果を確認できます。 >> user = User.find_by(email: "mhartl@example.com") >> user.password_digest => "$2a$10$xxucoRlMp06RLJSfWpZ8hO8Dt9AZXlGRi3usP3njQg3yOcVFzb6oK"これは、Userオブジェクトを作成したときに、"foobar"という文字列がハッシュ化された結果です。bcryptを使って生成されているので、この文字列から元々のパスワードを導出することは、コンピュータを使っても非現実的です
また6.3.1で説明したように、has_secure_passwordをUserモデルに追加したことで、そのオブジェクト内でauthenticateメソッドが使えるようになっています。このメソッドは、引数に渡された文字列 (パスワード) をハッシュ化した値と、データベース内にあるpassword_digestカラムの値を比較します。試しに、先ほど作成したuserオブジェクトに対して間違ったパスワードを与えてみましょう。
>> user.authenticate("not_the_right_password") false >> user.authenticate("foobaz") false間違ったパスワードを与えた結果、user.authenticateがfalseを返したことがわかります。次に、正しいパスワードを与えてみましょう。今度はauthenticateがそのユーザーオブジェクトを返すようになります。
>> user.authenticate("foobar") => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-05-23 20:36:46", updated_at: "2016-05-23 20:36:46", password_digest: "$2a$10$xxucoRlMp06RLJSfWpZ8hO8Dt9AZXlGRi3usP3njQg3...">第8章では、このauthenticateメソッドを使ってログインする方法を解説します。なお、authenticateがUserオブジェクトを返すことは重要ではなく、返ってきた値の論理値がtrueであることが重要です。4.2.3で紹介した、!!でそのオブジェクトが対応する論理値オブジェクトに変換できることを思い出してください。この性質を利用すると、user.authenticateがいい感じに仕事をしてくれるようになります。
>> !!user.authenticate("foobar") => true今日はここまで
- 投稿日:2020-03-28T13:51:51+09:00
Railsの既存AppをDockerに切り替える際に役立つ記事
既存のAppをDockerに切り替える
過去に制作したRailsアプリをDockerで構築したいと思います。
参考になる記事
基本的にこの記事を使って、進めていきました。
Ruby on Rails 「途中まで作ったアプリにDockerを導入したい」に挑戦してみる(MySQL / Sequel Pro)build時のエラーが出たら、下記の資料が役立ちます。
Docker docker-compose up時、You must use Bundler 2 or greater with this lockfile. という地獄のエラー
- Gemfile.lockの記述を削除して、空状態にする。(Dockerに切り替える際に阻害してしまう)
- Dockerfileのbundlerのバージョンを指定(RUN gem install bundler -v 1.3.0)
- $ docker-compose run web bundle install
- 改めてbuildする
これで進めました
dockerFROM ruby:2.5.1 RUN apt-get update -qq && \ apt-get install -y build-essential \ libpq-dev \ nodejs RUN mkdir /app_name ENV APP_ROOT /app_name WORKDIR $APP_ROOT ADD ./Gemfile $APP_ROOT/Gemfile ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock RUN gem install bundler -v 1.3.0 RUN bundle install ADD . $APP_ROOTRuby | bundler を特定のバージョンに切り替えて実行する
起動する際にエラーが出た場合
docker-compose up したらdriver failed programming external connectivity on endpointが出てきた
- 投稿日:2020-03-28T13:36:35+09:00
RailsアプリにDockerとCircleCIを導入した際、DB周りでエラーになったときの対処法
こんにちは、ペーパーエンジニアのよしこです。
CircleCIで
git push
時に自動テスト(RSpec)しているRailsアプリに、Dockerを導入しました。その際にデータベース周りで躓いたエラーがあります。
同じエラーの報告が少なかったので、エラー解消した対処法を共有します。
エラー文
git push
をトリガーにCircleCIがテストを実行するのですが、その前にデータベースを構築します。
そこでエラーが出ていました。circleci/config.yml# DBをセットアップ - run: name: DBをセットアップ command: bin/rails db:schema:load --trace※全文は下記
#!/bin/bash -eo pipefail bin/rails db:schema:load --trace ** Invoke db:schema:load (first_time) ** Invoke environment (first_time) ** Execute environment ** Invoke db:load_config (first_time) ** Execute db:load_config ** Invoke db:check_protected_environments (first_time) ** Invoke environment ** Invoke db:load_config ** Execute db:check_protected_environments rails aborted! PG::ConnectionBad: could not translate host name "db" to address: Name or service not known /home/circleci/project/vendor/bundle/gems/pg-0.20.0/lib/pg.rb:56:in `initialize' . . 省略 . bin/rails:4:in `require' bin/rails:4:in `<main>' Tasks: TOP => db:schema:load => db:check_protected_environments Exited with code exit status 1 CircleCI received exit code 1環境
Ruby : 2.6.3
Rails : 5.1.6
postgres : 12.2
CircleCI : 2.1
Docker-compose version: '3'結論
database.yml
に、test環境でhost: localhost
を追加することで解決しました。config/database.ymldefault: &default adapter: postgresql encoding: unicode pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: postgres password: host: db timeout: 5000 development: <<: *default database: haito_notice_development test: <<: *default database: haito_notice_test host: localhost # <<<<<追加<<<<< production: <<: *default password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>Dockerのimage上では
db
としてDB構築していますので、develop環境やproduction環境ではhost: db
とする必要があります。しかし私の場合は、CircleCIのDocker imageをtest環境かつ
host: 127.0.0.1 (localhost)
で構築しているため、これに合わせてあげる必要があったのではと理解しています。CircleCI
基本、公式のドキュメントを元に記載していますが、
別のエラー対処のためにworkflowsやcommandsを現在は適用していないため参考程度に。circleci/config.ymlversion: 2.1 jobs: build: docker: - image: circleci/ruby:2.6.3-stretch-node environment: BUNDLE_JOBS: 3 BUNDLE_RETRY: 3 BUNDLE_PATH: vendor/bundle PGHOST: 127.0.0.1 PGUSER: postgres RAILS_ENV: test - image: circleci/postgres:12-alpine environment: POSTGRES_USER: postgres POSTGRES_DB: app_test steps: - checkout - restore_cache: name: 依存関係キャッシュを復元 keys: - v1-dependencies-{{ checksum "Gemfile.lock" }} - v1-dependencies- - run: name: Bundler を指定 command: bundle -v - run: name: バンドルをインストール command: bundle check || bundle install - save_cache: key: v1-dependencies-{{ checksum "Gemfile.lock" }} paths: - vendor/bundle - run: name: 静的コード解析を実行(RuboCop) command: bundle exec rubocop - run: name: DBの起動まで待機 command: dockerize -wait tcp://localhost:5432 -timeout 1m - run: name: DBをセットアップ command: bin/rails db:schema:load --trace # ここでエラーが起こっていました!!! - run: name: テストを実行(RSpec) command: | bundle exec rspec --profile 10 \ --format RspecJunitFormatter \ --out test_results/rspec.xml \ --format progress \ $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings) - store_test_results: path: test_resultsDocker
念の為記載します。
こちらもDocker公式のドキュメントを参考に作っています。# Dockerfile FROM ruby:2.6.3 RUN apt-get update -qq && \ apt-get install -y nodejs \ postgresql-client RUN mkdir /app ENV APP_ROOT /app WORKDIR $APP_ROOT COPY ./Gemfile $APP_ROOT/Gemfile COPY ./Gemfile.lock $APP_ROOT/Gemfile.lock RUN bundle install COPY ./ $APP_ROOT COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 CMD ["rails", "server", "-b", "0.0.0.0"]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.ymlversion: '3' services: db: image: postgres:12-alpine environment: POSTGRES_HOST_AUTH_METHOD: 'trust' volumes: - ./tmp/db:/var/lib/postgresql/data 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ご指摘やご不明な点などがございましたらお気軽にご連絡ください。
- 投稿日:2020-03-28T11:34:23+09:00
どこのページからでもリンクさせたい時
はじめに
この記事が、初めての投稿なので見にくかったらすみません。。
自分の備忘録も兼ねて、学んだことを書いていきます!トップページからしか、リンク出来ない!!
フリマアプリの作成中のことです。
ヘッダーに、ユーザー新規登録ボタンを置いていました。
トップページでは、そのボタンから登録ページへ遷移できます。
ですが、商品検索ページや商品詳細ページでは、ボタンを押してもエラーになってしまいました!!絶対パスを相対パスに変更
結論から言うと、絶対パスを相対パスに変えたら解決しました。
絶対パス↓
header.html.haml%li.listsRight__item.listsRight__item--new = link_to "新規登録", "users/sign_up"相対パス↓
header.html.haml%li.listsRight__item.listsRight__item--new = link_to "新規登録", "/users/sign_up""users" の前に、"/(スラッシュ)" を付けただけですね。
絶対パスになっていたため、トップページ(http://localhost:3000/) を基準にしてしか飛ぶ事ができませんでした。
それを相対パスにする事で、商品ページ(http://localhost:3000/items/) など、どのページからでも遷移できるようになりました!最後に
絶対パス/相対パスは、ただのターミナルでのディレクトリ指定方法だと考えていました。
ですが、実際の挙動にも影響を与えることを知り、基本が一番大切だなと改めて思いました。もし解釈が間違えていたら、コメントをお願いします!
- 投稿日:2020-03-28T10:38:05+09:00
日々のうんちをシェアするアプリを作る~その1~
うんちしぇあ。作ります
技術の勉強をかねて、何か作りたいなあ、、、なんか良いアイディアないかなあ、、、
そんな思いを日々持っていました。
しかし特に決まらず、ふと彼女に「なんかほしいアプリない?」って聞いてみました。
すると
「毎日何回うんちしたかシェアするアプリかなあ、、、、」は!?
こいつ天才か!?僕はそう思いました。
だから作ります。うんちしぇあ。使う技術
僕はReactというものが書けます。少しですが。
だからNext.jsやってみたいなーっていう安直な理由でNext.js書きます。
あとRailsもかけるからAPI的に使います。とまあ、明らかに技術力は低いので、間違っているところ・アドバイス・情報共有などあればバンバンください。絶対に受け入れます。一緒に成長していきましょう。
女性ならプライベートでの勉強会も早速作って行くよん
まずはサクッとNext.jsのチュートリアルを終わらせました。。。しかし!!良さが全然わからん。なんか英語だし。
はい。もう良いです。作りながら勉強します。
やっぱうんちしぇあ。だしうんちのコンポーネントからだよね。
pages/unchis/index.ts// packages import moment from "moment"; // types import {IUnchi} from "../../types/unchi"; //components import { Calendar } from "../components/Calendar"; import {useState} from "react"; import UnchiLists from "../components/UnchiLists"; const today: string = moment().format("YYYY-MM-DD").toString(); const Index = () => { const searchUnchi = (date: string): IUnchi => { const emptyUnchi: IUnchi = { date: date, count: 0 } return emptyUnchi; }; const [unchi, setUnchi] = useState<IUnchi>(searchUnchi(today)); const handleClick = (date: string) => { setUnchi(searchUnchi(date)) }; return ( <> <h1>今日のうんち</h1> <Calendar onClick={handleClick} /> <UnchiLists {...unchi} /> <style> {` html { width: 100%; height: 100%; } body { width: 100%; height: 100%; position: relative; } `} </style> </> ) } export default Indexpages/components/UnchiLists.tsximport {IUnchi} from "../../types/unchi"; import moment from "moment"; import { Card } from "./Cards" const UnchiLists = (unchi: IUnchi) => { return ( <Card> {moment(unchi.date).format('M月D日')}のうんち <ul> <li> {unchi.count}回 </li> </ul> </Card> ) } export default UnchiListsCalendarはネットにあるコピペで使えるデザイン付きのやつを利用させてもらいました。
いきなりちょっと本気出しちゃったかな・・・
これで一旦日付に対してうんちの回数を確認できるようになったから次はうんちの登録画面かな。今回は特に難しいことや困ったことがなかったので技術についてあまり触れてませんが、詰まったところなどあれば適宜共有していこうかと思っています。
その2もお楽しみに!!
- 投稿日:2020-03-28T09:43:29+09:00
Dockerを学習するための記事一覧
Dockerを学習するための記事をまとめてみました
エラーの対策も記述してます。
Running
bundle update
will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.
ERROR: Service 'web' failed to build: The command '/bin/sh -c bundle install' returned a non-zero code: 6参考になる記事一覧
今回の動画ではインストール完了済みなので、この記事は飛ばしています。
DockerをMacにインストールする(更新: 2019/7/13)今回実施する記事は下記になります。
DockerでRailsの環境構築Mysqlの設定方法は下記が参考になります。
丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)下記で $ docker-compose run web bundle installの方法が記載されてました。
DockerでRailsのプロジェクトを立ち上げるまで下記のエラーが出た場合
Running `bundle update` will rebuild your snapshot from scratch, using only the gems in your Gemfile, which may resolve the conflict. ERROR: Service 'web' failed to build: The command '/bin/sh -c bundle install' returned a non-zero code: 6bundle updateを実施します
ターミナル$ docker-compose run web bundle update
- 投稿日:2020-03-28T00:43:34+09:00
[HowTo]マッチングアプリ的な友達機能を実装してみる。
現在、個人アプリを作成しているのですが、その中の機能として「友達機能」を実装することにしました。
スクールではカリキュラムになかった内容であったため、自身で色々と調べて実装したので、備忘録として、
以下のように記事にさせていただきます。
皆様の実装に少しでも役立てていただければ、幸いです。はじめに-機能に対する考え方
今回の「友達機能」実装にあたり、当初、「友達申請=>承認=>友達になる」というよくある流れでの実装を考えてました。
しかしながら、少し堅苦しい感じがしたので、もう少し気軽に友達申請を実現するために、最近流行っているマッチングアプリ的に「いいね」を友達申請の代わりに使用し、お互いに「いいね」されたら「友達」にするというやり方での実装を考えました。
以降、その考えをベースに実装をしております。実装イメージ
今回の機能の実装イメージは以下となります。
友達申請(いいね)の動作イメージ
友達申請(いいね)以下のようにハートマークをクリックすると完了します。
この動作をお互いに行うことで”友達”として認定します。お互いにいいねがされると友達として認識されます。
テーブル準備
まず、今回の友達機能を実装するにあたり"relationship"テーブルを準備いたします。
ターミナルrails g model relationshipマイグレーションファイルは以下のように作成します。
migration_fileclass CreateRelationships < ActiveRecord::Migration[5.2] def change create_table :relationships do |t| t.integer :follower_id t.integer :following_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :following_id add_index :relationships, [:follower_id, :following_id], unique: true end endマイグレーションファイルへの記述完了次第、ターミナルでrails db:migrateをしておきましょう。
ターミナル$ rails db:migrateモデル編集
テーブルが作成できましたので、続いてモデルを編集していきます。
今回は、友達申請(いいね)する人を"following”、友達申請(いいね)される人を"follower"とし、
友達となっているかどうかを"matchers"メソッドを作って判別します。User(一部)class User < ApplicationRecord has_many :following_relationships, foreign_key: "follower_id", class_name: "Relationship", dependent: :destroy has_many :followings, through: :following_relationships has_many :follower_relationships, foreign_key: "following_id", class_name: "Relationship", dependent: :destroy has_many :followers, through: :follower_relationships def following?(other_user) following_relationships.find_by(following_id: other_user.id) end def follow!(other_user) following_relationships.create!(following_id: other_user.id) end def unfollow!(other_user) following_relationships.find_by(following_id: other_user.id).destroy end #友達判定 def matchers followings & followers end endRelationshipモデルについては、follower/followingをUserに帰属させます。
relationshipclass Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :following, class_name: "User" validates :follower_id, presence: true validates :following_id, presence: true end以上でモデルに関しての編集は完了となります。
ビューの編集
今回の”友達申請(いいね)”に関してのビューを作成します。
(以下は”友達申請(いいね)”に関してのみの記述となりますので、必要に応じて肉付けしてください)今回のポイントは以下の通りです。
- if文で既に友達申請(いいね)をしているか否かを見極めます。
- 既に友達申請(いいね)をしている場合は、ハートを赤くしておき、ハートを押すと未申請状態(白色)にします。
- 逆に未申請の場合は、ハートを白くしておき、ハートを押すと申請済み状態(赤色)にします。
- これらの処理をform_withで情報を飛ばすことで実装してます。view(一部)- if current_user.following?(@user) = form_with model: @relationship,url: relationship_path, method: :delete, remote: true do |f| = button_tag type: 'submit', class: 'btn-liked',id: "unfollow_form" do %i.fas.fa-heart.fa-3x - else = form_with model: @relationship, remote: true do |f| %div= hidden_field_tag :following_id, @user.id = button_tag type: 'submit', class: 'btn-likes',id: "follow_form" do %i.far.fa-heart.fa-3xコントローラ
上記にてビューも完成したので、そちらに合わせてRelationshipコントローラを編集していきます。
友達申請(いいね)をしたときは、”create"メソッドを使用し、
友達申請(いいね)を取り消すときは、"destroy"メソッドを使用してます。
今回、基本的に申請時・申請取り消し時にページ遷移の必要はなかったので、Userページにredirectしてます。RelationshipsControllerclass RelationshipsController < ApplicationController def create current_user.following_relationships.create(create_params) redirect_to user_path(params[:following_id]) end def destroy @user=current_user @relationship = Relationship.where(following_id: params[:id],follower_id:@user.id) @relationship.destroy_all redirect_to user_path(params[:id]) end private def create_params params.permit(:following_id) end end以上で、上記イメージの友達機能が実装できます!
今回はいいねボタンを友達申請ボタンの代わりに使用しましたが、viewを変更するだけで実装できます!
色々と試してみていただけますと幸いです。参照
Railsでマッチング機能を作ってみる
https://qiita.com/Utr/items/da03bf4f23aba03da656Ruby on Rails ~フォロー(友達申請)機能の実装(コードメモ)
https://qiita.com/wtb114/items/dbba4364871aacf520cd以上となります。最後までご覧いただき、ありがとうございました!
今後も学習した事項に関してQiitaに投稿していきますので、よろしくお願いします!
記述に何か誤りなどございましたら、お手数ですが、ご連絡いただけますと幸いです。