- 投稿日:2019-10-01T23:16:23+09:00
【Rails】Slim基本文法【初心者】
はじめに
Railsを使った開発で使うことになるであろうSlimについて簡潔にまとめてみました。
Slimとは
Railsで使用できるRuby製のHTMLテンプレートエンジン。
Slimのメリット
HTMLをよりシンプルに見やすく書くことができる。
Slim導入
Slimジェネレータを提供してくれる
slim-rails
とERB形式のファイルをSlim形式に変換してくれるhtml2slim
という2つのGemをインストールgem 'slim-rails' gem 'html2slim'Slimへ変換
すでにあるerb形式のファイルをSlim形式に変換するときは以下のコマンド
$ bundle exec erb2slim 変換したいerbファイルのパス --deleteSlim基本文法
<>
が不要、や<% end %>等の閉じタグも不要
<%= %>
→=
<% %>
→-
id指定 →#
class指定 →.
コメント →/
改行 →|
ERBとSlimの比較
最初にERB形式
example.html<div class="contents nav"> <ul class="navbar-nvv"> <% if current_user %> <li><%= link_to("MyPage", user_path, class: "nav-link") %></li> <% else %> <li><%= link_to("LogIn", login_path, class: "nav-link") %></li> <% end %> </ul> # これはコメントです <p id="greeting">Hello World!!</p> </div>次がSlim形式
example.html.slim.contents.nav ul.navbar-nav - if current_user li= link_to "MyPage", user_path, class: "nav-link" - else li= link_to "LogIn", login_path, class: "nav-link" / これはコメントです p#greeting Hello World!!さいごに
かなりスッキリしましたね!
以上Slimの基本文法でした。
- 投稿日:2019-10-01T22:58:51+09:00
WSL上でRuby on Rails + MySQLの環境づくり
はじめに と 注意
Ruby on Rails + MySQLの環境構築をしていく記事です。
先人様の記事の内容を主に進めていって、エラーが出たとこの解決策を書いてます。
初記事なのでやさしい目で見てください。背景
Rails + MySQL でアプリ作ってみたいけど環境構築で意味わかんないエラー出ちゃった。
全部Windowsのせいにしよう(逃避)。
でもmac持ってない......WSLならできるかな...?(ちなみに過去にWSL上で開発環境整えようとして失敗した。)
対象
- WSL上でRuby on Railsの開発をしたい!
- SQLiteじゃなくてMySQLでやりたいけど環境構築わかんね!
という方向け。
環境
- Windows 10 Home 64bit
- WSL( Windows Subsystem for Linux ) Ubuntu 18.04.3 LTS
1. WSLにRuby on Rails環境を整える
さっそく他人様まかせで申し訳ないですが
とても、とても素晴らしい記事があるので
ほぼこれを参考にやっていきます...
- Windows10 で WSL を使って Rails 環境を構築したときのメモ
- WSL導入もあります、シンボリックリンクまでやってください!
- Rubyのバージョンは2.6.3にしました。
※一部別の方法でインストールする部分があるのでリンク先とこの記事を見比べながら作業してください。
(一個一個のインストールに割と時間かかるので気長に待ちましょう。特にrubyのインストール)少し変更を加える部分 (と軽いhelp)
「2. Node.jsを入れよう」部分
nodejsを導入する際の
sudo apt-get install npmというコマンドだとnodeおよびnpmのバージョンが最新ではないためやり方を変更。
間違えて入れちゃったというひとも指示に従ってすれば大丈夫(たぶん)さあ説明を読むぞって思ったひとにはごめんなさい。この記事が神。
- 続・WSL(Ubuntu)でnode.jsの開発をする準備さっきのコマンドで入れちゃったって人は「削除する」って項目からやってください。
もう1つ
WSLを再起動とかすると
node -v
をしたときにnot found エラーが起きるかも。
そういうときはexport PATH=$HOME/.nodebrew/current/bin:$PATHを vim かなんかで
~/.bashrc
に追記して試してみてください。
vim で追記する方法は下の方。「3. rbenvを入れよう」部分
たいしたことではないですが最後の方で
export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)"を ~/.bashrc に追記する(厳密には上の1行はすでに追記されているはず)手順があります。
「どうやって!?」と思った方は下の方に書いてある「.bashrcに追記ってどうするの!!」を見て追記してみてください。「5. Rubyを入れよう」部分
本当に時間かかります。環境にもよると思うけれど僕は2~30分かかりました(笑)
解決策?
- 普通は長くて10分くらいらしいのでいったん待ってみる。
- "rbenv install ruby 遅い"でググって出てきたのを試す
BUILED ERROR!! となる人へ
僕も一度なりました。エラー文中に
Try ~~~ `apt -------`といった感じであったので(詳細なエラー文コピー取るの忘れてました;;)、
それに従ってコマンド実行すると治りました。.bashrcに追記ってどうするの!!
下のコマンドで、vim で .bashrc を開いて一番下とかに追記していく。
vim ~/.bashrcvim の使い方はこちらでどうぞ
- 知識0から始めるVim講座2. Ruby on Rails + MySQL
Ruby on Railsの環境構築お疲れさまでした。
僕自身この記事を書きながらやっていたら3時間程度たっていました...笑
ということで次はMySQLです。MySQLのインストール
まずはMySQLをインストールしていきます。が、ここでもとても良い記事を見つけました。
- WSLのUbuntu 18.04でMySQL 5.7をセットアップこれ通りに行えばエラーなく行けるはずですが、
過去にMySQLいれたことある人や間違えちゃった人はエラーが起こるかも。(体験談)
僕の場合はWSL上からきれいにMySQLを消してもう一回やればいけました。
過去のMySQL関連のファイルとか消したくないって人以外は下記コマンドを実行しましょう。sudo apt-get remove --purge mysql-* sudo apt-get autoremove --purge sudo rm -r /etc/mysql sudo rm -r /var/lib/mysqlMySQLが起動中だとうまく全ファイルを消せないので、
sudo service mysql stopとしてサーバを停止してから行ってください。
またサーバが止まらない!止められない!という方は
MySQLのプロセスを確認してps ax | grep mysqlでてきたプロセスを片っ端から
kill
していけばサーバが停止します。sudo kill プロセス番号Railsと組み合わせる
MySQLのインストールが終わったのでRailsアプリケーションのデータベースをMySQLとして作っていきます。
まずはRailsで使うMySQLのアカウントを作ります!
rootでログインして、mysql -u root -p現在あるアカウントをチェック
select User,Host from mysql.user; +------------------+-----------+ | User | Host | +------------------+-----------+ | debian-sys-maint | localhost | | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | +------------------+-----------+Rails用のアカウントを作ってチェック
create user 'ユーザネーム'@'localhost' identified by 'パスワード'; select User,Host from mysql.user; +------------------+-----------+ | User | Host | +------------------+-----------+ | debian-sys-maint | localhost | | mysql.session | localhost | | mysql.sys | localhost | | railsuser | localhost | | root | localhost | +------------------+-----------+ちゃんと追加されました!
次はRails上でMySQLを使うためのgemをインストールしていきます。
gem install mysql2しかしここでエラー
ERROR: Error installing mysql2: ERROR: Failed to build gem native extension. -省略- mysql client is missing. You may need to 'apt-get install libmysqlclient-dev' or 'yum install mysql-devel', and try again. -省略-指示に従って、一応
sudo
をつけて...sudo apt-get install libmysqlclient-devもう一回..!
$ gem install mysql2 Building native extensions. This could take a while... ... Done installing documentation for mysql2 after 0 seconds 1 gem installed通りました。
次にデータベースをMySQLに指定したRailsアプリケーションを作成。名前は適当。
rails new mysql-app -d mysql次にデータベース設定ファイルの書き換えをします。
Ruby on Railsの環境を構築したときにシンボリックリンクを設定したフォルダがあると思うのでそこに先ほど作ったRailsアプリケーションを移動。
最初からそこでrails new
すればよかったのでは?mv アプリの名前 移動先 -rvscodeを起動
ファイル > フォルダを開く > Railsアプリ(僕の場合mysql-app)
を選択してvscodeで開く。
config > database.yml
を開いて自分の登録したユーザネームとパスワードを入力します。default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: [ユーザネーム] password: [パスワード] socket: /var/run/mysqld/mysqld.sock development: <<: *default database: mysql_app_development test: <<: *default database: mysql_app_test production: <<: *default database: mysql_app_production username: mysql_app password: <%= ENV['MYSQL_APP_DATABASE_PASSWORD'] %>セーブ後データベースを作って
rails db:createアプリケーションを起動して
rails sブラウザ(http://localhost:3000) で確認出来たら終了です!
お疲れさまでした。参考
- Ruby on RailsでMySQLを使用・接続する際の手順と注意点
- RailsのDBを(初めから| |後から)MySQLに変更する
- 投稿日:2019-10-01T22:47:42+09:00
【Rails】画像の表示方法
画像の表示方法でウンウン詰まったのでメモに残しておきます。
方法
2種類あり、それぞれ特徴やパスの書き方が違うみたいです。
1、assets配下に置く場合
格納先:
/app/assets/images/
今回はこちらを使用することにしました。
/app/assets/
内のファイルはアセットパイプラインの対象となる→結果ページ読み込み速度が早くなる。
※アセットパイプライン
ファイルを最小化(圧縮)して読み込みを早くしてくれるフレームワーク。(初期装備のよう)
アセットを連結することによって、ページリクエスト数を減らすことができる=更にページ速度向上につながるという仕組み。
画像であればブラウザのキャッシュを残すようにしてくれるため、画像の読み込みが早くなる。
ただし、キャッシュを飛ばさないと画像が正しく表示されない、など起きる。
リファレンスの内容が思ったよりあったので、これは今後詳しく学ぶことにします。書き方
htmlで書く場合<img src="/assets/img_logo_pc.png" alt="">「imagesいらないのかーい!」という不意打ちでした。
ヘルパーの場合<%= image_tag 'img_logo.png' %>ディレクトリ分けしたくなる気持ちがありましたが、そちらはいったん寝かしておきます。
2、public配下に置く場合
格納先:
/public/
ユーザーがアップロードする、などの場合はこちらがいいようです。
こちらは後ほど使用する際にまた追記します。判断基準
assets=「システム側に依存する画像関係」の場合に使用
public=「ユーザーがアップロードするような画像関係」の場合に使用とあった。
ざっと調べただけでは明確にわからなかったので、
現状まずは進めつつ今後深く考えていくことにします。参考サイト
アセットパイプライン - Rails ガイド
Railsの画像のパス – assets配下とpublic配下 – | Boys Be Engineer 非エンジニアよ、エンジニアになれ
image_tagメソッドを使ったイメージタグの作成 - Ruby on Rails入門
- 投稿日:2019-10-01T21:50:07+09:00
rails tutorial勉強中にparamsメソッドが分からなかったので調べてみた
rails tutorial 8章を勉強中にparamsが分からなかった
rails tutorial8章 8.1.3節 ユーザーの検索と認証 に差し掛かった時、ん?と思った。
この節ではユーザーがログイン画面に入力したemailとpasswordを受け取って、
データベース上に存在しかつ正しいemailとpasswordの組み合わせになっているかを
確認するものだ。params[:session] { session: { password: "foobar", email: "user@example.com" } }画面に入力されたデータはこの形で受け取るらしいが、ここで詰まった。
what is paramsとsession ??
params?session?そんなの書いた覚えは無い…railsのお約束みたいなものなのかな?
ググってたらいいページを2つ見つけた。
ピカわか【Rails】paramsについて徹底解説!
WEBCAMP NEVI【Rails入門説明書】paramsについて解説このページのおかげでかなり理解が深まりました。
they are hash!
- 画面に入力された際に生まれたデータは すべてparamsメソッドにハッシュの形式でまとめられる
- paramsメソッドの中にいくつかあるハッシュのうち、 emailとpasswordはsessionメソッドの中に収納される
とりあえずこんな感じに理解しました。
おまけにdebugger
debuggerでparams見たらもっと分かりそう。
そんな感じでdebugger使ってみました。app/controllers/sessions_controller.rbdef create user = User.find_by(email: params[:session][:email].downcase) debugger #ここdebugger if user && user.authenticate(params[:session][:password]) # ユーザーログイン後にユーザー情報のページにリダイレクトする else # エラーメッセージを作成する render 'new' end end11: else 12: # エラーメッセージを作成する 13: render 'new' (byebug) params <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"LxkdeujFgCWan2zWZJXo+NAsl6FWTvQuOIleI6sr/rGV37pREiUahEJn8GH6Jet8YHs5/xT2tJIQBMIWpBz5EQ==", "session"=><ActionController::Parameters {"email"=>"spdfmasd@gmail.com", "password"=>"s;dlfka;sd"} permitted: false>, "commit"=>"Log in", "controller"=>"sessions", "action"=>"create"} permitted: false> (byebug) params[:session] <ActionController::Parameters {"email"=>"spdfmasd@gmail.com", "password"=>"s;dlfka;sd"} permitted: false> (byebug) params[:utf8] "✓"おー本当だ。文字コードすらハッシュで保存されてる。
終わりに
どれくらいの人がここで躓くのかわかりませんが、今後同じような状況で悩む人の手助けになればと思います。
間違いなどありましたらぜひともご指摘をお願いいたします。
- 投稿日:2019-10-01T21:21:31+09:00
【Rails】【docker-compose】Railsサーバーが立ち上がらなかった場合の対処方法
Railsサーバーが立ち上がらなくてつまずいたのでメモ。
Docker初心者です。現象
docker-compose up -d
でコンテナを起動しようとしたら何故か立ち上がらない・・・。
docker-compose up
やdocker-compose start
を試してみたけれどlocalhost:3000で表示されず。
docker-compose ps
(コンテナの確認)を実行してみると、どうやら「project_web_1」のステータスがExit 1
となっているのが原因っぽい。Name Command State Ports ----------------------------------------------------------------------------- project_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp project_web_1 bundle exec rails s -p 300 ... Exit 1
docker logs task_webserver_1
を実行すると以下のログが表示された。
※ここでdocker
をdocker-compose
にしていたので全然表示されなかった。。=> Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.12.1 (ruby 2.6.4-p104), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://0.0.0.0:3000 Use Ctrl-C to stop Started GET "/" for 172.21.0.1 at 2019-09-30 10:46:07 +0000 Cannot render console from 172.21.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 (0.5ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 ↳ /usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98 Processing by Rails::WelcomeController#index as HTML Rendering /usr/local/bundle/gems/railties-5.2.3/lib/rails/templates/rails/welcome/index.html.erb Rendered /usr/local/bundle/gems/railties-5.2.3/lib/rails/templates/rails/welcome/index.html.erb (5.2ms) Completed 200 OK in 28ms (Views: 22.1ms | ActiveRecord: 0.0ms) Started GET "/" for 172.21.0.1 at 2019-09-30 11:39:02 +0000 Cannot render console from 172.21.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 (0.5ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 ↳ /usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98 Processing by Rails::WelcomeController#index as HTML Rendering /usr/local/bundle/gems/railties-5.2.3/lib/rails/templates/rails/welcome/index.html.erb Rendered /usr/local/bundle/gems/railties-5.2.3/lib/rails/templates/rails/welcome/index.html.erb (1.6ms) Completed 200 OK in 6ms (Views: 4.5ms | ActiveRecord: 0.0ms) Started GET "/" for 172.21.0.1 at 2019-09-30 13:21:17 +0000 Cannot render console from 172.21.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 (0.5ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 ↳ /usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98 Processing by Rails::WelcomeController#index as HTML Rendering /usr/local/bundle/gems/railties-5.2.3/lib/rails/templates/rails/welcome/index.html.erb Rendered /usr/local/bundle/gems/railties-5.2.3/lib/rails/templates/rails/welcome/index.html.erb (1.6ms) Completed 200 OK in 4ms (Views: 3.4ms | ActiveRecord: 0.0ms) Started GET "/home/top/" for 172.21.0.1 at 2019-09-30 13:21:23 +0000 Cannot render console from 172.21.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by HomeController#top as HTML Rendering home/top.html.erb within layouts/application Rendered home/top.html.erb within layouts/application (0.3ms) Completed 200 OK in 2242ms (Views: 2226.3ms | ActiveRecord: 0.0ms) Started GET "/home/top/" for 172.21.0.1 at 2019-09-30 13:21:40 +0000 Cannot render console from 172.21.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by HomeController#top as HTML Rendering home/top.html.erb within layouts/application Rendered home/top.html.erb within layouts/application (0.4ms) Completed 200 OK in 149ms (Views: 131.7ms | ActiveRecord: 0.0ms) Started GET "/home/top/" for 172.21.0.1 at 2019-09-30 13:30:56 +0000 Cannot render console from 172.21.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 (0.9ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483 ↳ /usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98 Processing by HomeController#top as HTML Rendering home/top.html.erb within layouts/application Rendered home/top.html.erb within layouts/application (0.9ms) Completed 200 OK in 124ms (Views: 108.3ms | ActiveRecord: 0.0ms) Started GET "/home/top/" for 172.21.0.1 at 2019-09-30 13:31:00 +0000 Cannot render console from 172.21.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by HomeController#top as HTML Rendering home/top.html.erb within layouts/application Rendered home/top.html.erb within layouts/application (0.4ms) Completed 200 OK in 129ms (Views: 113.8ms | ActiveRecord: 0.0ms) - Gracefully stopping, waiting for requests to finish === puma shutdown: 2019-09-30 14:23:32 +0000 === - Goodbye! Exiting => Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options A server is already running. Check /app/tmp/pids/server.pid. Exiting => Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options A server is already running. Check /app/tmp/pids/server.pid. Exiting => Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options A server is already running. Check /app/tmp/pids/server.pid. Exiting => Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options A server is already running. Check /app/tmp/pids/server.pid. Exiting => Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options A server is already running. Check /app/tmp/pids/server.pid. Exitingメチャクチャ長文・・・。
とりあえずA server is already running. Check /app/tmp/pids/server.pid.
の文がやたらとあったので、調べてみた。すると以下の方の記事に遭遇。
【docker-compose】Railsサーバが起動しない場合の対処法 - Qiitaほぼ一緒の現象でしたので「これだ!」と思い参考にさせていただきました。
記事どおり
tmp/pids/server.pid
を削除してdocker-compose up -d
で再度起動。
無事表示されました。これ覚えておこう。
参考サイト
途中で参考になった記事です。
DockerでRuby on Railsの開発をしよう - Qiita
- 投稿日:2019-10-01T20:54:11+09:00
【rails】jQueryでいいね機能を非同期化しよう
やりたいこと
いいねを非同期でやりたい!!
railsの勉強し始めてしばらく同期通信しかやっていませんでした。。
そんな時、Ajaxという言葉を見つけました。
Ajax?何やと調べてみると非同期通信の意味らしい
非同期通信があると一つのページでいろいろなことができるようになる!!
ページに新しい情報を増やす時はだいたい非同期通信が使われていますこの記事を理解して非同期通信できるようになってください!!!
方法
今回の方法は以下の通りです
1. ボタンをクリックでAjax(非同期通信)でアクションへ送信
2. アクションで処理を進め、ビューへデータを返す
3. Ajaxが成功したらHTMLを書き換えるボタンをクリックでAjax(非同期通信)でアクションへ送信
ビューファイルから押すとAjaxでアクションへ送信するボタンを実装します
では、説明していきますlink_to
一般的な方法でいいね機能を実装するのであれば、このメソッドを使っていると思います
追加するのはremote true
とclass: "post_favorites_create"
です
remote true
は非同期通信にするための記述です。
class: "post_favorites_create"
はJaveScriptで反応させるために設定しています
クラス名は好きな名前にしてください
<%if @post.favrite?(current_user)%>
current_userが@postに対していいねをしてるかどうかのチェックです。
いいね実装時に条件分岐していると思うので、それに合わしてください。posts/show.html.erb<div class="favorite"> <%if @post.favrite.nil?%> <%= link_to post_favorites_path(@post), id: "post_favorites_deatroy", method: :delete, remote: true do %> <span>❤︎</span> <%end%> <%else%> <%= link_to post_favorites_path(@post), id: "post_favorites_create", method: :post, remote: true do %> <span style="color:red;">❤︎</span> <%end%> <%end%> </div>アクションで処理を進め、ビューへデータを返す
app/controllers/favorites_controller.rbdef create @post_favorites = Favorite.new(user_id: current_user.id,post_id: params[:post_id]) @post_favorites.save result = [done: "save",user_id: current_user.id, post_id: params[:post_id]] render :json => result end def destroy @post_favorites = Favorite.find_by(user_id: current_user.id, post_id: params[:post_id]) @post_favorites.destroy result = [done: "destroy",user_id: current_user.id, post_id:params[:post_id]] render :json => result endresultを定義するときの
done
はビューにresultを返した時に判断するための値です
render :json => result
resultをjson形式でビューに返すということです。Ajaxが成功したらHTMLを書き換える
$(document).on('ajax:success', '.favorite a', function(e) { Ajaxに成功した時の処理 });favoriteクラスないのaタグからのajaxが成功した時にfunction(e)を実行するという意味です
eはajaxで返ってきた値をeとして扱うapplication.js$(document).on('ajax:success', '.favorite a', function(e) { if (e.detail[0][0].done == "save"){ var post_fav = document.getElementById('post_fav') post_fav.innerHTML = '<a id="post_favorites__deatroy" data-remote="true" rel="nofollow" data-method="delete" href="posts/'+e.detail[0][0].post_id+'/favorites"><span style="color:red;">❤︎</span></a>' } if (e.detail[0][0].done == "destroy"){ var post_fav = document.getElementById('post_fav') post_fav.innerHTML = '<a id="post_favorites_create" data-remote="true" rel="nofollow" data-method="post" href="posts/'+e.detail[0][0].post_id+'/favorites"><span>❤︎</span></a>' } });これで非同期化が完了です
まとめると
link_toにremote: true
、id名の追記
redirect_toをrender :json =>
に変更
jsファイルにajax成功時の処理を記述する
以上3点です。以上のことを設定すると同期通信を非同期通信にすることができます
いいね以外にも他の機能を非同期にしてみましょう!!質問、気になるところなどございましたらコメントよろしくお願いします
- 投稿日:2019-10-01T19:19:40+09:00
Railsで作成したプロダクトをHerokuでデプロイする方法
はじめに
今までで、私はHerokuを使って、今回も含め3度しかデプロイをしたことがない。
- Railsで作った「タスク管理アプリケーション」
- HTML,CSS,だけの静的サイト
- Rails開発合宿で作った「Web系企業の紹介サイト」
つまり、いわゆる私はHeroku初心者と言わざるを得ない。しかし、そんな私でもQittaに投稿されている他の記事を参考にしてデプロイをできたのだから、きっとあなたも大丈夫なことを保障しよう。(この記事内容では保障できないが、、、)
あなたに少しでも参考になればと思っている。読者対象
- MacユーザでHerokuを使いデプロイを初めてやる方
- Herokuのちょっとしたコマンでを知りたい方
- 今後、Herokuでデプロイをやることになった自分
前提知識&準備
手法
Herokuでアプリケーションをデプロイするやり方には
- Heroku Command Line Interface(CLI)をダウンロードしてコマンドを叩いてデプロイ作業をする
- Herokuをグラウザで開いてGUIの画面でデプロイ作業をする
の2種類がある。
公式ページ
こちらの方で、事前にアカウントを作成している前提です。
登録されていない方はお願いします。
また、プロダクトのソースコードをGitHubで管理しており、GitHubとHerokuのアカウント連携も完了している前提です。作業
1:Heroku CLIがダウンロードされているかの確認
$heroku -v heroku/7.30.1 darwin-x64 node-v11.14.0私のPCにはHeroku CLIがダウンロードできていることが確認できた。
バージョンが表示されない場合はここからダウンロード2:Herokuにログインする
$heroku login heroku: Press any key to open up the browser to login or q to exit › Warning: If browser does not open, visit › https://cli-auth.heroku.com/auth/browser/*** heroku: Waiting for login... Logging in... done Logged in as me@example.comブラウザが開き、GUIでloginが実行される。
デプロイ作業をはじめる最初だけ実行する。3:Herokuにプロジェクトを作る
$heroku create Creating app... done, ⬢ polar-inlet-4930 http://polar-inlet-4930.herokuapp.com/ | https://git.heroku.com/polar-inlet-4930.git Git remote heroku added4:Herokuにソースコードをpushする
ここで、エラーが発生しなければゴールはすぐそこです!!
$git push heroku master Enumerating objects: 365, done. Counting objects: 100% (365/365), done. Delta compression using up to 8 threads Compressing objects: 100% (204/204), done. Writing objects: 100% (365/365), 71.57 KiB | 35.79 MiB/s, done. Total 365 (delta 141), reused 358 (delta 136) remote: Compressing source files... done. remote: Building source: remote: ... (長めのログ、gemファイルなどを読み込んでいる) ... remote: Verifying deploy... done. To https://git.heroku.com/polar-inlet-4930.git * [new branch] master -> master実行完了したら
5:プロダクトを確認
$heroku openブラウザが開きます。アクセスすると多分、エラーが発生しますが。。
6:HerokuにDBを作成
これで解決するはず!
heroku run rails db:migrate再度アクセス!!
できたら万歳!!
できなかったら、Herokuのログを分析してエラーを解消しましょう!!
以下に、コマンドをまとめます。使えるHerokuコマンドのまとめ(今後、まとめます。)
- 投稿日:2019-10-01T17:58:21+09:00
【Rails】Stripeの実装(カード情報保存版)
環境
- Rails 5.2.3
- stripe-ruby 4.21.3
ドキュメント(Stripe公式)
設定
KEYの設定(環境変数)
initializersの設定
コンソールでテスト
テスト結果は、stripeのダッシュボードで確認可能。
※ユーザ固有のキーを使っている場合のみ。
※ダッシュボードで 「テストデータを表示」 をチェックすること。リクエスト例
- amountはドル換算で 50セント以上になるようにする(50セント未満の場合、exceptionが発生する)。
- tokenは以下の何れかを参照 (※以下の例では
tok_mastercard
を使用している)。
- https://stripe.com/docs/testing#cards
- https://stripe.com/docs/testing#international-cards (※
currency
としてjpy
を使う場合はこっち。但し、テストなのでどちらもで良い)## customer の作成 customer = Stripe::Customer.create({ source: 'tok_mastercard', email: 'paying.user@example.com', }) ## customer にチャージする (カードではなく) charge = Stripe::Charge.create({ amount: 1000, currency: 'jpy', customer: customer.id, }) ## customer ID (customer.id) や、その他の情報をデータベースに保存する。 ## 再度、customer にチャージする際に、↑で保存した customer ID を使う。以下の customer_id はデータベースに保存していたもの。 charge = Stripe::Charge.create({ amount: 1500, currency: 'jpy', customer: customer_id, })レスポンス例(↑の例では
customer
に入るもの)> customer.class => Stripe::Customer < Stripe::APIResource > customer.id => "cus_FuW98aPiB42PLY" > customer => { :id => "cus_FuW98aPiB42PLY", :object => "customer", :account_balance => 0, :address => nil, :balance => 0, :created => 1569920002, :currency => nil, :default_source => "card_1FOh6k2eZvKYlo2C86LDl8BE", :delinquent => false, :description => nil, :discount => nil, :email => "paying.user@example.com", :invoice_prefix => "032DE63F", :invoice_settings => { :custom_fields => nil, :default_payment_method => nil, :footer => nil }, :livemode => false, :metadata => {}, :name => nil, :phone => nil, :preferred_locales => [], :shipping => nil, :sources => { :object => "list", :data => [ [0] { :id => "card_1FOh6k2eZvKYlo2C86LDl8BE", :object => "card", :address_city => nil, :address_country => nil, :address_line1 => nil, :address_line1_check => nil, :address_line2 => nil, :address_state => nil, :address_zip => nil, :address_zip_check => nil, :brand => "MasterCard", :country => "US", :customer => "cus_FuW98aPiB42PLY", :cvc_check => nil, :dynamic_last4 => nil, :exp_month => 10, :exp_year => 2020, :fingerprint => "7a9bk9ncM08SXfua", :funding => "credit", :last4 => "4444", :metadata => {}, :name => nil, :tokenization_method => nil } ], :has_more => false, :total_count => 1, :url => "/v1/customers/cus_FuW98aPiB42PLY/sources" }, :subscriptions => { :object => "list", :data => [], :has_more => false, :total_count => 0, :url => "/v1/customers/cus_FuW98aPiB42PLY/subscriptions" }, :tax_exempt => "none", :tax_ids => { :object => "list", :data => [], :has_more => false, :total_count => 0, :url => "/v1/customers/cus_FuW98aPiB42PLY/tax_ids" }, :tax_info => nil, :tax_info_verification => nil }レスポンス例(↑の例では
charge
に入るもの)> charge.class => Stripe::Charge < Stripe::APIResource > charge => { :id => "ch_1FOh8a2eZvKYlo2CqJvuzqx2", :object => "charge", :amount => 1000, :amount_refunded => 0, :application => nil, :application_fee => nil, :application_fee_amount => nil, :balance_transaction => "txn_1FOh8b2eZvKYlo2C0J9XmWSz", :billing_details => { :address => { :city => nil, :country => nil, :line1 => nil, :line2 => nil, :postal_code => nil, :state => nil }, :email => nil, :name => nil, :phone => nil }, :captured => true, :created => 1569920116, :currency => "jpy", :customer => "cus_FuW98aPiB42PLY", :description => nil, :destination => nil, :dispute => nil, :failure_code => nil, :failure_message => nil, :fraud_details => {}, :invoice => nil, :livemode => false, :metadata => {}, :on_behalf_of => nil, :order => nil, :outcome => { :network_status => "approved_by_network", :reason => nil, :risk_level => "normal", :risk_score => 34, :seller_message => "Payment complete.", :type => "authorized" }, :paid => true, :payment_intent => nil, :payment_method => "card_1FOh6k2eZvKYlo2C86LDl8BE", :payment_method_details => { :card => { :brand => "mastercard", :checks => { :address_line1_check => nil, :address_postal_code_check => nil, :cvc_check => nil }, :country => "US", :exp_month => 10, :exp_year => 2020, :fingerprint => "7a9bk9ncM08SXfua", :funding => "credit", :last4 => "4444", :three_d_secure => nil, :wallet => nil }, :type => "card" }, :receipt_email => nil, :receipt_number => nil, :receipt_url => "https://pay.stripe.com/receipts/acct_1032D82eZvKYlo2C/ch_1FOh8a2eZvKYlo2CqJvuzqx2/rcpt_FuWAT1u6h54pfgGz0uOTzqZbV8EgbPX", :refunded => false, :refunds => { :object => "list", :data => [], :has_more => false, :total_count => 0, :url => "/v1/charges/ch_1FOh8a2eZvKYlo2CqJvuzqx2/refunds" }, :review => nil, :shipping => nil, :source => { :id => "card_1FOh6k2eZvKYlo2C86LDl8BE", :object => "card", :address_city => nil, :address_country => nil, :address_line1 => nil, :address_line1_check => nil, :address_line2 => nil, :address_state => nil, :address_zip => nil, :address_zip_check => nil, :brand => "MasterCard", :country => "US", :customer => "cus_FuW98aPiB42PLY", :cvc_check => nil, :dynamic_last4 => nil, :exp_month => 10, :exp_year => 2020, :fingerprint => "7a9bk9ncM08SXfua", :funding => "credit", :last4 => "4444", :metadata => {}, :name => nil, :tokenization_method => nil }, :source_transfer => nil, :statement_descriptor => nil, :statement_descriptor_suffix => nil, :status => "succeeded", :transfer_data => nil, :transfer_group => nil }エラー処理
独自Exception
モック
実装時の注意点
- 投稿日:2019-10-01T16:10:18+09:00
EC2でrailsアプリをpwa化しようとしたらsafariでServiceWorker responce error is nullが発生
どのような問題が発生したのか?
awsのec2サーバー上にrailsプロジェクトとapacheの環境を作りrailsで顧客管理ツールをpwa化しようとした際に、
iOS版 safari,OSX版 safariで"サイトを開けません。The operation couldn't be completed. Protocol error" (NSPOSIXErrorDomain:100)とのエラーが頻出する問題が発生した。
何度もリロードしてみると時々画面が表示されるが、画像やアイコンなどが全く表示されない。
コンソール画面を開いてみると大量の"FetchEvent.respondWith received an error: Returned response is null"というエラーが発生していた。
動作環境
- AWS EC2
- apache for ruby
- Ruby on Rails 5.2.3
- rails gem service worker
結局の原因
「ec2内ロードバランサーのhttp/2通信が有効になっている」
結論に到るまで
コンソール上に表示された
"FetchEvent.respondWith received an error: Returned response is null"というエラーをググると大概出てくるサイトはこういったものだが、根本的な解決ができなかった。
ここでハマってしまった私個人の原因は、
「safariのpwa対策は不完全」という先入観である。
確かにsafariのpwa対策はchromeよりも遅れているが、
エラーの原因をservice workerにあるのではないかと考え長い時間serviceworkerについての記事を巡っていた。
なにせ時々リクエストが成功する意味がわからない。エラー文からすると
railsのgem,serviceworker-railsで生成されるjsファイル/app/aseets/javascripts/serviceworker.js.erbfunction onFetch(event) { event.respondWith( fetch(event.request).catch(function() { return caches.match(event.request).then(function(response) { if (response) { return response; } if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) { console.log('[Serviceworker]', "Fetching offline content", event); return caches.match('/offline.html'); } }) }) ); }ここの
if (response) { return responce; }が空になっているようだが、今まではこんなことがなかったし、
第一、開発用の別のec2サーバーでは同問題が全く起きていなかった。serviceworkerの古いキャッシュかと思いブラウザのキャッシュを消してみてもダメ、
turbolinkとの兼ね合いの悪さかと思いrailsのturbolinksをオフにしてもダメ、/app/views/application.html.erb<head> ... <meta name="turbolinks-cache-control" content="no-cache"> </head>これらでは効果がなかったので視野を広げることとし、サーバーの設定を確認してみることにした。
はじめに出てくる
「"サイトを開けません。The operation couldn't be completed. Protocol error" (NSPOSIXErrorDomain:100)」
というエラーを調べてみるとこんなサイトがiOS 11, macOS Hight Sierra で The operation couldn't be completed. Protocol error が出る場合の対処
Safariで発生するプロトコルエラーsafariだとapacheから独自レスポンスが乗っかり、クライアントではコネクトの失敗を返す「時がある」。
試しにロードバランサー => http/2 無効 にしてみると
全て治った。エラーも全く表示されない。どうやらec2サーバーにアクセスするうちに何度か成功するが、
レスポンスが成功した時の不完全なキャッシュをserviceworkerが登録してしまい、次のリロードで不完全なキャッシュを元に描画され、
ファイルが見つからないというエラーを吐いていたということらしい。これで真っさら解決した。
次に出てくる問題
ec2 LBのhttp/2通信を無効にしてしまったので、通信速度が懸念される。
+1%の高速化だけでもありがたい。
ただでさえrailsの速度はマイペースで、このツールでは膨大なデータのやりとりをするのになのでec2 LBのhttp/2は確保して、apache内の http/2通信の設定が必要になる。
- 投稿日:2019-10-01T15:00:06+09:00
git pushでherokuにデプロイしようとしたらfatal: unable to accessとなった
herokuにログインしてなかっただけだった。
$heroku login
でログインした。
その後は別のエラーにつまづいたが、、、
備忘録。
- 投稿日:2019-10-01T14:54:15+09:00
ActiveModel as_json のオプション一覧
.as_json(options = nil)
option名 詳細 root
true
を指定すると、root要素としてモデル名のkeyが挿入される(ネストがひとつ深くなる)only
要素名の配列を渡すと、その要素のみが出力される expect
要素名の配列を渡すと、その要素以外が出力される methods
モデルのメソッド名(配列可)を渡すと、そのメソッドの実行結果が出力される include
アソシエーション名(配列可、ネスト可)を渡すと、その関連情報も一緒に出力される rootuser.as_json(root: true) # => { "user" => { # "id" => 1, "name" => "Konata Izumi", "age" => 16, "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true # } }onlyuser.as_json(only: [:id, :name]) # => { "id" => 1, "name" => "Konata Izumi" }expectuser.as_json(except: [:id, :created_at, :age]) # => { "name" => "Konata Izumi", "awesome" => true }methodsuser.as_json(methods: :permalink) # => { "id" => 1, "name" => "Konata Izumi", "age" => 16, # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true, # "permalink" => "1-konata-izumi" }includeuser.as_json(include: :posts) # => { "id" => 1, "name" => "Konata Izumi", "age" => 16, # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true, # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" }, # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }include(example)user.as_json(include: { posts: { include: { comments: { only: :body } }, only: :title } }) # => { "id" => 1, "name" => "Konata Izumi", "age" => 16, # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true, # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ], # "title" => "Welcome to the weblog" }, # { "comments" => [ { "body" => "Don't think too hard" } ], # "title" => "So I was thinking" } ] }参考
https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json
- 投稿日:2019-10-01T14:23:31+09:00
東京公共交通オープンデータを使ってレアな行き先を調べよう
東京公共交通オープンデータを使ってみた
東京公共交通オープンデータっていろんなデータが取れることがわかった
試しにJRの行き先一覧を時刻表から取得してみる東京公共交通オープンデータチャレンジ
開発者サイト
JR東日本 駅時刻表 / Station timetable of JR Eastrailsでさらっと書いてみた
- Controller抜粋
# HttpClientを生成 http_client = HTTPClient.new # APIからJRの時刻表を全部取る response = http_client.get ”https://api-tokyochallenge.odpt.org/api/v4/odpt:StationTimetable”, {"odpt:operator"=>"odpt.Operator:JR-East", "acl:consumerKey"=>開発者サイトで取得したアクセストークン} hash = JSON.parse(response.body) @station_list = [] # 駅毎のデータなので毎に繰り返す hash.each do | h | # 駅データの中に時刻表のデータがあるのでデータ分繰り返す h["odpt:stationTimetableObject"].each do | timetable | # 時刻表データの行き先毎に繰り返し、行き先を配列に入れる timetable["odpt:destinationStation"].each do | destStation | @station_list << destStation end end end # 配列のデータを駅毎にまとめてカウントする @station_list = @station_list.group_by(&:itself).map{ |key,value| [key, value.count] } # 駅名でソート @station_list.sort!
- view抜粋
<table border=1> <th>行き先</th><th>本数</th> <tbody> <% @station_list.each do |station| %> <tr> <td><%= station[0] %></td> <td><%= station[1] %></td> </tr> <% end %> </table>ブラウザでアクセスすると以下のように出力される
行き先の日本語化は別途やろう
- 投稿日:2019-10-01T14:02:15+09:00
Rails 部分テンプレート
部分テンプレート
- 同じHTML構造の部分を共通化することによって、無駄なくビューファイルを作成する。
- 部分テンプレートのファイル名は必ずアンダーバー「_」から始まる。
(例)
tweets/index.html.erb<div class="contents row"> <% @tweets.each do |tweet| %> <div class="content_post" style="background-image: url(<%= tweet.image %>);"> #--------------ここから-------------- <div class="more"> <span><%= image_tag 'arrow_top.png' %></span> <ul class="more_list"> <li> <%= link_to "詳細", tweet_path(tweet.id), method: :get %> </li> <% if user_signed_in? && current_user.id == tweet.user_id %> <li> <%= link_to '編集', "/tweets/#{tweet.id}/edit", method: :get %> </li> <li> <%= link_to '削除', "/tweets/#{tweet.id}", method: :delete %> </li> <% end %> </ul> </div> <%= simple_format(tweet.text) %> <span class="name"> <a href="/users/<%= tweet.user_id %>"> <span>投稿者</span><%= tweet.user.nickname %> </a> </span> </div> #----------------ここまでを切り取り---------------------- <% end %> <%= paginate(@tweets) %> </div>_tweet_html.erbを作成して切り取った部分を貼り付け
tweets/_tweet.html.erb<div class="content_post" style="background-image: url(<%= tweet.image %>);"> <div class="more"> <span><%= image_tag 'arrow_top.png' %></span> <ul class="more_list"> <li> <%= link_to "詳細", tweet_path(tweet.id), method: :get %> </li> <% if user_signed_in? && current_user.id == tweet.user_id %> <li> <%= link_to '編集', "/tweets/#{tweet.id}/edit", method: :get %> </li> <li> <%= link_to '削除', "/tweets/#{tweet.id}", method: :delete %> </li> <% end %> </ul> </div> <%= simple_format(tweet.text) %> <span class="name"> <a href="/users/<%= tweet.user_id %>"> <span>投稿者</span><%= tweet.user.nickname %> </a> </span> </div>index.html.erbを以下のように編集
tweets/index.html.erb<div class="contents row"> <% @tweets.each do |tweet| %> <%= render partial: "tweet", locals: { tweet: tweet } %> # <% end %> <%= paginate(@tweets) %> </div>ここでのrenderメソッドのlocalsオプションに注目してみると
{ tweet: tweet }
の右側のtweet
はeachメソッド内の変数としてのtweet
でtweet
のインスタンスを示している。一方、左側の tweet は部分テンプレート内での変数の名前を表している。おしまい
- 投稿日:2019-10-01T11:30:47+09:00
rails new してから git commit -m 'first commit' までの手順まとめ
はじめに
技術検証とかで新たに rails プロジェクトを作成する際に、毎回同じようなコマンドを叩いていたのでまとめてみました。
こんなことやらずに docker を使えって言われたらそれはそうなんですが、それはそれ。試したバージョン情報
- rbenv: 1.1.2
- Ruby: 2.6.4
- Rails: 6.0.0
実行すること
プロジェクトファイルの作成
$ mkdir new_project $ cd new_projectruby最新化
$ brew update && brew upgrade ruby-build # 0.0.0 には latest version を入れる(latest versionはググる) $ rbenv install 0.0.0 # 0.0.0 には latest version を入れる $ rbenv local 0.0.0rails のセットアップ
$ bundle init # 0.0.0 には latest version を入れる $ echo 'gem "rails", ">=0.0.0(latest version)"' >> Gemfile $ bundle install --path vendor/bundle # optionとして ` --api --skip-test --skip-turbolinks --skip-coffee` などを指定。 # ここでは `--skip-turbolinks` を例で使用していますが turbolinks が憎いわけではありません。 # その他の option は、 `rails new -h` で確認。 $ bundle exec rails new . --skip-turbolinksgit の設定
$ git init # gitignore は次のものををそのままコピペ `https://github.com/github/gitignore/blob/master/Rails.gitignore` $ vim .gitignore $ git add . $ git commit -m 'first commit'終わり
- 投稿日:2019-10-01T09:33:21+09:00
Railsのblank?が優秀だった
背景
Railsでアプリケーションを作っていて、空文字判定の実装をしました。
当然blank?
を使用したわけですが、思ったより優秀で驚いた話です。String.blank? の動作
空文字
一番ベーシックなタイプです。これは当然true。
irb(main):001:0> ''.blank? => true半角スペース
これも可能なら対応してもらいたいところだが... → true
irb(main):002:0> ' '.blank? => true半角スペース(複数)
これはブランクと言えるのか...? → true
irb(main):003:0> ' '.blank? => true全角スペース
これは流石に... → true
irb(main):004:0> '\U+FFE3'.blank? => true全角スペース(複数)
もうわかってきた。これはtrue! → true
irb(main):005:0> '\U+FFE3\U+FFE3\U+FFE3\U+FFE3'.blank? => true改行
まさかこれも...? → true
irb(main):014:0> ' irb(main):015:0' '.blank? => true全角スペース + 改行
falseにしたい...! → true
irb(main):022:0> ' irb(main):023:0' \U+FFE3 irb(main):024:0' irb(main):025:0' '.blank? => true文字列「aaa」
少し心配になってきたので、念のため... → false
irb(main):006:0> 'aaa'.blank? => falseRubyのメソッドではなくて、Railsで実装されている
これはrailsで実装されているのであって、Rubyでは使えません。
irb(main):001:0> ''.blank? Traceback (most recent call last): 2: from /Users/yoshinori/.rbenv/versions/2.5.3/bin/irb:11:in `<main>' 1: from (irb):1 NoMethodError (undefined method `blank?' for "":String)終わりに
文字列か有効かどうか判断するのに 'blank?' が優秀なことがわかりました。
'present?' は 'blank?' の真逆になります。
すごく便利。
- 投稿日:2019-10-01T01:48:32+09:00
【41日目】パーシャルの中にインスタンス変数を使ってはいけない理由と正しい使い方
今日はRuby on Railsのパーシャルについて2度目の学習になりました。
先月学習した時にも出てきた「パーシャルの中にインスタンス変数をつかってはいけない」という教え。
そういうものか〜と思って従っていましたが、実はあまりちゃんと意味がわかっていなかった様に思います。今回はきちんとその理由と対処方法についてまとめておこうと思います。
非常に初歩的な基本ですが、バグの原因になる部分と思われるので、しっかり整理して当たり前のことが当たり前にできるようにしていこうと思います。パーシャルとは
Railsの基本原理の一つである "Don't Repeat Yourself" を実現するための仕組み。
複数のViewで用いられる似たようなコードを「パーシャル」として保存し、同じコードを使う各ファイルでは、毎回同じコードを書かなくても、パーシャルを呼び出す1行を書けば良くなるといった機能である。
なお、パーシャルは「_パーシャル名」という命名がなされます。これによって、同じコードを何度も書いたりコピペしたりする手間が省けるだけでなく、「パーシャル部分に更新や変更があった場合に、一箇所だけ直せばことたりる」という利点があります。
これは単純に労力を削減しコードがスッキリするだけではなく、たくさん記入しなくて済むため打ち間違いなどのヒューマンエラーによるミスを減らしたり、同じコードを使うファイルが何箇所もあると、その機能の変更や更新があると、全ファイルを直さなければならず、うっかり漏れるとバグになったりすることも防げます。例えば今回私が作っている掲示板アプリではこんな感じ。
掲示板の入力フォームは新規作成でも編集画面でも共通なので、パーシャル化することになりました。① パーシャル化前の状態
new.html.erb<div class="d-flex align-items-center"> <h1>掲示板作成</h1> <div class="ml-auto boards__linkBox"> <%= link_to '一覧', boards_path, class: 'btn btn-outline-dark' %> </div> </div> <%= form_for @board do |f| %> <div class="form-group"> <%= f.label :name, '名前' %> <%= f.text_field :name, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :title, 'タイトル' %> <%= f.text_field :title, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :body, '本文' %> <%= f.text_area :body, class: 'form-control', rows: 10 %> </div> <%= f.submit '保存', class: 'btn btn-primary' %> <% end %>② パーシャルに分割して、パーシャルを呼び出している。
new.html.erb<div class="d-flex align-items-center"> <h1>掲示板作成</h1> <div class="ml-auto boards__linkBox"> <%= link_to '一覧', boards_path, class: 'btn btn-outline-dark' %> </div> </div> <%= render partial: 'board' %>③ パーシャルとして移植した掲示板フォーム部分
_board.html.erb<%= form_for @board do |f| %> <div class="form-group"> <%= f.label :name, '名前' %> <%= f.text_field :name, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :title, 'タイトル' %> <%= f.text_field :title, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :body, '本文' %> <%= f.text_area :body, class: 'form-control', rows: 10 %> </div> <%= f.submit '保存', class: 'btn btn-primary' %> <% end %>パーシャルを見てみると、確かに分割した段階ではインスタンス変数が使われています。
パーシャル内部でインスタンス変数を使ってはいけない理由
パーシャルはもともとViewの一部でしたから、コントローラからインスタンス変数を渡されて動作するコードが書かれている場合があります(上記①)。
単純にそれをコピペしてパーシャル化すると、Viewからはインスタンス変数が消え、パーシャルに突然インスタンス変数が出てくることになってしまいます。
このことによって、理由は大きく2つあります。
①パーシャルを使っているviewのなかにはインスタンス変数が出てこない。
=>コントローラでパーシャルで使っていることに気づかず、コントローラ側インスタンス変数を変えたり削除したりするとバグの原因になる。
②パーシャルに直接インスタンス変数を渡す仕組みになっており、コントローラでインスタンス変数をいじるたびに、パーシャルのことも考慮しなければならない。つまり、インスタンス変数を誤って消したり、勝手に変えてしまったり、いったい何が渡されているかが見えにくかったりするし、あるいは逆に、コントローラ側でインスタンス変数をいじるたびに「そういえばパーシャルで使ってないか確認しないと」といったように手間が増えてしまうということです。
解決策
viewで一度インスタンス変数をローカル変数に格納し、パーシャルではローカル変数を用いるようにすれば、良いです。
上記の例で言えば、以下ようになります。②のviewでインスタンス変数をローカル変数に入れます。
new.html.erb
<%= render partial: 'board', locals: {board: @board} %>
③のパーシャルのインスタンス変数をローカル変数に直します。
_board.html.erb
<%= form_for board do |f| %>
これだけです。
ちなみにさらに省略した書き方として
<%= render partial: 'board', object: @board %>
<%= form_for object do |f| %>
があります。これはパーシャル名(ここでいうform)と同名のローカル変数にインスタンス変数を格納してくれるものです。
さらに省略すると、こんな書き方もあります。
<%= render @board %>
これは自動的に_@以下.html.erbを呼び出し、@以下と同名のローカル変数に、インスタンス変数を格納して、渡すところまで自動的に行ってくれるものです。