- 投稿日:2020-02-12T23:47:06+09:00
【Rails】flashメッセージをフェードアウトで消す方法【JavaScript】
はじめに
この記事では、flashメッセージを表示したあと一定時間後にフェードアウトさせる方法を解説します。
flashメッセージ系の記事はたくさんあるのですが、
どれもbootstrap
を使用してたり
何やら複雑な方法だったり(Hamlハムル??とかrenderとかややこしい!)。。もっと簡単な記事はないのか!とモヤモヤしたので、自分まとめます。
自分と同じような人の為に!!(自分用のメモですすみませんw)flashメッセージの表示方法
それではレッツ実装!!
まずはコントローラーから。
コントローラー
def destroy @review = Review.find(params[:id]) @review.destroy if review.destroy redirect_to root_path, notice: "︎レビューを削除しました!" end endメッセージを表示させる記述は4行目の
if
文からです。レビューが削除された後、
root_path
(ホーム画面)に戻って
「レビューを削除しました」というメッセージが表示される。という流れです。続いてビュー画面へ!
ビュー
<% if flash[:notice] %> <div class="flash"><%= flash[:notice] %></div> <% end %> <%= yield %>flashメッセージはいろんな場面で共通で使う事が多いので、
views/layouts/application.html.erb
の<%= yield %>
より上の部分に記述します。コントローラーとビューはひとまず完成!!
JavaScriptの下準備
まずは
Gemfile
以下のコードを記述します。gem 'jquery-rails'からのターミナルで
$ bundle install
続いて
app/assets/javascripts/application.js
に以下のコードを加えましょう。//= require jqueryこれで
jQuery
の下準備完了。flashメッセージをフェードアウトさせる方法
いよいよ実装していきます。
①jsファイルに記述する場合(推奨)
以下のコードを
app/assets/javascripts/application.js(上と同じ場所)
に加えたら完成!!$(function(){ setTimeout("$('.flash').fadeOut('slow')", 2000); });②ビューファイルに直接記述する場合
以下のコードを
flashメッセージを表示させたいビューファイルに加えたら完成!!<script> $(function() { setTimeout("$('.flash').fadeOut('slow')", 2000); }); </script>※「.flash」はapplicaton.html.erbのdivに付けたクラス名です。各自自由に命名しましょう。
※数字部分は好みに合わせて変えましょう!ちなみに1000で1秒です。さいごに
今回はレビューの削除
destroy
後の実装でしたが、もちろん編集edit
や
お気に入りfavorite
なんかも実装可能です。あとはお好みで
CSS
をいじれば、
よく見るflashメッセージの完成です!!<参考>
https://qiita.com/dir_sh0606/items/b2165459deda97ae8468
- 投稿日:2020-02-12T20:18:07+09:00
ローカル環境にて、nokogiriが原因(?)でbundle installできないときの解決策
ローカル環境にrailsの開発環境を作ろうと以下の記事Ruby初学者のRuby On Rails 環境構築【Mac】
を参考に進めていると、以下のエラーに遭遇した。$bundle install #以下が実行結果 Fetching gem metadata from https://rubygems.org/............. Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Using rake 13.0.1 Using concurrent-ruby 1.1.6 Using i18n 1.8.2 Using minitest 5.14.0 Using thread_safe 0.3.6 Using tzinfo 1.2.6 Using zeitwerk 2.2.2 Using activesupport 6.0.2.1 Using builder 3.2.4 Using erubi 1.9.0 Using mini_portile2 2.4.0 Fetching nokogiri 1.10.8 Installing nokogiri 1.10.8 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. current directory: /Users/(user)/Desktop/Rails/vendor/bundle/ruby/2.5.0/gems/nokogiri-1.10.8/ext/nokogiri /Users/(user)/.rbenv/versions/2.5.0/bin/ruby -I /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/site_ruby/2.5.0 -r ./siteconf20200212-25664-dl2tcu.rb extconf.rb --use-system-libraries checking if the C compiler accepts -I /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/libxml2... *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options. Provided configuration options: --with-opt-dir --without-opt-dir --with-opt-include --without-opt-include=${opt-dir}/include --with-opt-lib --without-opt-lib=${opt-dir}/lib --with-make-prog --without-make-prog --srcdir=. --curdir --ruby=/Users/(user)/.rbenv/versions/2.5.0/bin/$(RUBY_BASE_NAME) --help --clean /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:456:in `try_do': The compiler failed to generate an executable file. (RuntimeError) You have to install development tools first. from /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:574:in `block in try_compile' from /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:521:in `with_werror' from /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:574:in `try_compile' from extconf.rb:138:in `nokogiri_try_compile' from extconf.rb:162:in `block in add_cflags' from /Users/(user)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/mkmf.rb:632:in `with_cflags' from extconf.rb:161:in `add_cflags' from extconf.rb:416:in `<main>' To see why this extension failed to compile, please check the mkmf.log which can be found here: /Users/(user)/Desktop/Rails/vendor/bundle/ruby/2.5.0/extensions/x86_64-darwin-18/2.5.0/nokogiri-1.10.8/mkmf.log extconf failed, exit code 1 Gem files will remain installed in /Users/(user)/Desktop/Rails/vendor/bundle/ruby/2.5.0/gems/nokogiri-1.10.8 for inspection. Results logged to /Users/(user)/Desktop/Rails/vendor/bundle/ruby/2.5.0/extensions/x86_64-darwin-18/2.5.0/nokogiri-1.10.8/gem_make.out An error occurred while installing nokogiri (1.10.8), and Bundler cannot continue. Make sure that `gem install nokogiri -v '1.10.8' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: rails was resolved to 6.0.2.1, which depends on actioncable was resolved to 6.0.2.1, which depends on actionpack was resolved to 6.0.2.1, which depends on actionview was resolved to 6.0.2.1, which depends on rails-dom-testing was resolved to 2.0.3, which depends on nokogiriどうやらnokogiriがインストールできてないみたい..
まず、解決策
以下の記事macOSアップデート後の『xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools)...』の対処法
を参考にした。以下を実行し、xcodeのツール(?)のインストールをした後、再度bundle installをしたら解決した。
$ xcode-select --installどのように解決に至ったか
エラーメッセージに
Check the mkmf.log file for more details. You may need configuration options.
と出ていたので、mkmk.logを開いた。
そこにmkmk.logxcrun: error: invalid active developer pathというエラーが出ていたので、ググったところ上記の解決につながった記事がヒットした。
- 投稿日:2020-02-12T20:09:22+09:00
Railsで検索機能を実装する場合の4つのパターン
パターン
- Ransackを使う
- コントローラーに書く
- Concernに書く
- Formオブジェクトを使う
Ransackを使う
Ransack使うとソートも楽に書けそうだけど、
追々カスタマイズが面倒そうなのもあり最初から自分で書くことが多い。
あまり使ったこと無いので、ケースによっては触ってみたい。コントローラーに書く
comments = Comment.where(post_id: params[:post_id]) if params[:post_id].present?こんな感じでコントローラーに直接where文を書くケース。
何も考えずに書くとこうなることが多いと思う。
条件式が増えてくると複雑度上がってrubocopに怒られる。Concernに書く
module Comments module SearchModule extend ActiveSupport::Concern def self.do_search(params) comments = Comment.all search_by_post_id(comments, params[:post_id]) if params[:post_id].present? end def self.search_by_post_id(comments, post_id) comments.where(post_id: params[:post_id]) end end endComments::SearchModule.do_search(params)検索用のConcernモジュールに切り分ける方法。FatControllerはこれで解消できるので良さげ。
ただConcern特有のお作法があるので、自由に書きづらく、単純なクラスが使いたくなった。Formオブジェクトを使う
class CommentSearchForm < SearchForm attribute :post_id, Types::Maybe::Coercible::Integer def do_search comments = Comment.all search_by_post_id(comments, post_id.value_or) if post_id.value_or.present? end private def search_by_post_id(comments, post_id) comments.where(post_id: post_id) end endDry::Types.load_extensions(:maybe) module Types include Dry::Types.module end class SearchForm < Dry::Struct endCommentSearchForm.new(params.to_hash.symbolize_keys).do_searchモデルに紐付かないパラメータを処理する時にはVirtusが便利で良く使われているそう。
ただ公式リポジトリをみると作者が後継ライブラリである
dry-rb
を推奨していたので、dry-rbを元に実装した。
https://github.com/solnic/virtus厳密には型指定のみなので以下の3つ。
dry-validation
でバリデーションも出来るようなので、後々使ってみたい。gem 'dry-monads' gem 'dry-struct' gem 'dry-types'普通にgemをインストールすると一番古い0.5系になるので、最新の使い方とは違うので注意。
Formオブジェクトを使ったことで、次のようなメリットがある気がする。
まだ余り使いこなせてないので詳しい方の意見聞きたい。
- フォーム関連の処理が書かれていることがひと目でわかる
- FatController対策になる
- シンプルなクラスでテスト書きやすい
- 投稿日:2020-02-12T18:35:36+09:00
【Rails】Pinterest APIで自分のアカウントのピン一覧を取得し、JSONを返すサンプルコード
はじめに
Pinterest APIを使ってみたので自分用に備忘録を残します。
今回対象とする機能は、「自分のアカウントのピン一覧を取得する」です。
公式ドキュメントはこちら※利用制限等についてはご注意下さい。
(著作権所有者の許可を得ている場合を除いて)画像を保存または改変しないこと、また Pinterest にある画像をユーザーが印刷できるようにしないこと
ピン以外のデータを保存しないこと
などなど、制限があります。
デベロッパー向けガイドライン | Pinterest Policy環境
OS: macOS Catalina 10.15.3 Ruby: 2.6.5 Rails: 6.0.2.1前提
- Access Tokenの取得方法
rails new
など最低限の下準備などは細かく触れていません。
Access Tokenの取得はこちらから
1.
net_http_module.rb
HTTPリクエストを投げる部分は分割しておきます。
今回はGETリクエストを投げるだけなので、超シンプルです。app/controllers/concerns/net_http_module.rbrequire 'active_support' module NetHttpModule extend ActiveSupport::Concern require 'net/http' def api_get(set_URL) uri = URI.parse(set_URL) response = Net::HTTP.get_response(uri) end end2.
pins_controller.rb
※事前に以下のような
Pin
モデル、コントローラーが作成済とします。$ rails g model Pin pin_id:integer pin_url:string image_url:string width:integer height: integer
$ rails g controller api/v1/pins index
app/controllers/api/v1/pins_controller.rbclass Api::V1::PinsController < ApplicationController include NetHttpModule def index # Pinterest APIのAccess Tokenはcredentialsで管理 url = "https://api.pinterest.com/v1/me/pins/?access_token=#{Rails.application.credentials.api_key[:pinterest]}&fields=id%2Cnote%2Curl%2Cimage" # NetHttpModuleで定義したapi_getメソッドを使用 response = api_get(url) # 429(リクエスト多すぎ)ならエラーメッセージを返す if response.code == "429" render json: { error: response.message }, status: response.code elsif response.code == "200" #privateで定義したメソッドを使用してDBにpin情報を保存 save_pins_and_cursor(response) # Vue.jsに返すJSONを作成する(この辺は必要に応じて変更) res = { message: response.message, pins: Pin.all.as_json, } render json: res, status: response.code else render json: { error: response.message }, status: response.code end end private def save_pins_and_cursor(response) pins = JSON.parse(response.body)['data'] # 返ってきたデータの数だけpin情報をDBに保存する pins.each do |pin| Pin.create( pin_id: pin['id'], # Pinterestが各ピンにつけている一意なID pin_url: pin['url'], # Pinterestの該当ピンに飛ぶURL image_url: pin['image']['original']['url'], # 画像が保存されているURL width: pin['image']['original']['width'], # 画像の幅 height: pin['image']['original']['height'] # 画像の高さ ) end end endあとはこれで返したJSONをフロントエンド側でゴニョゴニョすればOKです!
【便利】APIを試せるツールも公式に用意されている
こちらのリンクから、APIの各種機能を試せるAPI Explorerにアクセスできます。
GUIで使えるのは頭が疲れてるときにも優しいですね。便利。
以上です!
おわりに
最後まで読んで頂きありがとうございました
どなたかの参考になれば幸いです
参考にさせて頂いたサイト(いつもありがとうございます)
- 投稿日:2020-02-12T18:23:19+09:00
docker-composeで開発/本番環境を切り替える
docker-composeで簡単に環境を切り替えたい
webアプリの開発でサーバーやDBを同時に立ち上げるためにdocker-composeを使用していて
テストや動作確認もコンテナ上で起動してやる場合、webアプリを動かすコンテナとして
- 「production環境」 最小限のパッケージとモジュールを入れて余分なキャッシュ等も消し、できるだけイメージサイズを小さくしたもの
- 「development環境」 テスト関連やgit、分析ツール等の開発に必要なものが入ったやや大きいサイズのイメージのもの
というふうに2種類以上の環境を使い分けたくなりますが、これをどう切り替えたらいいかという話
色々やり方は考えられますがdoker-compose内で展開できる環境ファイルの
.env
ファイルを使うのがよさそうです。この記事ではRailsアプリでnginxとMySQLを使用しdocker-composeはversion:3以上を使うのを想定してますが、他のケースでも応用できると思います。
.envをdocker-composeに展開する
.envファイルをdocker-compose.ymlと同じ階層に用意するとdocker-compose内で変数として展開できます。
これを利用するとbuildするDockerfileのファイル名も変数で切り替えることができます。docker.compose.yml
version: '3.7' services: nginx: build: containers/nginx volumes: - public:/myapp/public - sockets:/myapp/tmp/sockets/ ports: - '8080:80' depends_on: - rails rails: build: context: . dockerfile: Dockerfile_${DOCKER_RAILS_ENV} image: webapp_${DOCKER_RAILS_ENV} volumes: - .:/myapp - public:/myapp/public - sockets:/myapp/tmp/sockets/ ports: - "3000:3000" links: - db environment: RAILS_ENV: $DOCKER_RAILS_ENV DB_HOST: db DB_USERNAME: $DB_USERNAME DB_PASSWORD: $DB_PASSWORD RAILS_SERVE_STATIC_FILES: db: image: mysql:5.7 volumes: - mysql-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: $DB_PASSWORD MYSQL_DATABASE: root ports: - "3306:3306" volumes: mysql-data: public: sockets:.env
DOCKER_RAILS_ENV=production DB_USERNAME=root DB_PASSWORD=passworddocker-compose.ymlの
dockerfile: "Dockerfile_${DOCKER_RAILS_ENV}"
の部分で
production環境用に作ったDocker_production
というファイルとDocker_development
というファイルを.envで定義した環境変数で指定し切り替えています。1
またbuild:の下のimage:webapp_${DOCKER_RAILS_ENV}
でビルドされるイメージ名を指定しています。2
ちなみに${DOCKER_RAILS_ENV:-development}
とすればデファルト値でdevelopmentになります
イメージ名にも同じことができるので必要であればimage: mysql:${DB_VER-5.7}
としてバージョンの指定なんかもできます。
参考:案外知られてないdocker-composeの環境変数定義の記法注意点
ホストマシンのLinux等に.envで指定した変数と同名の環境変数が設定されてる場合、ホストマシンの環境変数が優先されるので、意図的に使いたい場合を除いて名前がバッティングしないようにする必要があります。
今回の例だとRAILS_ENVは使われやすいので、DOCKER_RAILS_ENVとしてかぶらないようにしてます。またデフォルトで.gitignoreや.dockerignoreに.envは入ってないので間違ってもAWSへの接続情報などのクレデンシャルを.envに記述してgithubやdockerhub等にアップロードしないように注意しましょう。
Dockerfileは複数必要か
結局この方法だとdocker-composeは1つで済みますがDockerfileの方は2種類以上必要になります。
これも一つにできないかと考えたのですが、複雑になるのでやめました。3
アプリのルートをどうしてもDockerfile一つにしたい場合は、片方のDockerfileを配下に別のディレクトリを作って入れてdocker-composeのcontext:
の部分を切り替えるようにすればいいと思います。
- 投稿日:2020-02-12T18:23:19+09:00
docker-composeで.envで開発/本番環境を切り替える
docker-composeで簡単に環境を切り替えたい
webアプリの開発でサーバーやDBを同時に立ち上げるためにdocker-composeを使用していて
テストや動作確認もコンテナ上で起動してやる場合、webアプリを動かすコンテナとして
- 「production環境」 alpineをベースに最小限のパッケージとモジュールを入れて余分なキャッシュ等も消し、できるだけイメージサイズを小さくしたもの
- 「development環境」 テスト関連やgit、分析ツール等の開発に必要なものが入ったやや大きいサイズのイメージのもの
のように2種類以上の環境を使い分けたくなりますが、これをどう切り替えたらいいかという話
単純にdocker-compose.ymlを複数作って -f でファイル名を指定する方法や
公式ドキュメントにあるように拡張用のcomposeファイルを作る方法もありますが、doker-compose内で展開できる環境ファイルの.env
ファイルを使用して切り替えるのが色々と応用がきくのでお薦めです。作りたい環境に合わせたDockerfileを用意する
まず環境に合わせたDockerfileを用意します。
例えば
- 「Docker_production」
- 「Docker_development」
という感じに分けます。
.envを用意しdocker-composeに変数を指定する
.envファイルをdocker-compose.ymlと同じ階層に用意するとdocker-compose内で変数のように展開できます。
これを利用するとbuildするDockerfileのファイル名も変数で切り替えることができます。
以下はRailsを想定してますが、他のケースでも応用できると思います。docker.compose.yml
version: '3.7' ~ 省略 ~ rails: build: context: . dockerfile: Dockerfile_${DOCKER_RAILS_ENV} image: webapp_${DOCKER_RAILS_ENV} volumes: - .:/myapp - public:/myapp/public - sockets:/myapp/tmp/sockets/ ports: - "3000:3000" links: - db environment: RAILS_ENV: $DOCKER_RAILS_ENV DB_HOST: db DB_USERNAME: $DB_USERNAME DB_PASSWORD: $DB_PASSWORD RAILS_SERVE_STATIC_FILES: ~ 省略~.env
DOCKER_RAILS_ENV=production DB_USERNAME=root DB_PASSWORD=passwordbuildするDockerfile名に.envに書かれた環境変数を利用する
docker-compose.ymlの
dockerfile: "Dockerfile_${DOCKER_RAILS_ENV}"
の部分で
production環境用に作ったDocker_production
というファイルとDocker_development
というファイルを.envで定義した環境変数で指定し切り替えています。1
.envをDOCKER_RAILS_ENV=development
とすればdevelopment環境でupができます。作成されるイメージ名を指定する
デフォルトでは"ディレクトリの名前"_"指定したコンテナ名(rails)"になるので環境を変えても同じイメージ名がつきます。
build:の下にimage:を指定するとイメージ名を指定してビルドできます。
->https://docs.docker.com/compose/compose-file/#build
image:webapp_${DOCKER_RAILS_ENV}
でビルドされるイメージ名も環境で変わるようにしています。2ちなみに
${DOCKER_RAILS_ENV:-development}
とすれば変数が指定されていない場合のデファルト値がdevelopmentになります
必要であれば使用するDBのイメージ名でimage: mysql:${DB_VER-5.7}
として公式イメージのバージョンの指定にも応用できます。
参考:案外知られてないdocker-composeの環境変数定義の記法注意点
ホストマシンのLinux等に.envで指定した変数と同名の環境変数が設定されてる場合、ホストマシンの環境変数が優先されるので、意図的に使いたい場合を除いて名前がバッティングしないようにする必要があります。
今回の例だとRAILS_ENVは使われやすいので、DOCKER_RAILS_ENVとしてかぶらないようにしてます。またデフォルトで.gitignoreや.dockerignoreに.envは入ってないので間違ってもAWSのキーなどのクレデンシャルを.envに記述してgithubやdockerhub等にアップロードしないように注意しましょう。
Dockerfileは複数必要か
結局この方法だとdocker-composeは1つで済みますがDockerfileの方は2種類以上必要になります。
これも一つにできないかと考えたのですが、複雑になるのでやめました。3アプリのルートをどうしてもDockerfile一つにしたい場合は、片方のDockerfileを配下に別のディレクトリを作って入れてdocker-composeの
context:
の部分を切り替えるようにすればいいと思います。
- 投稿日:2020-02-12T17:51:19+09:00
Railsの検索まとめ(find、find_by、where、結合)
find
user = User.find(97)主キー(基本的にid)に対応するレコードを取り出すことができる。
見つからない場合、例外(ActiveRecord::RecordNotFound
)が発生する。find_by
user = User.find_by(email: 'hogehoge@example.com') user2 = User.find_by(age: 24, name: 'Jack')与えられた条件に合致するレコードのうち、最初の1件を取り出す。
複数条件で絞り込むこともできる。
見つからない場合nil
を返す。where
users = User.where(age: 24) #24才のユーザー users2 = User.where.not(age: 24) #24才以外のユーザー users3 = User.where(age: 24).where(region: 'Asia') #24才でアジアに住むユーザー users4 = User.where(age: 24).or(User.where(name: 'Jack')) #24才、またはJackという名前のユーザー users5 = User.where(age: 24).where(region: 'Asia').or(User.where(name: 'Jack')) #24才でアジアに住むユーザー、またはJackという名前のユーザー与えられた条件に合致するレコードの集合を取り出す。
見つからない場合ActiveRecord_Relationクラス
を返す。空の配列っぽいけど、配列ではないらしい。結合
パターンが多すぎるので、使用したものを適宜追加するスタイルでいきます。
1対多
使用するデータclass User < ApplicationRecord has_many :items end class Item < ApplicationRecord belongs_to :user end「user>email」,「item>price」の複数条件で検索し、全てのカラムのデータを取得users = User.joins(:items).select("users.*, items.*").where(users: {email: 'hogehoge@example.com'}).where(items: {price: 300})
select
で取得するカラム名を指定
where(テーブル名{検索条件})
で検索条件を指定1対1
使用するデータclass User < ApplicationRecord has_one :user_datum end class UserDatum < ApplicationRecord belongs_to :user enduser_data>email受け取りフラグが立っているユーザーを取得users = User.joins(:user_datum).select('users.*, user_data.*').where(user_data: {receive_email: 1})
joins
の引数が単数形になる。
※dataの単数形がdatum参考
- 投稿日:2020-02-12T17:50:34+09:00
Postgresqlに接続できない時の対処法
開発環境
- ubuntu(WSL)
- Postgresql
解決策
サーバを起動させるために下記のコマンドを実行します。
sudo /etc/init.d/postgresql startエラー内容
rails s
したら、以下のようになった。PG::ConnectionBad (could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
pg_lsclusters
で現状確認。
やっぱり、downしてる。10 main 5432 down postgres /var/lib/postgresql/10/main /var/log/postgresql/postgresql-10-main.log下記のコマンドでクラスターを起動させる。
pg_ctlcluster 10 main start
install: cannot change owner and permissions of ‘/var/run/postgresql’: No such file or directory Error: Could not create /var/run/postgresql/10-main.pg_stat_tmp: No such file or directoryサーバが起動していないことが原因だあるらしいので、下記のコマンドを実行させる。
sudo /etc/init.d/postgresql startこれで動きました。
参考文献
- 投稿日:2020-02-12T17:50:18+09:00
固まったRailsのローカルアプリケーションサーバ(Puma)を強制終了する
Railsを使用して開発していると、Pumaが固まって
Ctrl+C
でもPumaを終了できないことがあります。そんな時は以下のコマンドで解決!pkill -9 -f 'puma 4.3'上記のコマンドを叩いた後、ターミナルにて以下のように表示されていれば強制終了成功です。
Killed: 9
- 投稿日:2020-02-12T17:50:18+09:00
固まったRailsのローカルWebサーバ(Puma)を強制終了する方法
Railsを使用して開発していると、Pumaが固まって
Ctrl+C
でもPumaを終了できないことがあります。そんな時は以下のコマンドで解決!pkill -9 -f 'puma 4.3'上記のコマンドを叩いた後、ターミナルにて以下のように表示されていれば強制終了成功です。
- 投稿日:2020-02-12T17:50:18+09:00
固まったRailsのローカルアプリケーションサーバ(Puma)を強制終了する方法
Railsを使用して開発していると、Pumaが固まって
Ctrl+C
でもPumaを終了できないことがあります。そんな時は以下のコマンドで解決!pkill -9 -f 'puma 4.3'上記のコマンドを叩いた後、ターミナルにて以下のように表示されていれば強制終了成功です。
Killed: 9
- 投稿日:2020-02-12T17:34:43+09:00
dependent: :destroy 削除が出来ない
今回のテーマは、destroyメソッドが機能しない。
という事で、今回陥った点からまずお話致します。
トークルームにてチャットが出来るアプリにて、マイページでトークルームを削除できない。
という問題点でかなり悩まされる事に。テーブル
memo_roomsに関連付けられているのは、 usersテーブル memo_room_postsテーブル categoriesテーブル favoritesテーブル memo_room_commentsテーブルの5つです。解決前までの流れ。
最初にルームが削除出来ないと気付いた時には
def destroy
@memo_room.destroy
flash[:success] = 'メモルームを削除しました。'
redirect_to user_path(current_user)
endコントローラにてこんな感じで書いていました。これにより、失敗しても成功しても削除しました。としか出ず、成功はしてるが、
何故か削除出来ていない。こう勘違いしました。どうしても解けず、質問掲示板にて質問をしました。
def destroy
if @memo_room.destroy
flash[:success] = 'メモルームを削除しました。'
else
flash[:danger] = '削除に失敗しました。'
redirect_to user_path(current_user)
end
endすると・・・成否がどちらでも削除しました。となっているので失敗してるかわからないと、このようにアドバイスを頂き、
アドバイス通りにelseを追加したところ、削除に失敗しました。と表示されました。成功してると思っていたが、実は失敗していたんですね。それに気づかず成功してる前提で何故消えないかを探していたので
見つかる訳がありません。rbファイル dependent: :destroy 間違い編
class MemoRoom < ApplicationRecord belongs_to :user belongs_to :category, dependent: :destroy has_many :memo_room_posts, dependent: :destroy has_many :favorites, dependent: :destroy has_many :memo_room_comments, dependent: :destroy class MemoRoomPost < ApplicationRecord belongs_to :user belongs_to :memo_room, dependent: :destroy has_many :memo_room_posts, dependent: :destroy has_many :memo_rooms, dependent: :destroy has_many :categories, dependent: :destroy has_many :favorites, dependent: :destroydependent: :destroyをとりあえず付けておけば削除した際に関連されたデータも消えるから便利程度の認識でした。
ここがまず間違いでこれのせいで、関連付けが多すぎて網目のようになり、ぐちゃぐちゃになってがんじがらめになっていると教えて頂きました。そして、アドバイスとして一番わかりやすかったのが、
こいつを消したら意味が無くなるもの に対してのみその方向でのdependent: :destroyをつけるようにしてください
この時、初めて使い方をきちんと知りました。とてもわかりやすい一言でした。
解決編
class MemoRoom < ApplicationRecord belongs_to :user belongs_to :category has_many :memo_room_posts, dependent: :destroy has_many :favorites, dependent: :destroy has_many :memo_room_comments, dependent: :destroymemo_roomを消す事により、トークルーム内の投稿であるmemo_room_postsとmemo_roomにいいねされたfavoritesそしてトークルームが消えればコメントも無意味になるのでmemo_room_commentsこの3つのみにdependent: :destroyを付けてみました。
すると、がんじがらめから抜けて綺麗になったのか、削除する事が出来ました。必要のない項目にdependentがダメ!なんて初心者は知らない人もいると思うので、役に立つメモとなればと思います。
知ったかぶり ←しっかりと知識をつけることの大切さを知りました。https://teratail.com/questions/240800
実際に質問した際のURL。も載せておきます。
以上上手く削除が出来なくなってしまった際のメモでした。参考になればと思います。
- 投稿日:2020-02-12T17:21:11+09:00
自身が sidekiq のプロセスかどうか判定する
問題
Rails Server や Rake Task のプロセスで実行するパッチを Sidekiq のプロセスでは実行したくないが Sidekiq では、オフィシャルな場合分け方法を提供していない。
解決策
Sidekiq プロセスは起動時に config/sidekiq.yml を読み込み、 Sidekiq.options に格納する。したがって自身が Sidekiq プロセスかどうかを見るには config/sidekiq.yml にしか書かれていない情報がすでに読み込まれているかを確かめればよい。たとえば下のような分岐をする。
if Sidekiq.options[:queues].present? puts "Sidekiq プロセス" else puts "Rails Server プロセスをはじめとした Sidekiq 以外のプロセス" endもし config/sidekiq.yml 以外で queues を定義している場合はその限りでない。
- 投稿日:2020-02-12T17:11:56+09:00
HerokuでUglifier::Error: Unexpected token: operator (>).が出たときの解決策
今回僕がHerokuでデプロイしようと試みようとし立ちはだかった「Uglifier::Error: Unexpected token: operator (>).」のエラについての記事です。
僕がデプロイしたときのバージョン等は以下。
[box class="box17"]
・rubyのバージョン:2.5.1
・Railsのバージョン:5.2.3[/box]
バージョン rubyのバージョン 2.5.1 Railsのバージョン 5.2.3 やったことはただ1つです。
それはconfigのproduction.rbに記述されてある
config.assets.js_compressor = :uglifierを
config.assets.js_compressor = Uglifier.new(harmony: true)config.assets.js_compressor = :uglifierがconfig.assets.js_compressor = Uglifier.new(harmony: trueに変わりましたね。
こうすることで僕は無事デプロイすることができ、世界中にアプリを公開することができました。
めでたしです。Heriokuは簡単にデプロイできることで重宝されてますが、たまにやっかいなエラーが起きます。
そんなときは焦らずエラー文を読んで、慎重に解決の糸口を探ってみましょう。
- 投稿日:2020-02-12T17:02:55+09:00
Rails】An error occurred while installing unf_ext (0.0.7.6), and Bundler cannot continue.【解決策】
AWSでデプロイ作業をしていたら以下のようなエラーが出ました。
An error occurred while installing unf_ext (0.0.7.6), and Bundler cannot continue.完全に詰んだかと思いましたが、1時間かけて解決することができたので共有しようと思います。
rubyのバージョン 2.5.1 Railsのバージョン 5.2.3 bundlerのバージョン 2.0.2 では早速見ていきましょう。
僕は、Pay.jpのgemをbundle installしたかったのですが、An error occurred while installing unf_ext (0.0.7.6), and Bundler cannot continueというエラーが出ました。
結論、インスタンスを再起動しましょう。
そして、停止することができたら「開始ボタン」を押しましょう。これでひとまずはOKです。
さらに、NginxとMysqldを再起動します。
sudo service nginx restartsudo service mysqld restartコマンドは上記です。
これで準備は整いました。これで本番環境でもbundle installすることができると思います。
エラーが無事解決されましたら幸いです。
- 投稿日:2020-02-12T16:57:54+09:00
【Rails】deviseユーザーの登録日を表示する【めちゃ簡単】
Deviseのユーザー登録日を表示することに成功しました。めちゃ簡単なので、ぜひ実装してみてください。
rubyのバージョン 2.5.1 Railsのバージョン 5.2.3 手順は以下のような感じになります。
・❶:コントローラーにTime_zoneの記述
・❷:ビューにcreated_atの記述手順1:コントローラーにTime_zoneの記述
まずは表示したいビューのコントローラーに以下の記述を行います。
class UsersController < ApplicationController before_action :set_zone 〜〜〜〜省略〜〜〜〜 private def set_zone Time.zone='Tokyo' endこんな感じで、日本語表記を綺麗なものに変更します。
僕の場合だと、usersコントローラーのmember.html.erbにユーザー登録日を表示したかったので、usersコントローラーにTime_zoneを表記しました。
別にapplication_controllerでもOKだと思いますが、変なエラーが起きても嫌なので記述する場所は限定しておくのが無難。
手順2:ビューにcreated_atの記述
以下のように記述します(自分が定義した変数に合わせて記述してください)。
<div class="member-information"><%=@user.created_at.strftime("%Y年%m月%d日 ")%></div>これで実装は終わりです。
ちなみに、created_atというカラムはデフォルトでついているものですが、もしマイグレーションファイルに記述がない場合は以下を記述してくださいね。
t.timestamps null: falseそして、null: falseだとやっかいごとが起こるそうですが、僕の開発アプリだと大丈夫そうなのでnull: falseはつけています。
お疲れ様でした!!
- 投稿日:2020-02-12T16:54:50+09:00
#Rails + dotenv-rails / overload env files / not linux or OS or shell or docker container env
dotenv-rails does not prepare smart configuration for overload env ?
i write in
config/environments/development.rb
or
config/application.rb
( after Rails app configuration )
Dotenv.overload(*[ Rails.root.join(".env.#{Rails.env}.local"), (Rails.root.join(".env.local") unless Rails.env.test?), Rails.root.join(".env.#{Rails.env}"), Rails.root.join(".env"), ].compact)see
https://github.com/bkeepers/dotenv/blob/v2.7.5/lib/dotenv/rails.rb#L57Original by Github issue
- 投稿日:2020-02-12T16:50:23+09:00
【AWS&Rails】 listen "#{app_path}/tmp/sockets/unicorn.sock"がないみたいなエラー【解決策】
AWSでデプロイしようと試みてたら以下のようなエラー出ました。
listen "#{app_path}/tmp/sockets/unicorn.sock"Unicorn関係はほんとに嫌いです?
しかし解決できたので共有します!
rubyのバージョン Railsのバージョン bundlerのバージョン 2.5.1 5.2.3 2.0.2 では早速見ていきましょう
!手順1 本番環境にいく
ec2ユーザー ElasticIP アプリ名 $まずはここまでいきましょう。
手順2 ps aux | grep unicornコマンドを叩く
ps aux | grep unicornそして1番上の左の数字をkillしてあげてください。
kill 1番上の左の数字手順3 RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -Dコマンドを叩く
まだこれだけではエラーは治りません。
最後に以下のコマンドを叩く必要があります。
ec2ユーザー ElasticIP アプリ名 $ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -Dこれでエラーがでなければ、無事成功です。
デプロイ環境でローカルの内容をみれると思います。
お疲れ様でした!
- 投稿日:2020-02-12T16:41:40+09:00
【Rails】uninitialized constant Message::ImageUploaderエラー【解決策】
Railsでアプリ開発を行なっていたところ
uninitialized constant Message::ImageUploaderこんなエラーが出ました。
Rubyのバージョン 2.5.1 Railsのバージョン 5.2.3 しかし、無事解決することができたので、共有したいと思います。
結論、「rails s」をし直しましょう。
$ rails s僕は今まで何個もアプリを作ってきたんですが、「uninitialized constant Message::ImageUploaderエラー」はrails sし直すことで解決できています。
見たことないエラーはほんとにヒヤヒヤしますね。。。
- 投稿日:2020-02-12T16:35:53+09:00
【Rails】See Crash Report log file under the one of following:とかいう脅迫文を解決した
どうも、謎のエラーに苦しめられたノロノロです。
いや〜ひさびさに冷や汗止まらないエラーに出くわしました。!
See Crash Report log file under the one of followingこれを解決するのに1時間半はかかりました。
結論、mysqlのバージョンを1つ下げましょう。
僕の場合だと
gem ‘mysql2’, ‘>= 0.4.4’, ‘< 0.6.0’mysqlのバージョンはこんな感じでした。これを
gem ‘mysql2’, ‘0.4.10’これに変えて「bundle install」すればOKです。僕とエラー内容が一致しているなら治ると思います。
ただ、なぜこんなエラーが出て、なぜバージョンを下げたらエラーが治るかの「原因」はわからずじまいなんですよね。
ちょっと気持ち悪いですが、無事Railsで開発もできるようになりましたし、よしとしましょう。
- 投稿日:2020-02-12T16:34:24+09:00
SEしてるけど実はあんまりコード書いたことないんだよねって人に捧ぐ、Rails on Dockerハンズオン vol.7 - Secure password -
はじめに
第7回目となる今回は、モデルにセキュアなパスワードをもたらしてくれる
has_secure_password
メソッドの使い方を紹介していきます。
パスワードは他人に知られてはまずいものです。万が一、データを抜かれたり画面に表示されちゃったりしても一目でわからないようになっていることが望ましいですね。そんなことを実現してくれるメソッドがhas_secure_password
です。
has_secure_password
Railsでは、モデルにセキュアなパスワード属性を実装するメソッドとして、
has_secure_password
が用意されています。モデルに対してhas_secure_password
を適用することでモデルは以下の恩恵を受けることができます。
- 仮想属性として
password
とpassword_confirmation
を利用可能password
とpassword_confirmation
の同一性について勝手に検証してくれる- DBにはハッシュ化した
password_digest
を保存するようになる- ハッシュ値でパスワード検証をする
authenticate
メソッドを利用可能ハッシュ化についてちょっとお話ししておきます。ハッシュ化はある文字列を不可逆な別の文字列に変換してくれます。『不可逆』とは元に戻せないという意味です。
『暗号化』の場合は『復号化』することで元の文字列に変換しなおせるんです。これは『可逆』といいますね。では、早速モデルに
has_secure_password
メソッドを適用していきます。
has_secure_password
ではハッシュ化を行うためにbcrypt
gemを利用します。まずは、bcrypt
のインストールからやっていきます。Gemfile
の中身をみるとわかりますが、bcrypt
は最初からGemfile
の中に書かれておりコメントアウトされているだけです。なのでコメントアウトをとってdocker-compose build
をするだけでOKです。Gemfile... gem 'bcrypt', '~> 3.1.7' ...$ docker-compose build $ docker-compose up -dコンテナも立ち上げておきましょう。
次に、
has_secure_password
メソッドを使うようになるとpassword
をハッシュ化した値をpassword_digest
カラムにDB保存するようになります。マイグレーションファイルを作成しDBにpassword_digest
カラムを追加しておきます。# rails g migration add_password_digest_column_to_user password_digest:string Running via Spring preloader in process 251 invoke active_record create db/migrate/20200130075100_add_password_digest_column_to_user.rb # rails db:migrate == 20200130075100 AddPasswordDigestColumnToUser: migrating ==================== -- add_column(:users, :password_digest, :string) -> 0.0522s == 20200130075100 AddPasswordDigestColumnToUser: migrated (0.0533s) ===========そして、Userモデルにhas_secure_passwordメソッドを適用します。
app/models/user.rbclass User < ApplicationRecord ... has_secure_password ... endこれで完了です!簡単ですね!
では恩恵がちゃんと受けられているか確認してみましょう。
password
とpassword_confirmation
まずは
password
属性とpassword_confirmation
属性の二つの仮想属性が利用できることを確認してみます。> user = User.new(name: "hanako", email: "hanako@sample.com", password: "password", password_confirmation: "password") => #<User:0x0000563d32829928 id: nil, name: "hanako", email: "hanako@sample.com", created_at: nil, updated_at: nil, password_digest: [FILTERED]> > user.password => "password" > user.password_confirmation => "password" > user.password_digest => "$2a$12$OfB2RwhC2b2hHW.8bIpNqeEoqM9xS3OdLubSUS4Gew0Ece1dnTzDG"DBカラム的に
password_digest
しかないはずですが、password
、password_confirmation
が使えていることがわかります。さらにその結果がpassword_digest
として利用可能なのもわかりますね。
password
とpassword_confirmation
の一致性確認ここで一度
valid?
メソッドを使ってみましょう。これは今現在のモデルでvalidationを突破できるのかを確認できるメソッドです。先ほどまではsave
でvalidationできるかを確認していましたが、実はvalidationを調べるためだけであればこれでOK。> user.valid? User Exists? (3.6ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "hanako@sample.com"], ["LIMIT", 1]] => true今は
password
とpassword_confirmation
がマッチしているのでvalidationは通っているようです。では、password_confirmation
を別の文字列に変更した場合どうでしょうか?> user.password = "password1" => "password1" > user.valid? User Exists? (35.3ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "hanako@sample.com"], ["LIMIT", 1]] => false > user.errors.full_messages => ["Password confirmationとPasswordの入力が一致しません"]validationでエラーになっていることがわかりましたね...あ、日本語化していない。
日本語化しましょう!config/locales/ja.ymlja: activerecord: attributes: user: name: "お名前" email: "メールアドレス" password: "パスワード" password_confirmation: "確認用パスワード" ...> reload! Reloading... => true > user.valid? User Exists? (4.7ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "hanako@sample.com"], ["LIMIT", 1]] => false > user.errors.full_messages => ["確認用パスワードとパスワードの入力が一致しません"]
password
とpassword_confirmation
が一致していない場合はエラーが起きていますね。
ただ、password_confirmation
のようなものは必要なのか?という論争もあると思います。
「登録フォームにおけるパスワード確認用の入力欄は必要か | UX MILK」
例えばこちらの記事では、確認用パスワードを用意するのではなく、パスワードの欄のマスクを外せるようにした方がコンバージョンが上がると述べられていたりします。
has_secure_password
では、password_confirmation
がnil
の場合、password
とpassword_confirmation
の一致を確認しません。> user.password_confirmation = nil => nil > user.valid? User Exists? (2.4ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "hanako@sample.com"], ["LIMIT", 1]] => trueなので確認用パスワードを使う使わないによらず、
has_secure_password
メソッドは強力なのです。DBではハッシュ化された
password_digest
が使われる最初にもお話しした通り、ハッシュ化は不可逆なデータ変換です。
has_secure_password
ではpassword_digest
にハッシュ化されたpassword
を保存します。それを確認してみましょう!実際にUserを作成してみましょう。
> user.save (0.4ms) BEGIN User Exists? (2.3ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "hanako@sample.com"], ["LIMIT", 1]] User Create (69.4ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["name", "hanako"], ["email", "hanako@sample.com"], ["created_at", "2020-01-31 20:35:57.284982"], ["updated_at", "2020-01-31 20:35:57.284982"], ["password_digest", "$2a$12$OfB2RwhC2b2hHW.8bIpNqeEoqM9xS3OdLubSUS4Gew0Ece1dnTzDG"]] (5.6ms) COMMIT => true > user.find(1) => #<User:0x0000563d32823898 id: 1, name: "hanako", email: "hanako@sample.com", created_at: Fri, 31 Jan 2020 20:35:57 JST +09:00, updated_at: Fri, 31 Jan 2020 20:35:57 JST +09:00, password_digest: [FILTERED]> > user.password => nil > user.password_digest => "$2a$12$OfB2RwhC2b2hHW.8bIpNqeEoqM9xS3OdLubSUS4Gew0Ece1dnTzDG"
user.password_digest
をみてもなんのことやらわかりませんね。ちょっと安心です。パスワード認証する
authenticate
メソッドさて、ハッシュ化されてセキュアになったのはいいのですが、不可逆なのでこのままではパスワードで認証ができません。
has_secure_password
ではハッシュ化されたパスワードの認証をするためのauthenticate
メソッドが用意されています。これは、password_digest
の値と入力したpassword
を同じハッシュ関数でハッシュ化した値の一致をチェックしてくれるものです。> User.find_by(email: "hanako@sample.com").authenticate("a") User Load (4.5ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "hanako@sample.com"], ["LIMIT", 1]] => false > User.find_by(email: "hanako@sample.com").authenticate("password") User Load (2.5ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "hanako@sample.com"], ["LIMIT", 1]] => #<User:0x0000563d32d80688 id: 1, name: "hanako", email: "hanako@sample.com", created_at: Fri, 31 Jan 2020 20:35:57 JST +09:00, updated_at: Fri, 31 Jan 2020 20:35:57 JST +09:00, password_digest: [FILTERED]>このように
find_by
と組み合わせて使えばメールアドレスとパスワードの2key認証を実装できます。authenticate
は不一致の場合はfalse
を、一致の場合はモデルオブジェクトを返却します。
password
にvalidationを設ける仮想属性である
password
に対してもvalidationをつけることができます。
一方で、has_secure_password
で作られたpassword
にはデフォルトでpresence
のvalidationが設定されています。これに加えて6文字以上でないといけない文字数制限をつけてみましょう。
デフォルトでpresence
がついてはいるのですが、入力がない場合にpresence
とlength
の両方に引っかかってしまうのでpresence
については適用しない方がわかりやすいでしょう。
has_secure_password
にvalidations: false
オプションをつけてデフォルトのpresence
validationを向こうにした後に、他の属性と同様にlength
のvalidationをつけてみましょう。app/models/user.rbclass User < ApplicationRecord ... has_secure_password validations: false ... validates :password, length: { minimum: 6 } ... end
password
のvalidationについて試してみましょう。> user = User.new(name: "john", email: "john@sample.com") => #<User:0x0000563d32287768 id: nil, name: "john", email: "john@sample.com", created_at: nil, updated_at: nil, password_digest: nil> > user.valid? User Exists? (4.9ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "john@sample.com"], ["LIMIT", 1]] => false > user.errors.full_messages => ["パスワードは6文字以上で入力してください"] > user.password = "a" * 5 => "aaaaa" > user.valid? User Exists? (4.8ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "john@sample.com"], ["LIMIT", 1]] => false > user.errors.full_messages => ["パスワードは6文字以上で入力してください"] > user.password = "a" * 6 => "aaaaaa" > user.valid? User Exists? (3.5ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "john@sample.com"], ["LIMIT", 1]] => trueということで、
password
のlength
validationが正しく挙動していることがわかりました。Userを作成
最後にこのバリデーションの中でちゃんとUserを作成できることを確認しておきましょう!
> User.create(name: "John Smith", email: "john@sample.com", password: "password") (1.9ms) BEGIN User Exists? (7.4ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "john@sample.com"], ["LIMIT", 1]] User Create (30.2ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["name", "John Smith"], ["email", "john@sample.com"], ["created_at", "2020-02-09 16:40:36.252829"], ["updated_at", "2020-02-09 16:40:36.252829"], ["password_digest", "$2a$12$JHC/AEBGXyffzL9..2ThOuxDRlRCSP2RxtFxZxDzeOfsQIX6BGqym"]] (3.0ms) COMMIT => #<User:0x000055f6f7e390b0 id: 2, name: "John Smith", email: "john@sample.com", created_at: Sun, 09 Feb 2020 16:40:36 JST +09:00, updated_at: Sun, 09 Feb 2020 16:40:36 JST +09:00, password_digest: [FILTERED]>validationにひっかからないUserであればちゃんと作成できることが確認できましたね!
ユーザー情報を確認するViewを作ってみる
ここまででUserモデルがほぼ完成しました。この情報をUIで見れるようにViewを作ってみましょう!
Scaffoldを思い出してください。ユーザーの詳細情報を見るためのページは
/users/:id
のURLにアクセスして閲覧することができ、show
アクションにルーティングされていました。今回もそれにそってユーザーのページを作っていきます。
ユーザー詳細ページを作成する
ユーザー詳細ページは、
/users/:id
のパスにアクセスした時に、そのid
のユーザーの詳細情報が表示されるページにしたいと思います。
この通りになるように、ファイルの編集や作成を行っていきます。いままでrails g ~
コマンドでファイルを作成してきましたが、Railsの規則に則っていれば普通にファイルを作成しても動きます。Routing
まずは、
/users/:id
のルーティングを生成します。以前、Scaffoldの時にresources
メソッドを使ってUsersコントローラーに対するルーティングを定義しました。/users/:id
はその時のshow
アクションへのルーティングと同じです。
resources
メソッドはonly
オプションをつけることで特定のアクションへのルーティングのみを定義してくれます。今回はこのオプションを使ってルーティングを定義します。config/routes.rbRails.application.routes.draw do root 'static_pages#home' resources :users, only: [:show] endこれでルーティングの設定は完了です。試しに
rails routes
コマンドでルーティングを確認してみてもいいかもしれません。# rails routes Prefix Verb URI Pattern Controller#Action root GET / static_pages#home user GET /users/:id users#show ...確かに
GET /users/:id
がusers#show
にルーティングされています。Controller
次にUsersコントローラーを作成していきます。コントローラーは複数形の名称にするのがルールです。
# touch app/controllers/users_controller.rbapp/controllers/users_controller.rbclass UsersController < ApplicationController def show @user = User.find(params[:id]) end end
users
コントローラにshow
アクションがある、というところまでは今までの内容からわかりますね。
show
アクションの中身をみてみます。
params[:id]
は/users/:id
のパスパラメータの:id
を取得しています。例えば/users/1
にアクセスした場合はparams[:id]=1
だし、users/2
にアクセスした場合はparams[:id]=2
です。
User.find()
はModel CRUDの回で話した通り、id
でUserのモデルオブジェクトを取得するメソッドなので、パスのid
のユーザーがこれで取得できるわけです。
あとはViewに引き渡すためにこれをインスタンス変数@user
に代入しています。ではこれを受け取るViewを作っていきます。
View
まず、Viewファイルを格納するディレクトリを作成して、その中に
show.html.erb
ファイルを作ります。
UsersコントローラのためのViewなのでapp/views/users/
ディレクトリ内にファイルを作成していきます。# mkdir app/views/users # touch app/views/users/show.html.erb最初は情報が表示されていればOKとしましょう。特にUIは拘らない。
app/views/users/show.html.erb<div class="container my-5"> <%= @user.name %> <br> <%= @user.email %> </div>
name
と
http://localhost:3000/users/2
にアクセスしてみましょう。
こんなページが表示されていれば成功です!
(id
は実際にDBに格納されているデータ次第ですので、Rails consoleでUser.all
を打つなどして存在するUserのid
を確認してみてくださいね。)後片付け
次回に向けてデータを消します。
$ docker-compose down $ docker-compose run --rm web rails db:migrate:resetDBコンテナが立ち上がった状態だと思うのでdownさせます。
$ docker-compose downまとめ
今回は、
has_secure_password
メソッドを使ってセキュアなパスワードが利用できるようになりました。
コード的にいえばたった1行has_secure_password
を付け加えるだけでパスワードをハッシュ化してセキュアに扱うことができるようになる。これって強力なメソッドですよね。さらにユーザーの情報を表示するページまで作ることができました。
次回はSign up(ユーザー登録)ページを作っていきましょう!では、次回も乞うご期待!ここまでお読みいただきありがとうございました!
Reference
- Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
- 登録フォームにおけるパスワード確認用の入力欄は必要か | UX MILK
- 「ハッシュ」とは何なのか、必ず理解させます | ALIS
Links
・ Vol.1 - Introduction -
・ Vol.2 - Hello, Rails on Docker -
・ Vol.3 - Scaffold, RESTful, MVC -
・ Vol.4 - Static pages -
・ Vol.5 - Model and CRUD -
・ Vol.6 - Model validation -
・ Vol.7 - Secure password - ?この記事
- 投稿日:2020-02-12T15:55:22+09:00
railsメモ
- ORMとは
wikiによると
O/Rマッピングとは、オブジェクト指向言語におけるオブジェクトと、リレーショナルデータベースにおける>レコードとを対照させることである。ここでの「マッピング」とは「対応付ける」というほどの意味である。
O/Rマッピングによって、リレーショナルデータベースのレコードがオブジェクトとして直感的に扱えるよう?になり、リレーショナルデータベースにアクセスするプログラムを記述する処理を容易にすることが可能となる。オブジェクトへのデータ取得などの処理を透過的に行えるようになるので、煩雑になりがちなデータベースに関する処理の記述がスマートになり、また柔軟なアプリケーションの構築が可能となる。
なお、O/Rマッピング用のフレームワークやライブラリはO/Rマッパーなどと呼ばれる。代表的なものとしては、Java言語向けのHibernateやRuby言語向けのActiveRecordなどがある。
との事である。
つまり、ActiveRecordが入ってるおかげでスクリプト言語と同様に、翻訳しなくても自動翻訳して
データベースとやり取りすることを可能にしているというようなことだと思われる。
- database.ymlファイルの主な役割とは
db:createのコマンドを実行した時に作成されるデータベースの名称を指定する。
RailsアプリケーションがSQLサーバーにアクセスするときのソケットファイルの位置を指定する。
とのことであるがあまりよくわかっていない
- 投稿日:2020-02-12T14:20:50+09:00
Rails AWS EC2デプロイフロー ①EC2インスタンス作成編
はじめに
自分でアプリケーションは作ったものの、なかなかAWSにデプロイできなったので記録として残しておく!
わかりやすく....1.EC2インスタンスの作成
①サービスの中から 「EC2」 を選択します
②左の一覧からインスタンスをクリック
③インスタンス作成をクリック
④AMI(Amazon Machine Image)の選択
AMIとは
Amazon マシンイメージ (AMI) は、ソフトウェア構成 (オペレーティングシステム、アプリケーションサーバー、アプリケーションなど) を記録したテンプレートです。AMI から、クラウドで仮想サーバーとして実行される AMI のコピーであるインスタンスを起動します。インスタンスとは
インスタンスとは、クラウドの仮想サーバーです。起動時の設定は、インスタンスを起動した際に指定した AMI のコピーです。1 つの AMI から、複数の異なるタイプのインスタンスを起動することもできます。インスタンスタイプとは本質的に、インスタンスに使用されるホストコンピュータのハードウェアを決定するものです。インスタンスタイプごとに異なる処理内容やメモリの機能が提供されます。インスタンスタイプは、インスタンス上で実行するアプリケーションやソフトウェアに必要なメモリの量と処理能力に応じて選択します。
⑤タイプの選択
無料枠で利用できる「t2.micro」を選択しましょう。
起動をクリックします。
⑥キーペアのダウンロード
1,modalが表示されるので、【新しいキーペアの作成】を選択
2,【キーペア名】を任意で入力
3,【キーペアのダウンロード】を行う。
こちらはインスタンスにSSHでログインする際に必要となる「秘密鍵」です。これがないとEC2インスタンスにログインできないので、必ずダウンロードしてパソコンに保存しておきましょう。
4,【インスタンスの作成】をクリック
キーペアのダウンロードが完了すると、クリック出来ない状態になっていた「インスタンスの作成」が、クリックできるように変更されます。そちらをクリックして、EC2インスタンスを作成しましょう。
2.Elastic IPの作成と紐付け
Elastic IPとは
Elastic IPとは、AWSから割り振られた固定のパブリックIPアドレスのことを言います。このパブリックIPアドレスをEC2インスタンスに紐付けることで、インスタンスの起動、停止に関わらず常に同じIPアドレスで通信をすることが可能になります。
Elastic IPアドレスは、AWSに登録したアカウントに紐つけされるIPアドレスです。IPアドレスは基本的にパブリックIPアドレスとプライベートIPアドレスの2つに分けることができ、パブリックIPアドレスはインターネットを通じて機器を利用する際に割り当てられるアドレスで、最もポピュラーなIPアドレスと言えます。
一方のプライベートIPアドレスはインターネットではなくローカルのネットワークでのみ割り当てられるIPアドレスで、インターネットからは遮断されたIPです。Elastic IPの作成
2,Elastic IP アドレスの割り当てをクリック
3,割り当てをクリック,その後、閉じるをクリック
Elastic IPの紐付け
1,上図の【アクション】から【アドレスの関連付け】を選択
2,【アドレスの関連付け】ページにあるインスタンスIDを入力、【プライベートID】には入力しない、【関連付け】をクリック
3,インスタンス画面からElastic IPが紐づけられたことを確認する
3.ポートを開く
インスタンス画面
1,セキュリティグループのリンクをクリック
3,開かれたモーダルで、ルールの追加をクリック、タイプを「HTTP」、プロトコルを「TCP」、ポート範囲を「80」、送信元を「カスタム / 0.0.0.0/0, ::/0」に設定
4.EC2インスタンスへのログイン
$ cd ~ $ mv Downloads/鍵の名前.pem .ssh/ $ cd .ssh/ $ chmod 600 鍵の名前.pem $ ssh -i 鍵の名前.pem ec2-user@作成したEC2インスタンスと紐付けたElastic IP以上で、AWSのEC2インスタンスの作成手順となります!
- 投稿日:2020-02-12T12:48:32+09:00
【Rails】Passwordを無視してUserUpdate(更新)する
実装すること
・User情報Update(更新)時に、password、password_confirmationの値が空の場合でも、Updateを成功させる。
前提条件
・bcryptの導入が完了していること。
実装
例として、このようなフォームを作成して解説します。
edit.html.erb<%= form_with model: @user, local: true do |f| %> <%= f.label :content %> <%= f.text_area :content, id: "spec-content", class: "user_edit_content", value: @current_user.content %> <%= f.label :password %> <%= f.password_field :password, placeholder: "パスワード" %> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation, placeholder: "パスワード(確認)" %> <% end %>users_controller.rbdef edit @user = User.find_by(id: params[:id]) end def update @user = User.find_by(id: params[:id]) if @user.update(user_edit_params) redirect_to user_path(@user), notice: '編集されました' else render :edit end end private def user_edit_params params.require(:user).permit(:content,:password,:password_confirmation) enduser.rbhas_secure_password validations: false validates :password, presence: true, confirmation: true, length: { in: 6..50, message: 'は6〜50文字で記入してください' }このままでは、passwordを更新したくない場合でも、password、password_confirmationに値を入力しなければ、validationに引っかかってしまう。
※has_secure_password validations: false で、独自のvalidationを設定できるようにする。
ifオプションを使用する
validationにifオプションを使用する。
ifオプションは、特定の条件の場合にのみバリデーションを有効にすることが可能。user.rbhas_secure_password validations: false validates :password, presence: true, ・・・ if: :password_was_entered? def password_was_entered? password.present? || password_confirmation.present? end・password_was_entered? の解説
パスワード、またはパスワード(確認)のどちらかが入力されたらなら検証。
どちらとも入力されなかったら検証をパスする。
このようにして実装することができる。しかし、このままではUserのcreate時にもpasswordが空でもパスしてしまう。
そこで、onオプションを使用し、validatesの実行のタイミングを設定することで、パスさせないことができる。
user.rbhas_secure_password validations: false validates :password, presence: true, confirmation: true, length: { in: 6..50, message: 'は6〜50文字で記入してください' }, on: :create validates :password, presence: true, ・・・ if: :password_was_entered?, on: :update def password_was_entered? password.present? || password_confirmation.present? end最後に
初学者のためわかりづらい、適切ではない部分があるかと思われます。
お気付きの点がございましたら、お気軽にコメントいただけると幸いです。
- 投稿日:2020-02-12T12:35:58+09:00
Rails6 のちょい足しな新機能を試す 120(Rails.logger in Fiber編)
はじめに
Rails 6 に追加された新機能を試す第120段。 今回は、
Rails.logger in Fiber
編です。
Rails 6 (と Rails 5.2.4.1) では、Fiber の中でRails.logger.local_level
でログレベルを変更しても、それが親(Fiber の親)に影響を与えないようになっています。Ruby 2.6.5, Rails 6.0.2.1, Rails 5.2.4.1, Rails 5.2.3 で確認しました。 (Rails 6.0.0 でこの修正が入っています。)
$ rails --version Rails 6.0.2.1今回は、適切な利用例を思いつきませんでした。controller を1つ作って、
index
の中で、 Fiber を使って確認します。Rails プロジェクトを作る
Rails プロジェクトを新たに作成します。
$ rails new rails_sandbox $ cd rails_sandboxHome コントローラを作る
index
ビューを持つHome
コントローラを作成する。$ bin/rails g controller Home index
Controller を修正する
Controller を修正し、
index
の中で、Fiber を使います。Fiber#resume
を呼ぶ前後で、Rails.loggerapp/controllers/home_controller.rbclass HomeController < ApplicationController def index Rails.logger.level = 1 Rails.logger.info("[Before] Rails.logger.debug? #{Rails.logger.debug?}") Fiber.new do Rails.logger.local_level = 0 Rails.logger.info("[Fiber] Rails.logger.debug? #{Rails.logger.debug?}") end.resume Rails.logger.info("[After] Rails.logger.debug? #{Rails.logger.debug?}") end endrails server を実行して確認する
rails server
を実行し、 http://localhost:3000/home/index にアクセスすると以下のように出力されます。[Before] Rails.logger.debug? false [Fiber] Rails.logger.debug? true [After] Rails.logger.debug? falseRails 5 では
5.2.4 では、Rails 6 と同様の動作をしますが、 Rails 5.2.3 では、以下のように
Fiber#resume
実行後、元に戻りません。[Before] Rails.logger.debug? false [Fiber] Rails.logger.debug? true [After] Rails.logger.debug? trueその他
どうもよくわからない事象が1つ。
以下のように、Fiber.new
のブロックの中をRails.logger.info
からRails.logger.debug
に修正します。/app/... Fiber.new do Rails.logger.local_level = 0 Rails.logger.debug("[Fiber] Rails.logger.debug? #{Rails.logger.debug?}") end.resume ...そうするとなぜか以下のようにFiberの中のログ出力が無視されてしまいました。
Rails.logger.debug?
がtrue
になるので、Rails.logger.debug
でも出力されるかと思ったのですが...[Before] Rails.logger.debug? false [After] Rails.logger.debug? false試したソース
https://github.com/suketa/rails_sandbox/tree/try120_logger_in_fiber
参考情報
- 投稿日:2020-02-12T12:27:30+09:00
railsアプリをHerokuにデプロイ
Cloud9から作成したrailsアプリをHerokuにデプロイする際に
色々と躓いたので成功した手順を書き留めておきます。1.Herokuアカウント作成
以下のURLからHerokuアカウントを作成します。
https://jp.heroku.com/無料プランでも月550時間の稼働時間が利用できます。
利用が想定されるユーザの規模が非常に小さい場合は無料プランで十分だと思います。2.Herokuにアプリを作成
Herokuにログイン
$ Heroku loginプロジェクトフォルダに移動し、Herokuにrailsアプリを作成
$ Heroku create アプリ名3.Heroku用の設定
以下のようにGemfileに追加
group :production do gem 'pg', '0.20.0' end以下のようにproduction:内の記述を変更
config/database.ymlproduction: adapter: postgresql encoding: unicode pool: 5 database: フォルダ名_production username: フォルダ名 password: <%= ENV['フォルダ名_DATABASE_PASSWORD'] %>4.アプリをデプロイ
デプロイを実行
$ git push heroku masterマイグレーションを実行
$ Heroku run rails db:migrate5.うまくいかない時
エラーが出る場合は3のherokuの設定もしくはGitでコミットできているかを確認してください。
それでもエラーが出る場合は下の項目を実行してみてください。
修正後はマイグレーションを実行しましょう。PostgreSQL アドオンの追加
PostgreSQLのインストールができてない可能性があるため、手動でアドオンを追加します。
$ heroku addons:create heroku-postgresql:hobby-dev環境変数の確認
実行に必要な環境変数が空になっている可能性があります。
configでそれぞれの環境変数に値が入っているかを確認してください。$ heroku config値が空だった場合は以下のコマンドで環境変数に値を入れます。
$ heroku config:set HENSU=hensuエラーログ参照
以下のコマンドでログを確認すれば、エラーの詳細がわかります。
$ heroku logsリアルタイムで出力したいなら以下のコマンド
$ heroku logs --tail画像アップローダー
もし画像アップロード機能を実装している場合はローカルではなく
クラウド上にファイルが保存されるように設定を行う必要があります。
設定はこのあたりの記事が参考になりました。
https://qiita.com/junara/items/1899f23c091bcee3b058
https://qiita.com/daichi41/items/af2a56ea46c13ca55fd3
https://qiita.com/hmmrjn/items/479c9e9ce82771f1b6d7
https://pg-happy.jp/rails-aws-s3-upload.html
- 投稿日:2020-02-12T11:15:17+09:00
【Rails+MySQL】MySQLのIDを1から振り直す
MySQLを使用してアプリ作成していると、テストデータを追加したり消したりしてIDがぽこぽこ抜けちゃうことが多いと思います。
IDをもう一度1から振り直してデータを綺麗に整えたい時に以下の方法を使うと楽だったので共有できたらと思います。
アプリのデータベースの確認
MySQLに入って以下のコマンドを打ちます。
> use app_name_development;Railsで作ったテーブルが保存されてるデータベースです。
Idを1から振り直す
「delete from テーブル名」でテーブルのデータを消してから以下を実行
ALTER TABLE `テーブル名` auto_increment = 1;これで中身が綺麗に振り直されてるのではないでしょうか?
まとめ
テストデータ管理はなんだかんだ大変なので参考になれば幸いです。
- 投稿日:2020-02-12T10:10:21+09:00
Rails6 each を使った時にDBのレコードの内容が全て出力されてしまう
目的
- eachを用いた時に意図しない出力になってしまったので解決方法を記載する。
症状
下記のコードを用いてDBのとあるテーブルのレコードの内容を出力したところレコードの全ての内容が最後に出力されてしまう。
<%= @posts.each do |post| %> <%= post.content %><br> <%= post.link %><br> <% end %>↓出力
testでーす https://qiita.com/miriwo [#<Post id: 1, content: "testでーす", created_at: "2020-02-08 00:54:38", updated_at: "2020-02-08 00:54:38", link: "https://qiita.com/miriwo">]原因
- 単純なミス
@posts.each do |post|
の部分を<%= %>で囲んでしまっていたため、処理部分が出力されてしまった。正しいコード
下記に正しいコードを記載する。
<% @posts.each do |post| %> <%= post.content %><br> <%= post.link %><br> <% end %>↓出力
testでーす https://qiita.com/miriwo教訓
<%= %>
は結果を出力したい時のみ使用する。<% %>
は結果を出力したくない時に使用する。
- 投稿日:2020-02-12T08:08:37+09:00
RailsにReactやVueはいらない? ajaxでviewを非同期で操作するgem(ActionPartial)を作りました。
ActionPartial
個人でCrover(クリエイターズプラットフォーム)というサービスを企画&開発&運営しているnirと申します。
Croverの開発で動的な
view
の実装が必要になったので、gem
を作ることにしました。それで出来上がったのが、
Rails
で動的なview
を簡単に実現できるgem
のActionPartial
です。Crover(クリエイターズプラットフォーム)
→ https://crover.megithub(ActionPartial)
→ https://github.com/nir-searchright/actionpartialなぜ、
React
やVue
を使わなかったかというと
- 学習コストが高い
- フレームワークにフレームワークを組み込むことに違和感がある
- 設計思想的な問題(Rails
をRails
のまま使いたい)自分だけで使うはもったいないので、とりあえず公開することにしました。
注意)
-React
やVue
を否定する記事ではありません。DEMO
簡易的なデモを作成したのでご自由にお試しください。
デモ
→ https://actionpartial-demo.herokuapp.comgithub(ActionPartial DEMO)
→ https://github.com/nir-searchright/actionpartial_demo
heroku
の無料プランなのでデモのページを開くのに時間がかかるかもしれません。ここから先はデモを元に説明します。
導入方法
Gemfile
に'actionpartial', github: 'nir-searchright/actionpartial'
を追加してbundle install
します。現状、RubyGemsには登録していないので
github
のurl
は必須です。gem 'actionpartial', github: 'nir-searchright/actionpartial'
application_helper.rb
でrequire
するだけで準備は完了です。application_helper.rbrequire 'action_partial'ActionPatialができること
ActionPartial
ができることは
partial
の更新partial
の追加partial
の削除これらの単純な機能だけです。
単純故に応用も利きやすい設計となっているはずです。(多分)
仕組み
前提として、
Rails
ではlink_to
やform
にremote: true
をつけるとajax
で通信が行われます。<%= form_for(@post, remote: true) do |f| %> 内容 <% end %>
ajax
でリクエストを送り、サーバーからxxx.js.erb
を返します。def create 内容 render 'posts/js_erb/create.js.erb' endサーバーから返された
xxx.js.erb
でview
に
-innerHTML
でpartial
を更新(https://developer.mozilla.org/ja/docs/Web/API/Element/innerHTML)
-insertAdjacentHTML
で任意の位置にpartial
を挿入(https://developer.mozilla.org/ja/docs/Web/API/Element/insertAdjacentHTML)
-remove
でpartial
を削除(https://developer.mozilla.org/ja/docs/Web/API/ChildNode/remove)
などをしています。helperメソッド一覧
html.erb
で使うヘルパーindex.html.erb<%= ap_render "posts/list/container", class: "posts-padding-bottom", locals: {posts: @posts} %> <%= ap_render "posts/new" %>
<%= ap_init(path, options={}) %>
- 最初は表示したくないけど、後からコンテンツを追加したい場所に使います。
- エラーメッセージなどの後から表示する要素に使います。
<%= ap_render(path, options={}) %>
- 非同期で更新したい
partial
をrender
するのに使います。- 基本的に
Rails
が提供するrender
と大して変わりません。
js.erb
で使うヘルパー※サンプルコードは下の個別の説明に載せてあります。
<%= ap_before(path, options = {}) %>
ap_init
もしくはap_render
したpartial
の外側上部にpartial
が挿入されます。
<%= ap_prepend(path, options = {}) %>
ap_init
もしくはap_render
したpartial
の内側上部にpartial
が挿入されます。
<%= ap_append(path, options = {}) %>
ap_init
もしくはap_render
したpartial
の内側下部にpartial
が挿入されます。
<%= ap_after(path, options = {}) %>
ap_init
もしくはap_render
したpartial
の外側下部にpartial
が挿入されます。
<%= ap_replace(path, options = {}) %>
ap_init
もしくはap_render
したpartial
がサーバーから取得した新しいpartial
に置換されます。
<%= ap_remove(id) %>
html
上に存在する要素をid
指定で削除します。基本の使い方
基本は更新or追加or削除したい
partial
のid
(ap_init
もしくはap_render
した場合はpartial
のpath名
)を指定して使います。
partial
でインスタンス変数を使っている場合はlocals
で変数を渡します。add.js.erb<%= ap_append("posts/list/loop", locals: {posts: @posts}) %>詳しくはDEMOの
add.js.erb
をご確認ください。(このコードは無限スクロールのコードです。)
他の要素や
partial
にpartial
を追加(挿入)したい場合追加したい要素や
partial
のid
(ap_init
もしくはap_render
した場合はpartial
のpath名
)を指定することでpartial
の追加(挿入)が可能です。create.js.erb<%= ap_prepend("posts/list/item", id: "posts/list/loop", class: "posts-list-item", locals: {post: @post}) %>詳しくはDEMOの
create.js.erb
をご確認ください。(このコードは新規投稿のコードです。)複数の
partial
から一つのpartial
を指定したい場合
js.erb
で使えるヘルパーはoption
でid
を指定することができます。例えば
html.erb
でこんな感じでeach
で回しながらコンテンツにid
を指定することでjs.erb
でコンテンツを指定することが可能です。<% posts.each do |post| %> <div id="posts_<%= post.id %>"> 内容 </div> <% end %>詳しくはDEMOの
update.js.erb
destroy.js.erb
calcel.js.erb
をご確認ください。jsの実行
当たり前と言えば当たり前ですが
js.erb
にjs
のコードを書いておけば一緒に実行できます。create.js.erb<%= ap_prepend("posts/list/item", id: "posts/list/loop", class: "posts-list-item", locals: {post: @post}) %> document.getElementById("post_content").value = ""; scrollTo(0, 0);ただ、この
js
が実行できてしまう仕組みがセキュリティ的に良いのかは分かりません。理論上だと
https
通信をしているなら問題ないはずですが、どうなのでしょうか?どなたか詳しい方がいればコメントで教えていただけると助かります。
最後に
今回やったことは「ネット上でよく見かける
js.erb
での非同期更新を簡単に使えるようにgem化した」だけです。作ってみて思ったよりも使いやすかったのでとりあえず公開しました。
ActionPartial
は学習コスト低めでhtml
とjs
とrails
だけ理解していれば使えるのでスピーディーな開発ができると思います。スピードを重視するスタートアップのプロジェクトとの相性が良いのではないでしょうか?
機能的にはシンプルな
gem
なので導入で不具合を吐くことはほとんどないと思います。そのうち気が向いたら
rails ujs
を使ったコンテンツの非同期更新の記事もアップします。
- 投稿日:2020-02-12T08:08:37+09:00
RailsにReactやVueは不要!? ajaxでviewを非同期で操作するgem(ActionPartial)を作りました。
ActionPartial
個人でCrover(クリエイターズプラットフォーム)というサービスを企画&開発&運営しているnirと申します。
Croverの開発で動的な
view
の実装が必要になったので、gem
を作ることにしました。それで出来上がったのが、
Rails
で動的なview
を簡単に実現できるgem
のActionPartial
です。Crover(クリエイターズプラットフォーム)
→ https://crover.megithub(ActionPartial)
→ https://github.com/nir-searchright/actionpartialなぜ、
React
やVue
を使わなかったかというと
- 学習コストが高い
- フレームワークにフレームワークを組み込むことに違和感がある
- 設計思想的な問題(Rails
をRails
のまま使いたい)自分だけで使うはもったいないので、とりあえず公開することにしました。
注意)
-React
やVue
を否定する記事ではありません。DEMO
簡易的なデモを作成したのでご自由にお試しください。
デモ
→ https://actionpartial-demo.herokuapp.comgithub(ActionPartial DEMO)
→ https://github.com/nir-searchright/actionpartial_demo
heroku
の無料プランなのでデモのページを開くのに時間がかかるかもしれません。ここから先はデモを元に説明します。
導入方法
Gemfile
に'actionpartial', github: 'nir-searchright/actionpartial'
を追加してbundle install
します。現状、RubyGemsには登録していないので
github
のurl
は必須です。gem 'actionpartial', github: 'nir-searchright/actionpartial'
application_helper.rb
でrequire
するだけで準備は完了です。application_helper.rbrequire 'action_partial'ActionPatialができること
ActionPartial
ができることは
partial
の更新partial
の追加partial
の削除これらの単純な機能だけです。
単純故に応用も利きやすい設計となっているはずです。(多分)
仕組み
前提として、
Rails
ではlink_to
やform
にremote: true
をつけるとajax
で通信が行われます。<%= form_for(@post, remote: true) do |f| %> 内容 <% end %>
ajax
でリクエストを送り、サーバーからxxx.js.erb
を返します。def create 内容 render 'posts/js_erb/create.js.erb' endサーバーから返された
xxx.js.erb
でview
に
-innerHTML
でpartial
を更新(https://developer.mozilla.org/ja/docs/Web/API/Element/innerHTML)
-insertAdjacentHTML
で任意の位置にpartial
を挿入(https://developer.mozilla.org/ja/docs/Web/API/Element/insertAdjacentHTML)
-remove
でpartial
を削除(https://developer.mozilla.org/ja/docs/Web/API/ChildNode/remove)
などをしています。helperメソッド一覧
html.erb
で使うヘルパーindex.html.erb<%= ap_render "posts/list/container", class: "posts-padding-bottom", locals: {posts: @posts} %> <%= ap_render "posts/new" %>
<%= ap_init(path, options={}) %>
- 最初は表示したくないけど、後からコンテンツを追加したい場所に使います。
- エラーメッセージなどの後から表示する要素に使います。
<%= ap_render(path, options={}) %>
- 非同期で更新したい
partial
をrender
するのに使います。- 基本的に
Rails
が提供するrender
と大して変わりません。
js.erb
で使うヘルパー※サンプルコードは下の個別の説明に載せてあります。
<%= ap_before(path, options = {}) %>
ap_init
もしくはap_render
したpartial
の外側上部にpartial
が挿入されます。
<%= ap_prepend(path, options = {}) %>
ap_init
もしくはap_render
したpartial
の内側上部にpartial
が挿入されます。
<%= ap_append(path, options = {}) %>
ap_init
もしくはap_render
したpartial
の内側下部にpartial
が挿入されます。
<%= ap_after(path, options = {}) %>
ap_init
もしくはap_render
したpartial
の外側下部にpartial
が挿入されます。
<%= ap_replace(path, options = {}) %>
ap_init
もしくはap_render
したpartial
がサーバーから取得した新しいpartial
に置換されます。
<%= ap_remove(id) %>
html
上に存在する要素をid
指定で削除します。基本の使い方
基本は更新or追加or削除したい
partial
のid
(ap_init
もしくはap_render
した場合はpartial
のpath名
)を指定して使います。
partial
でインスタンス変数を使っている場合はlocals
で変数を渡します。add.js.erb<%= ap_append("posts/list/loop", locals: {posts: @posts}) %>詳しくはDEMOの
add.js.erb
をご確認ください。(このコードは無限スクロールのコードです。)
他の要素や
partial
にpartial
を追加(挿入)したい場合追加したい要素や
partial
のid
(ap_init
もしくはap_render
した場合はpartial
のpath名
)を指定することでpartial
の追加(挿入)が可能です。create.js.erb<%= ap_prepend("posts/list/item", id: "posts/list/loop", class: "posts-list-item", locals: {post: @post}) %>詳しくはDEMOの
create.js.erb
をご確認ください。(このコードは新規投稿のコードです。)複数の
partial
から一つのpartial
を指定したい場合
js.erb
で使えるヘルパーはoption
でid
を指定することができます。例えば
html.erb
でこんな感じでeach
で回しながらコンテンツにid
を指定することでjs.erb
でコンテンツを指定することが可能です。<% posts.each do |post| %> <div id="posts_<%= post.id %>"> 内容 </div> <% end %>詳しくはDEMOの
update.js.erb
destroy.js.erb
calcel.js.erb
をご確認ください。jsの実行
当たり前と言えば当たり前ですが
js.erb
にjs
のコードを書いておけば一緒に実行できます。create.js.erb<%= ap_prepend("posts/list/item", id: "posts/list/loop", class: "posts-list-item", locals: {post: @post}) %> document.getElementById("post_content").value = ""; scrollTo(0, 0);ただ、この
js
が実行できてしまう仕組みがセキュリティ的に良いのかは分かりません。理論上だと
https
通信をしているなら問題ないはずですが、どうなのでしょうか?どなたか詳しい方がいればコメントで教えていただけると助かります。
最後に
今回やったことは「ネット上でよく見かける
js.erb
での非同期更新を簡単に使えるようにgem化した」だけです。作ってみて思ったよりも使いやすかったのでとりあえず公開しました。
ActionPartial
は学習コスト低めでhtml
とjs
とrails
だけ理解していれば使えるのでスピーディーな開発ができると思います。スピードを重視するスタートアップのプロジェクトとの相性が良いのではないでしょうか?
機能的にはシンプルな
gem
なので導入で不具合を吐くことはほとんどないと思います。そのうち気が向いたら
rails ujs
を使ったコンテンツの非同期更新の記事もアップします。