- 投稿日:2020-08-20T23:11:23+09:00
Railsチュートリアル 5章最後でHerokuが動かなくなる
Railsチュートリアルをもりもりとやっています。
5章に来て初めて本格的に躓いてしまったので、備忘録として記録します。herokuが動かない・・・
5章の最後に'git push heroku'をしたところこの画面がでました。
Application error An error occurred in the application and your page could not be served. If you are thee application owner, check your logs for details. You can do this from the Heroku CLI with the command heroku logs --tail対処
ひとまず色々検索して以下のコマンドを打ちました。
heroku logsherokuのログの出力
見るべきポイント:errorと書かれている行>>$ heroku logs #ログ出力 : (中略) #errorと書かれている箇所を探す : 2020-08-20T12:51:51.649616+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=enigmatic-harbor-30759.herokuapp.com request_id=6cf41616-a067-4dc6-8d2c-696895e1b968 fwd="60.76.208.83" dyno= connect= service= status=503 bytes= protocol=https :
heroku run rails crailsでコンソールを起動する
見るべきポイント:エラーになっているファイル、エラー原因>>$ heroku run rails c #コンソールの起動 : (中略) #ファイル名が書かれている箇所を探す : 4: from /app/app/helpers/application_helper_test.rb:1:in `<top (required)>' : :#最終的なエラーを確認する /app/vendor/bundle/ruby/2.6.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292:in `require': No such file to load -- test_helper.rb (LoadError)以上の内容でわかったこと
・logを見る限りだと、よくわからない・・・
・console起動でみると、どうやらapplication_helper_test.rbが悪さをしている?
・上のファイルがtest_helper.rbを呼んでるけど、ファイルがないと言われている。結論
application_helper_test.rbがtest_helper.rbを参照できないようフォルダ構成になっていまいました。
本来であれば、testsフォルダ内にあるべきapplication_helper_test.rbファイルが別のフォルダ(app)に入っていたようです。
ひとまず、ファイルを移動させ、再度実行するとエラーがなくなりました。感想
エラーが出た時の、ログの読み取りがうまく出来なかったのでてこずってしまいました・・・。改めて整理してみて、案外読み取るポイントってシンプルなんだなと改めておもいました。
- 投稿日:2020-08-20T22:16:08+09:00
privateメソッドとprotectedメソッド
【概要】
1.結論
2.”private”と”protected”とは何か
3.どのように”private”と”protected”を使うのか
4.ここから学んだこと
1.結論
状況に応じて使い分けるが、ほぼprivateメソッドを使えばいい!
2.”private”と”protected”とは何か
publicは制限がないメソッドですが、private,protectedの違いがわからず悩みました。
private:
クラス外からは呼び出せない。同じインスタンス内で呼び出せる。protected:
クラス外からは呼び出せない。同じインスタンス内で呼び出せる。しかし、他のインスタンスであれば同じクラスであれば呼び出せる。ということですが、3.の例を見て
理解していきましょう。3.どのように”private”と”protected”を使うのか
privateclass saturday def day holiday end private def holiday puts "今日は休日です" end end human=Human.new human.holidayprotectedclass school def class (student) @student = student end def brother(human) puts "#{class_brother}と#{human.class_brother}は兄弟です。" end end protected def class_brother @student end studentA=Student.new studentA.school("桜木") studentB=Student.new studentB.school("山田") studentA.brother("山田 ")もちろんprivateのプログラムでも、
protectedが適用できます!
ただ、protectedの方は、同じクラス内でも
違うメソッド(他のインスタンスであれば同じクラスであれば呼び出せる)のため、protectedのみでしか適用できません。4.ここから学んだこと
i)privateは使用頻度が高いと思いますが、protectedは使う範囲・頻度が限られそうです。
ii)邪魔されたくないメソッドや、影響されたくないメソッド(インスタンス変数)を使う時にはものすごく便利なのでこれからも使いつつ、プログラムを学んでいきます!
- 投稿日:2020-08-20T21:55:14+09:00
RuboCopの導入と基本的な使用方法【ポートフォリオ備忘録①】
はじめに
現在、ポートフォリオとしてオリジナルアプリを作成中ですが、自分一人でコードを書いているとついつい自分が読めれば問題ないコードを書きがちになってしまいます。
変な癖が付かないように、その辺も意識していきたいということで、RuboCopを導入することにしました。導入で学んだことを忘れないようにここに綴ります。
RuboCopとは
RuboCopとは、Ruby向けの静的解析ツールである。Ruby Style Guideのコーディング規約に従って、コードが書かれているかチェックするためのgemである。
参考:RuboCop // DocsRuboCopをインストール
Gemfileに記入
Gemfilegroup :development do gem 'rubocop', require: false gem 'rubocop-rails', require: false endインストール
terminal$ bundleRuboCopを使ってみる
terminal$ bundle exec rubocop(余談ですが、bundle execって何?という時は bundler、bundle execについて ※自分用メモ の記事がオススメ。)
なんだかエラーがたくさん出てしまった・・・
直さなければいけないところが書かれているので親切だけど、全部直すには時間がかかり過ぎてしまう。ということで以下のコマンドを実行
$ rubocop --auto-gen-configすると、.rubocop_ymlと.rubocop_todo.ymlの2つのファイルが生成される。
.rubocop_todo.yml
.rubocop_todo.ymlファイルの生成によって、先ほど大量に出た警告が一旦無視されるように設定される。すなわち.rubocop_todo.ymlに書かれているかぎり、RuboCopに怒られなくて済むということである。
試しにもう一度 budle exec rubocop を実行すると、警告が出なくなっている。
とはいえあくまでも今は一時的に警告を回避しているに過ぎないので、このファイルに何も書かれていなくてもRuboCopから警告文が表示されない状況に持っていくことが最終的な目標となる。
警告をなくす
rubocop_todo.ymlBundler/OrderedGems: Exclude: - 'Gemfile' Layout/SpaceInsideArrayLiteralBrackets: Exclude: - 'config/environments/production.rb' Layout/TrailingEmptyLines: Exclude: - 'Gemfile'例えば、.rubocop_todo.ymlに上記のような記載があったら、まずは
Bundler/OrderedGems:
Exclude:
- 'Gemfile'をコメントアウトして、bundle exec rubocop を実行。すると警告文が出る。
このあとの手順は2パターンある。
①警告に従って、コードを修正する
②コードは変えず、警告のデフォルト変更・もしくは今後警告を出さないように設定する①は文章通り、該当ファイルを開いて、コードを修正するということ。
.rubocop.yml
②については、先ほど .rubocop_todo.ymlと同時に生成された .rubocop.ymlに設定を書いていく。
- RuboCopの対象から外す時
rubocop.ymlinherit_from: .rubocop_todo.yml AllCops: # 除外するファイル Exclude: - "config/environments/*" - "vender/**/*" - "bin/**" - "db/**/*" - "tmp/**/*" - "lib/**/*" - "Gemfile"自分でExcludeを設定する時は、デフォルトの設定が外れてしまうため、それらも改めて明記する必要がある。
ちなみに、上記のファイルの最上部にinherit_from: .rubocop_todo.yml があるが、この一文がないと、.rubocop_todo_ymlに書かれた内容が反映されないので注意。
- Copの無効化
Copとは、RuboCopにおける1つのルールのことである。
上から3つ目までのCopは、有効にするか無効にするかを記述しないとエラーが出るので書く。rubocop.ymlStyle/HashEachMethods: Enabled: true Style/HashTransformKeys: Enabled: true Style/HashTransformValues: Enabled: true # 日本語のコメントを許可する Style/AsciiComments: Enabled: false警告文に対してどちらかの対処を行ったら、コメントアウトした警告を削除する。
.rubocop_todo.ymlの中身がなくなるまでこの繰り返し。まとめ
- .rubocop.yml:コーディングのルールを記載
- .rubocop_todo.yml:現在の違反部分を記載 (名前の通りToDoリスト的な役割だ!)
.rubocop_todo.ymlの違反を1つずつチェックして、コードを修正もしくは新たなルールを作って.rubocop.ymlに記載するのがRuboCopの基本的な使い方である。
おわりに
とにかく今後はRuboCopを適宜利用して、規約に沿ったコードを書いていきたいと思います。本記事で何かツッコミどころ、おかしな点などあればご教示いただけると幸いです。
尚、本記事は以下を参考にしています。
- 投稿日:2020-08-20T21:55:14+09:00
RuboCopの導入と基本的な使用方法【ポートフォリオ①】
はじめに
現在、ポートフォリオとしてオリジナルアプリを作成中ですが、自分一人でコードを書いているとついつい自分が読めれば問題ないコードを書きがちになってしまいます。
変な癖が付かないように、その辺も意識していきたいということで、RuboCopを導入することにしました。導入で学んだことを忘れないようにここに綴ります。
RuboCopとは
RuboCopとは、Ruby向けの静的解析ツールである。Ruby Style Guideのコーディング規約に従って、コードが書かれているかチェックするためのgemである。
参考:RuboCop // DocsRuboCopをインストール
Gemfileに記入
Gemfilegroup :development do gem 'rubocop', require: false gem 'rubocop-rails', require: false endインストール
terminal$ bundleRuboCopを使ってみる
terminal$ bundle exec rubocop(余談ですが、bundle execって何?という時は bundler、bundle execについて ※自分用メモ の記事がオススメ。)
なんだかエラーがたくさん出てしまった・・・
直さなければいけないところが書かれているので親切だけど、全部直すには時間がかかり過ぎてしまう。ということで以下のコマンドを実行
$ rubocop --auto-gen-configすると、.rubocop_ymlと.rubocop_todo.ymlの2つのファイルが生成される。
.rubocop_todo.yml
.rubocop_todo.ymlファイルの生成によって、先ほど大量に出た警告が一旦無視されるように設定される。すなわち.rubocop_todo.ymlに書かれているかぎり、RuboCopに怒られなくて済むということである。
試しにもう一度 budle exec rubocop を実行すると、警告が出なくなっている。
とはいえあくまでも今は一時的に警告を回避しているに過ぎないので、このファイルに何も書かれていなくてもRuboCopから警告文が表示されない状況に持っていくことが最終的な目標となる。
警告をなくす
rubocop_todo.ymlBundler/OrderedGems: Exclude: - 'Gemfile' Layout/SpaceInsideArrayLiteralBrackets: Exclude: - 'config/environments/production.rb' Layout/TrailingEmptyLines: Exclude: - 'Gemfile'例えば、.rubocop_todo.ymlに上記のような記載があったら、まずは
Bundler/OrderedGems:
Exclude:
- 'Gemfile'をコメントアウトして、bundle exec rubocop を実行。すると警告文が出る。
このあとの手順は2パターンある。
①警告に従って、コードを修正する
②コードは変えず、警告のデフォルト変更・もしくは今後警告を出さないように設定する①は文章通り、該当ファイルを開いて、コードを修正するということ。
.rubocop.yml
②については、先ほど .rubocop_todo.ymlと同時に生成された .rubocop.ymlに設定を書いていく。
- RuboCopの対象から外す時
rubocop.ymlinherit_from: .rubocop_todo.yml AllCops: # 除外するファイル Exclude: - "config/environments/*" - "vender/**/*" - "bin/**" - "db/**/*" - "tmp/**/*" - "lib/**/*" - "Gemfile"自分でExcludeを設定する時は、デフォルトの設定が外れてしまうため、それらも改めて明記する必要がある。
ちなみに、上記のファイルの最上部にinherit_from: .rubocop_todo.yml があるが、この一文がないと、.rubocop_todo_ymlに書かれた内容が反映されないので注意。
- Copの無効化
Copとは、RuboCopにおける1つのルールのことである。
上から3つ目までのCopは、有効にするか無効にするかを記述しないとエラーが出るので書く。rubocop.ymlStyle/HashEachMethods: Enabled: true Style/HashTransformKeys: Enabled: true Style/HashTransformValues: Enabled: true # 日本語のコメントを許可する Style/AsciiComments: Enabled: false警告文に対してどちらかの対処を行ったら、コメントアウトした警告を削除する。
.rubocop_todo.ymlの中身がなくなるまでこの繰り返し。まとめ
- .rubocop.yml:コーディングのルールを記載
- .rubocop_todo.yml:現在の違反部分を記載 (名前の通りToDoリスト的な役割だ!)
.rubocop_todo.ymlの違反を1つずつチェックして、コードを修正もしくは新たなルールを作って.rubocop.ymlに記載するのがRuboCopの基本的な使い方である。
おわりに
とにかく今後はRuboCopを適宜利用して、規約に沿ったコードを書いていきたいと思います。本記事で何かツッコミどころ、おかしな点などあればご教示いただけると幸いです。
尚、本記事は以下を参考にしています。
- 投稿日:2020-08-20T21:55:14+09:00
RuboCopの導入と基本的な使用方法【ポートフォリオメモ①】
はじめに
現在、ポートフォリオとしてオリジナルアプリを作成中ですが、自分一人でコードを書いているとついつい自分が読めれば問題ないコードを書きがちになってしまいます。
変な癖が付かないように、その辺も意識していきたいということで、RuboCopを導入することにしました。導入で学んだことを忘れないようにここに綴ります。
RuboCopとは
RuboCopとは、Ruby向けの静的解析ツールである。Ruby Style Guideのコーディング規約に従って、コードが書かれているかチェックするためのgemである。
参考:RuboCop // DocsRuboCopをインストール
Gemfileに記入
Gemfilegroup :development do gem 'rubocop', require: false gem 'rubocop-rails', require: false endインストール
terminal$ bundleRuboCopを使ってみる
terminal$ bundle exec rubocop(余談ですが、bundle execって何?という時は bundler、bundle execについて ※自分用メモ の記事がオススメ。)
なんだかエラーがたくさん出てしまった・・・
直さなければいけないところが書かれているので親切だけど、全部直すには時間がかかり過ぎてしまう。ということで以下のコマンドを実行
$ rubocop --auto-gen-configすると、.rubocop_ymlと.rubocop_todo.ymlの2つのファイルが生成される。
.rubocop_todo.yml
.rubocop_todo.ymlファイルの生成によって、先ほど大量に出た警告が一旦無視されるように設定される。すなわち.rubocop_todo.ymlに書かれているかぎり、RuboCopに怒られなくて済むということである。
試しにもう一度 budle exec rubocop を実行すると、警告が出なくなっている。
とはいえあくまでも今は一時的に警告を回避しているに過ぎないので、このファイルに何も書かれていなくてもRuboCopから警告文が表示されない状況に持っていくことが最終的な目標となる。
警告をなくす
rubocop_todo.ymlBundler/OrderedGems: Exclude: - 'Gemfile' Layout/SpaceInsideArrayLiteralBrackets: Exclude: - 'config/environments/production.rb' Layout/TrailingEmptyLines: Exclude: - 'Gemfile'例えば、.rubocop_todo.ymlに上記のような記載があったら、まずは
Bundler/OrderedGems:
Exclude:
- 'Gemfile'をコメントアウトして、bundle exec rubocop を実行。すると警告文が出る。
このあとの手順は2パターンある。
①警告に従って、コードを修正する
②コードは変えず、警告のデフォルト変更・もしくは今後警告を出さないように設定する①は文章通り、該当ファイルを開いて、コードを修正するということ。
.rubocop.yml
②については、先ほど .rubocop_todo.ymlと同時に生成された .rubocop.ymlに設定を書いていく。
- RuboCopの対象から外す時
rubocop.ymlinherit_from: .rubocop_todo.yml AllCops: # 除外するファイル Exclude: - "config/environments/*" - "vender/**/*" - "bin/**" - "db/**/*" - "tmp/**/*" - "lib/**/*" - "Gemfile"自分でExcludeを設定する時は、デフォルトの設定が外れてしまうため、それらも改めて明記する必要がある。
ちなみに、上記のファイルの最上部にinherit_from: .rubocop_todo.yml があるが、この一文がないと、.rubocop_todo_ymlに書かれた内容が反映されないので注意。
- Copの無効化
Copとは、RuboCopにおける1つのルールのことである。
上から3つ目までのCopは、有効にするか無効にするかを記述しないとエラーが出るので書く。rubocop.ymlStyle/HashEachMethods: Enabled: true Style/HashTransformKeys: Enabled: true Style/HashTransformValues: Enabled: true # 日本語のコメントを許可する Style/AsciiComments: Enabled: false警告文に対してどちらかの対処を行ったら、コメントアウトした警告を削除する。
.rubocop_todo.ymlの中身がなくなるまでこの繰り返し。まとめ
- .rubocop.yml:コーディングのルールを記載
- .rubocop_todo.yml:現在の違反部分を記載 (名前の通りToDoリスト的な役割だ!)
.rubocop_todo.ymlの違反を1つずつチェックして、コードを修正もしくは新たなルールを作って.rubocop.ymlに記載するのがRuboCopの基本的な使い方である。
おわりに
とにかく今後はRuboCopを適宜利用して、規約に沿ったコードを書いていきたいと思います。本記事で何かツッコミどころ、おかしな点などあればご教示いただけると幸いです。
尚、本記事は以下を参考にしています。
- 投稿日:2020-08-20T21:49:55+09:00
Railsで既存アプリにgretelを使用しパンくずリストを追加した。
目的
Railsで作成したフリマサイトでgretelを使ってパンくずリストを作成する。
gretel導入
https://github.com/lassebunk/gretel
document
https://www.rubydoc.info/gems/gretel
install
gem "gretel"$ bundle installfileの設定
設定fileを下記コマンドで作成
$ rails generate gretel:installconfig/breadcrumb.rb# Root crumb crumb :root do link "Home", root_path end # Issue list crumb :issues do link "All issues", issues_path end # Issue crumb :issue do |issue| link issue.title, issue parent :issues endviewに設置
<% breadcrumb :issue, @issue %>上記で各ページでどこまでのパンくずを出すのか設定する。実際にパンくずが出るのは下記を記述した箇所
<%= breadcrumbs pretext: "You are here: ", separator: " › " %>
- pretext→パンくずリストの前のテキスト
- separator→パンくずの間の区切り
親の設定
config/breadcrumb.rbのcrumbとendの間にparentを設定することで親を設定することができる。
config/breadcrumb.rbcrumb :root do link "Home", root_path end # user#index crumb :users do link "Users", users_path parent :root end上記でparentにrootを設定。
リンクをつける
リンクの設定
リンクはconfig/breadcrumb.rbのcrumbとendの間にlinkを下記のように設定する。
config/breadcrumb.rbcrumb :root do link "Home", root_path end # user#index crumb :users do link "Users", users_path parent :root end
- 投稿日:2020-08-20T21:47:23+09:00
PCが落ちてRailsがposgreSQLにつながらない 解決策
Rails でPG::ConnectionBad のエラーの解決方法
Macbook pro でスクールのrailsの課題を勧めていたらパソコンが落ちてしまった。PCを再起動してRails をターミナルで起動させようとしようとおなじみの以下を入力
% rails schromeでlocalhost:3000につなげてみると画像のようなエラーが発生した。
SQLが停止していると思い% brew services start postgresqlを行ったみたものの
Service `postgresql` already started, use `brew services restart postgresql` to restart.なので
% brew services restart postgresqlを実行してみたものの、全く変化なし。
英語のサイトを参照https://stackoverflow.com/questions/13410686/postgres-could-not-connect-to-server
rm /usr/local/var/postgres/postmaster.pid実行したらうまく行った? 1時間かかりましたが解決してよかった。もし同じようなエラーで困っている方がいたら参考にしてください。
posgreを正常に停止させないと出てくるエラーらしいです。
- 投稿日:2020-08-20T18:40:45+09:00
ActionControllerエラー集(Ruby on Rails)※随時更新
NoMethodError in [コントローラー名]#[アクション名]→undefined method `[キーワード]' for #<>
→[コントローラー名]の[アクション名]に[キーワード]のメソッドが定義されていない。
Sprockets::Rails::Helper::AssetNotFound in [コントローラー名]#[アクション名]→The asset "" is not present in the asset pipeline.
unknown attribute '[カラム名]' for [テーブル名].
→[テーブル]に[カラム]が見つからない。
【原因】
引っ張ってきたインスタンス変数やテーブルのカラムにないカラム名を指定してしまっている。私の場合編集画面で、
edit.html.haml= form_with model: @item, do |f| = f.file_field :[カラム名]で指定したカラムがテーブルに存在していませんでした。
【対処】
①マイグレーションファイルに指定のカラムがあるか
②マイグレーションファイルの指定のカラムにスペルミスはないか
を確認ミスがあった場合、
マイグレーションファイルを修正
→rails dbコマンド周りはこちら【逆引きrails dbコマンド集】
参考記事
- 投稿日:2020-08-20T18:30:09+09:00
rails dbコマンド逆引き
はじめに
Ruby on Railsで開発をしていて、MySQLのDBを修正する機会が何度もあり、その都度エラーやデータの入れ直しが発生するため、コマンドの概要ともう少し掘り下げたところまでそれぞれの意味がおさらいできればと思い、記事にしました。
ご指摘等あればよろしくお願いいたします。入門編
①モデルファイルを作成したので、DBに反映させたい→migrate
rails g modelでモデルファイル作成後、
$ rails db:migrate②DBを作ったが、不要になったので削除したい→drop
$ rails db:drop③1つ前に反映したDBテーブルを修正するために、migrationを戻したい→rollback
$ rails db:rollback入門編まとめ
・migrateを実行すると、モデルファイルに基づいてマイグレーションファイルが作られる。
・作られたマイグレーションファイルを修正し、DBに反映するためには一度rollbackを実行しDB上から該当のテーブルを下げないといけない。(この際DBに入力したデータは削除される)初級編
①現在、どのマイグレーションファイルがDB上で有効なのか確認する→migirate:status
$ rails db:migrate:status適用されている場合はup、適用されていない場合はdownとしてファイルがターミナル上に表示される。
②任意のファイルまでrollbackを戻したい場合→rollback STEP=[ ]
$ rails db:rollback STEP=[戻したいステップ数]参考記事:【Rails】$rails db:rollbackしたい時の間違えない手順
③作成したDBを一旦削除して再度作成する(その1)→reset
$ rails db:reset④作成したDBを一旦削除して再度作成する(その2)→migrate:reset
$ rails db:migrate:reset初級編まとめ
・【resetとmigrate:resetの違い】どちらもDBを一旦dropして再度作成することは同じだが、どのファイルを元に作り直すかが異なるため、migrate:resetではマイグレーションファイルを修正していればその内容も反映して作成されるが、migrate:resetではそうならないようだ。
詳しくは以下(常にmigrate:resetを使っていれば良さそうなものだが、そうもいかない時があるようだ)
参考記事:rake db:reset と rake db:migrate:reset の違い
参考記事:rails db:migrate:resetできなかったのでrails db:resetした中級編
入力したDBの内容を消さずにカラムを修正したい→g migrationでカラム追加→db:migrate→カラム追加用のmigrationファイル削除
まとめ
コマンド 概要 オプションその他 migrate DBを作成 drop DBを削除 reset drop + migrate マイグレーションファイルの修正は反映されない migrate:reset drop + migrate マイグレーションファイルの修正も反映される migrate:rollback 1つ前に反映したmigrateまで STEP=[戻したいステップ数]で任意のmigrateまでDBを戻す migrate:status 現在どこまでmigrateが反映されているか確認する
- 投稿日:2020-08-20T18:30:09+09:00
逆引きrails dbコマンド集
はじめに
Ruby on Railsで開発をしていて、MySQLのDBを修正する機会が何度もあり、その都度エラーやデータの入れ直しが発生するため、コマンドの概要ともう少し掘り下げたところまでそれぞれの意味がおさらいできればと思い、記事にしました。
ご指摘等あればよろしくお願いいたします。入門編
①モデルファイルを作成したので、DBに反映させたい→migrate
rails g modelでモデルファイル作成後、
$ rails db:migrate②DBを作ったが、不要になったので削除したい→drop
$ rails db:drop③1つ前に反映したDBテーブルを修正するために、migrationを戻したい→rollback
$ rails db:rollback入門編まとめ
・migrateを実行すると、モデルファイルに基づいてマイグレーションファイルが作られる。
・作られたマイグレーションファイルを修正し、DBに反映するためには一度rollbackを実行しDB上から該当のテーブルを下げないといけない。(この際DBに入力したデータは削除される)初級編
①現在、どのマイグレーションファイルがDB上で有効なのか確認する→migirate:status
$ rails db:migrate:status適用されている場合はup、適用されていない場合はdownとしてファイルがターミナル上に表示される。
②任意のファイルまでrollbackを戻したい場合→rollback STEP=[ ]
$ rails db:rollback STEP=[戻したいステップ数]参考記事:【Rails】$rails db:rollbackしたい時の間違えない手順
③作成したDBを一旦削除して再度作成する(その1)→reset
$ rails db:reset④作成したDBを一旦削除して再度作成する(その2)→migrate:reset
$ rails db:migrate:reset初級編まとめ
・【resetとmigrate:resetの違い】どちらもDBを一旦dropして再度作成することは同じだが、どのファイルを元に作り直すかが異なるため、migrate:resetではマイグレーションファイルを修正していればその内容も反映して作成されるが、migrate:resetではそうならないようだ。
詳しくは以下(常にmigrate:resetを使っていれば良さそうなものだが、そうもいかない時があるようだ)
参考記事:rake db:reset と rake db:migrate:reset の違い
参考記事:rails db:migrate:resetできなかったのでrails db:resetした中級編
入力したDBの内容を消さずにカラムを修正したい→g migrationでカラム追加→db:migrate→カラム追加用のmigrationファイル削除
まとめ
コマンド 概要 オプションその他 migrate DBを作成 drop DBを削除 reset drop + migrate マイグレーションファイルの修正は反映されない migrate:reset drop + migrate マイグレーションファイルの修正も反映される migrate:rollback 1つ前に反映したmigrateまで STEP=[戻したいステップ数]で任意のmigrateまでDBを戻す migrate:status 現在どこまでmigrateが反映されているか確認する
- 投稿日:2020-08-20T18:30:09+09:00
逆引きrailsコマンド集【DB編(入門〜初級)】
はじめに
Ruby on Railsで開発をしていて、MySQLのDBを修正する機会が何度もあり、その都度エラーやデータの入れ直しが発生するため、コマンドの概要ともう少し掘り下げたところまでそれぞれの意味がおさらいできればと思い、記事にしました。
ご指摘等あればよろしくお願いいたします。入門編
①モデルファイルを作成したので、DBに反映させたい→migrate
rails g modelでモデルファイル作成後、
$ rails db:migrate②DBを作ったが、不要になったので削除したい→drop
$ rails db:drop③1つ前に反映したDBテーブルを修正するために、migrationを戻したい→rollback
$ rails db:rollback入門編まとめ
・migrateを実行すると、モデルファイルに基づいてマイグレーションファイルが作られる。
・作られたマイグレーションファイルを修正し、DBに反映するためには一度rollbackを実行しDB上から該当のテーブルを下げないといけない。(この際DBに入力したデータは削除される)初級編
①現在、どのマイグレーションファイルがDB上で有効なのか確認する→migirate:status
$ rails db:migrate:status適用されている場合はup、適用されていない場合はdownとしてファイルがターミナル上に表示される。
②任意のファイルまでrollbackを戻したい場合→rollback STEP=[ ]
$ rails db:rollback STEP=[戻したいステップ数]参考記事:【Rails】$rails db:rollbackしたい時の間違えない手順
③作成したDBを一旦削除して再度作成する(その1)→reset
$ rails db:reset④作成したDBを一旦削除して再度作成する(その2)→migrate:reset
$ rails db:migrate:reset初級編まとめ
・【resetとmigrate:resetの違い】どちらもDBを一旦dropして再度作成することは同じだが、どのファイルを元に作り直すかが異なるため、migrate:resetではマイグレーションファイルを修正していればその内容も反映して作成されるが、migrate:resetではそうならないようだ。
詳しくは以下(常にmigrate:resetを使っていれば良さそうなものだが、そうもいかない時があるようだ)
参考記事:rake db:reset と rake db:migrate:reset の違い
参考記事:rails db:migrate:resetできなかったのでrails db:resetした中級編
入力したDBの内容を消さずにカラムを修正したい→g migrationでカラム追加→db:migrate→カラム追加用のmigrationファイル削除
参考記事:Rails カラム名変更方法
参考記事:マイグレーションを使ったカラムの追加、削除、データ型の変更 [ 自分用メモ ]まとめ
コマンド 概要 オプションその他 migrate DBを作成 drop DBを削除 reset drop + migrate マイグレーションファイルの修正は反映されない migrate:reset drop + migrate マイグレーションファイルの修正も反映される migrate:rollback 1つ前に反映したmigrateまで STEP=[戻したいステップ数]で任意のmigrateまでDBを戻す migrate:status 現在どこまでmigrateが反映されているか確認する
- 投稿日:2020-08-20T16:35:39+09:00
【個人アプリ開発メモ】ログアウトしようとして生じたエラーの解決
ログアウトしようとしたらエラーが出てしまった。
エラー文
undefined method `id' for nil:NilClassposts_controller.rbdef index @posts = Post.all.includes(:user).order("created_at DESC").page(params[:page]).per(5) @post = Post.new #エラーが出たのはこの部分↓↓↓ @calorie_sum = Post.where(user_id: current_user.id, created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day).sum(:calorie) gon.today_sum = @calorie_sum @standard = Standard.find_by(user_id: current_user.id) @calorie_standard = @standard.calorie gon.standard = @calorie_standard if @calorie_sum >= @calorie_standard @difference = @calorie_sum - @calorie_standard else @difference = @calorie_standard - @calorie_sum end endなぜこのエラーが生じたのか?
posts_controller.rb@calorie_sum = Post.where(user_id: current_user.id, created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day).sum(:calorie)エラーが出たこのコードはどういう意味かというと、
Postモデル(投稿データが入ってる)から、
今日1日のcurrent_userの投稿を検索して抽出して、calorieカラムの数値を合計した結果をcalorie_sumというインスタンス変数に代入してる。なんでエラーになるかというと、
ログアウトするとuser_id: current_user.idのidのところがnilになってしまうから。解決するにはどうすればいいのか?
current_user.idがnilでもエラーにならないようにするためには、
posts_controller.rbを条件分岐で書けばいい気がする。こんな感じでどうだろうか?
posts_controller.rbdef index @posts = Post.all.includes(:user).order("created_at DESC").page(params[:page]).per(5) @post = Post.new # サインインしてる時だけ今日の摂取カロリーを表示するグラフを作るためのデータを取得する下記の関数が動く if user_signed_in? @calorie_sum = Post.where(user_id: current_user.id, created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day).sum(:calorie) gon.today_sum = @calorie_sum @standard = Standard.find_by(user_id: current_user.id) @calorie_standard = @standard.calorie gon.standard = @calorie_standard if @calorie_sum >= @calorie_standard @difference = @calorie_sum - @calorie_standard else @difference = @calorie_standard - @calorie_sum end else # サインインしてない時は何も動かない end endこれにあとviewのグラフの部分を
- if user_signed_in?で条件分岐して、サインインしてない時はグラフが出てこないようにしたら、無事解決しました。
- 投稿日:2020-08-20T15:44:39+09:00
【個人アプリ開発メモ】ArgumentError (wrong number of arguments (given 0, expected 2)):の解決
直面している問題
投稿しようとしたらできない。
なぜ?ターミナルに以下のエラー文が表示された。
エラー文
ターミナルArgumentError (wrong number of arguments (given 0, expected 2)): app/controllers/posts_controller.rb:21:in `create'argumentとは引数のことらしい。
2つの引数がなければいけないのに、それが0になってるからダメだという。引数が2つ必要?何のことだろう?
posts_controller.rbdef create @post = Post.new(post_params) ← ここで止まってしまってる if @post.save redirect_back(fallback_location: root_path) # なぜredirect_to root_pathじゃダメなのかわかってない else @posts = Post.includes(:user) flash.now[:alert] = '必須項目をしてください。' # フラッシュメッセージが出るか確認する redirect_back(fallback_location: root_path) end end private def post_params params.require(:post).permit(:food, :calorie, :protein, :fat, :carbo, :text, :image).merge(user_id: current_user.id) end試してみたこと
binding.pryでpost_paramsの中身を確認してみよう。
[1] pry(#<PostsController>)> post_params => <ActionController::Parameters {"food"=>"うどん", "calorie"=>"10", "protein"=>"10", "fat"=>"10", "carbo"=>"10", "text"=>"うどんを食べました", "image"=>#<ActionDispatch::Http::UploadedFile:0x00007fc7e7b4eae0 @tempfile=#<Tempfile:/var/folders/sy/26p55v5j47s2zjd8h7vr8f6h0000gn/T/RackMultipart20200820-34304-1e1sjww.png>, @original_filename="image4.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"post[image]\"; filename=\"image4.png\"\r\nContent-Type: image/png\r\n">, "user_id"=>17} permitted: true>ここが原因ではないようだ。
https://teratail.com/questions/164134
によれば、ターミナルArgumentError (wrong number of arguments (given 0, expected 2)):これは呼び出し側の引数の数 = 0
メソッド側の引数の数 = 2
という意味らしい。呼び出し側の引数の数と、メソッド側と引数の数はそれぞれどこで設定してるのだろうか?
試したこと
画像を選択せずに、画像以外を投稿してみた。
その結果、これは問題なく投稿できた。
この結果からわかったのは、やはり問題は画像関連だということ。画像を選択して、画像も投稿しようとするとエラーが生じる。
画像を投稿しようとすると、メソッド側の引数が2になる?解決した。原因は「え?そこ?」という感じ
image_uploader.rb#[300, 200][100, 100]という感じで2つあるとダメ process resize_to_fit: [300, 200][100, 100] #1つに戻したら解決した process resize_to_fit: [300, 200]前に投稿画像サイズの設定で、2通りのサイズを設定したいと思って上記のように設定していたが、これが邪魔だったみたい。
https://qiita.com/naota7118/items/33943b8887a4990645a9余計なことしなければよかった。
- 投稿日:2020-08-20T15:16:45+09:00
【LINE Bot】読書を手助けしてくれるコトバンクbotを作ってみた!
作ったもの
コトバンクで単語を検索して、その意味を返信してくれるLINE Botです。
辞書アプリやブラウザを立ち上げなくても、LINEで手軽に単語や言い回しの意味を調べられます。
背景
読書中の単語検索は煩わしい
たとえば読書をしていて知らない単語や言い回しに遭遇した場合、大抵は「スマホでブラウザを起動」→「単語を入力」→「検索結果の一覧から適当にページを選択」→「ページ内で単語の意味を確認」という手順でその意味を確認すると思います。(Kindleの辞書機能でも、固有名詞や珍しい言い回しは意外とヒットしなかったり)
これが1冊で1〜2回の作業なら何も問題ないのですが、海外の古典文学や厨二用語全開のラノベ(謎の横文字や難読漢字だらけ!)を読む場合だと、下手をすると数ページおきに知らない言葉が出てきたりして、そのたびに「スマホでブラウザを起動」→「単語を入力」→「検索結果の一覧から適当にページを選択」→「ページ内で単語の意味を確認」→「次の単語を入力」→「検索結果の一覧から適当にページを選択」→……(以下ループ)とするのは非常に面倒です。
そこでLINE Botの登場
その点、LINE Botであれば「LINEを起動」→「Botに単語を送信」→「返答(単語の意味)を確認」で済みますし、連続して検索する場合でも「単語を入力」→「返答を確認」→「単語を入力」→……だけで済みます。
また、チャット上に検索結果のログも残るので、読書後などにあらためて単語を確認することもできて一石二鳥なのではないかと考えました!
参考にさせていただいた記事
【Rails】1時間ぐらいで簡単にLINEのBot開発をしよう-アンケート集計Bot基礎-【画像付き】
LINE Developersの登録について、非常にわかりやすく学ぶことができました!1時間でWikipedia検索できるLINE BOTをサクッと作ってみよう!
Herokuのアプリケーションの作成や環境変数の設定など、めちゃめちゃ参考にさせていただきました!LINEBOT + RailsでWikipediabotを作ってみました。
WikipediaのAPIを利用してBotを作る記事です。
メッセージ応答部分の記述など、かなり参考にさせていただきました!Rails スクレイピング手法 Mechanizeの使い方
Mechanizeを使ったスクレイピングの方法を勉強させていただきました!Bot完成までの大まかな手順
- LINE Developersの登録
- Channelの作成
- トークンの発行
- Webhook URLの設定(Heroku上に作成したアプリのURLをあとから登録する)
- Herokuの登録
- Heroku上にアプリを作成
- git push heroku master(アプリをHerokuにプッシュ)
- トークンを環境変数として設定
- アプリケーションの実装
- gemの導入
- callbackアクションに処理を記述
- アプリをHerokuにアップ(完成!)
LINE Developersの登録
【Rails】1時間ぐらいで簡単にLINEのBot開発をしよう-アンケート集計Bot基礎-【画像付き】
1. 上の記事を参考に、Channelを作成
2. 「応答メッセージ: オフ」「Webhook: オン」に設定を変更
3. 「チャネルトークン」「チャネルシークレット」を発行
Herokuの登録
1時間でWikipedia検索できるLINE BOTをサクッと作ってみよう!
1.上の記事を参考に、Herokuに登録
2.Heroku上にアプリを作成(コマンドでもブラウザからでもOK)
3.さきほど発行した「チャネルトークン」「チャネルシークレット」をHerokuの環境変数に設定
% heroku config:set LINE_CHANNEL_TOKEN="チャネルアクセストークンの文字列" -a アプリケーション名 % heroku config:set LINE_CHANNEL_SECRET="チャネルシークレットの文字列" -a アプリケーション名4.Line Developerのページに戻り、Webhook URLを設定
Webhook URLに
https://作成したHerokuアプリのURL/callbackと設定し、
設定後に表示される「Webhookの利用」の項目もオンにする。アプリケーションの実装
LINEBOT + RailsでWikipediabotを作ってみました。
Rails スクレイピング手法 Mechanizeの使い方1.上の記事を参考にgemを導入
Gemgilegem 'line-bot-api' gem 'mechanize' gem 'dotenv-rails'% bundle install2.ルーティングの設定
config/routes.rbRails.application.routes.draw do post '/callback' => 'linebot#callback' end3.コントローラーの作成
% rails g controller linebot4.コントローラーの編集
app/controllers/linebot_controller.rbclass LinebotController < ApplicationController require 'line/bot' require 'mechanize' # callbackアクションのCSRFトークン認証を無効 protect_from_forgery :except => [:callback] def client @client ||= Line::Bot::Client.new { |config| config.channel_secret = ENV["LINE_CHANNEL_SECRET"] config.channel_token = ENV["LINE_CHANNEL_TOKEN"] } end def callback body = request.body.read signature = request.env['HTTP_X_LINE_SIGNATURE'] unless client.validate_signature(body, signature) head :bad_request end events = client.parse_events_from(body) events.each { |event| if event.message['text'] != nil # LINEで送られてきた文書を取得 word = event.message['text'] # インスタンス生成 agent = Mechanize.new end # ページのHTMLを取得 page = agent.get("https://kotobank.jp/word/#{word}") # 要素を取得 if page.search('.dictype.cf.daijisen').present? # デジタル大辞泉の項目が存在する場合の処理 elements = page.search('.dictype.cf.daijisen .description') elsif page.search('.dictype.cf.js-contain-ad.daijisenplus').present? # デジタル大辞泉プラスの項目が存在する場合の処理 elements = page.search('.dictype.cf.js-contain-ad.daijisenplus .description') else # デジタル大辞泉とデジタル大辞泉プラスの項目が存在しない場合の処理 elements = page.search('.description') end # 概要を返す response = elements.inner_text.gsub(/(\s| )+/, '') case event # メッセージが送信された場合 when Line::Bot::Event::Message case event.type # メッセージが送られて来た場合 when Line::Bot::Event::MessageType::Text message = { type: 'text', text: response } client.reply_message(event['replyToken'], message) end end } head :ok end endbotから送られてくるメッセージの前後に大量の空白があったので、
.gsub(/(\s| )+/, '')を追加して、スペースを取り除いています。# 概要を返す response = elements.inner_text.gsub(/(\s| )+/, '')5.Herokuにデプロイ
% git add . % git commit -m "linebot" % git push heroku master最後に
元々はAPIの学習を目的として作ったものでしたが、せっかくなので記事としてアウトプットしました!
ご指摘などございましたら、ぜひコメントよろしくお願いいたします!
- 投稿日:2020-08-20T15:14:18+09:00
【Rails】Enumを使う
どんな時に使う?
カラムstatusの値が
0だったら未処理
1だったら進行中
2だったら処理済み
みたいな時に、いい感じに書くことができます。定義
migrationファイルdef change add_column :orders, :status, :integer, default: 0 # デフォルト値は0に設定する endapp/models/order.rbclass Order < ApplicationRecord enum status: { waiting: 0, working: 1, completed: 2 } endできること
インスタンスを作成
order = Order.new( item: 'ボールペン', price: 150, status: 'working' # 今まで「1」と数値でセットしていた部分を文字列でセットできる。DBには「1」が保存される )インスタンスの中身を確認
pry(main)> order.status => "working" pry(main)> order.working? => true pry(main)> order.completed? => falseインスタンスを更新
pry(main)> order.status => "working" pry(main)> order.completed! => "completed" pry(main)> order.status => "completed"DBを検索
pry(main)> completed_orders = Order.completed => SELECT `orders`.* FROM `orders` WHERE `orders`.`status` = 2 LIMIT 11複数カラムで同じ名前を使いたい場合
こういう場合class Order < ApplicationRecord enum hoge_status: { waiting: 0, working: 1, completed: 2 } enum fuga_status: { waiting: 0, working: 1, completed: 2 } end
Order.waiting?としても、hoge_statusとfuga_statusのプログラムはどちらを見れば良いかわかりません。
こういった場合、_prefix: trueを使って解決します。prefixで解決class Order < ApplicationRecord enum hoge_status: { waiting: 0, working: 1, completed: 2 }, _prefix: true enum fuga_status: { waiting: 0, working: 1, completed: 2 }, _prefix: true endpry(main)> Order.hoge_status_completed? => SELECT `orders`.* FROM `orders` WHERE `orders`.`hoge_status` = 2 LIMIT 11 ` pry(main)> Order.fuga_status_completed? => SELECT `orders`.* FROM `orders` WHERE `orders`.`fuga_status` = 2 LIMIT 11参考
- 投稿日:2020-08-20T15:05:40+09:00
HerokuへデプロイしたアプリのDBを操作する方法! CLEARDB_DATABASE Sequel_Pro
環境
rails 6.0.3
Mysql 0.4.4
アプリケーションはHerokuへデプロイ済概要
Herokuへデプロイは出来たものの保存されたデータって見れないものだろうか
テストのために色々データ値を変更したり追加したりしたい方法
1.Sequel Proをダウンロード
Mysqlデータベースを難しいコマンド等必要なくGUIで操作できるアプリ
Sequel Proダウンロードリンク2.ターミナルでherokuの設定を確認
デプロイしたアプリケーションのディレクトリ下で(ターミナル)
heroku configを実行
下記のような設定が出力される(実際には///の部分はもっと長い)CLEARDB_DATABASE_URL: mysql:// DATABASE_URL: mysql2:// DB_HOSTNAME: /// DB_NAME: /// DB_PASSWORD: /// DB_PORT: /// DB_USERNAME: /// LANG: /// RACK_ENV: /// RAILS_ENV: /// RAILS_LOG_TO_STDOUT: /// RAILS_SERVE_STATIC_FILES: /// SECRET_KEY_BASE: ///3.Sequel Proにデータベース情報を入力
一番上のタブを「標準」に設定して
2.でターミナルに出力したCLEARDB_DATABASE_URL:の情報をもとにSequel Proに入力
※ポート名は空白でOKCLEARDB_DATABASE_URL: mysql://ユーザー名:パスワード@ホスト/データベース?reconnect=true次に「接続」と青いボタンを押したいところだがちょっとまった!
次回からもデータベースに接続を行うならお気に入りに追加をしておこうそして最後に「接続をテスト」をクリックして接続テストに成功すれば完了
最後に
Sequel Proの使い方は各々で調べてみて下さい。
私みたいなSQLコマンドひとつもわからない初心者でも簡単にDB操作が出来ちゃいます。かなり便利。
- 投稿日:2020-08-20T14:59:56+09:00
SNS認証 facebookだけ通らなかった原因
前提
- 今回、sns認証(google, facebook)を実装していました。
- そこで、googleは既存のusersテーブルと紐づくのに、facebookだけ認証が通らないエラーが発生しましたので、原因を記録します。(そのため、sns認証の細かい実装方法に関しては割愛します。)
- 原因は判明しましたが、根本的な解決には至っていませんので、あくまで原因を探す参考程度にご覧ください。
実行環境
rails 6.0.0
mysql 5.6.47
エラーの内容
今回、snsの認証とともに、userの情報を紐付けて保存する仕様にしていました。
ところがログインしようとすると、、、
おかしい、、、
Googleでは確かにログインができるのに、Facebookでは新規登録画面へ飛ばされてしまいました。原因
原因は、user.rbにありました。apiから送られてくるデータの検索方法がまずかったようです。
【user.rb】def self.from_omniauth(auth) sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create # sns認証したことがあればアソシエーションで取得 # 無ければemailでユーザー検索して取得orビルド(保存はしない) user = sns.user || User.where(email: auth.info.email).first_or_initialize( nickname: auth.info.name, email: auth.info.email ) # userが登録済みの場合はそのままログインの処理へ行くので、ここでsnsのuser_idを更新しておく if user.persisted? sns.user = user sns.save end { user: user, sns: sns } endこの記述の中だと、auth(apiから送られてくるユーザーデータ)の中から、:emailを参照してしまいます。
しかし、私自信、facebookのアカウントにemailを登録していなかったため、この検索に引っかからず、snsの情報をDBに保存していないよねと判断され、弾かれていたようです。
その後、Facebookのアカウント設定でemailを登録したところ、無事にログインできるようになりました。今後の課題
このままだと、emailをもたないfacebookユーザーは原因がわからないまま永遠にログインできない状態になってしまいます。ですので、データ参照の仕様を工夫する必要がありそうです、、、
- 投稿日:2020-08-20T14:22:04+09:00
Rails × Herokuアプリ高速化のためにやった/やりたいこと
実装が比較的楽で、インパクトが大きいものをピックアップ。
Cloudflare (CDN)
JSやCSSはCDNに乗せる。他のCDNでも良いが、Cloudflareは無料だから最高。
Cloudinary (画像配信)
画像はCloudinaryで最適化して配信。画像の圧縮、適切なサイズでの配信、Webp対応等全部やってくれる。
クエリ最適化
N+1をなくす。pluckを使う。
partialを使いすぎない
partialは、保守性可読性とパフォーマンスのトレードオフ。
collectionオプションが使える場合には活用する。ループの中の処理に気を使う
インスタンスを無駄に生成しない、など。
HTMLをgzip圧縮
Herokuだとデフォルトでgzip圧縮してくれないので、gemを使って対応。
ブラウザにキャッシュさせる期間を伸ばす
Cloudflare等の設定をいじって対応
重い処理はPumaの外に流す
例えば画像アップロードはLambda等に捌かせる
金を積む
最後はこれ
- 投稿日:2020-08-20T14:21:18+09:00
【個人アプリ開発メモ】画像が粗くなってしまう問題の解決
直面している問題
ヒントはつかめてる
画像のデフォルトのサイズは
app/uploaders/image_upload.rbのこの部分で設定できる。image_upload.rbprocess resize_to_fit: [300, 200]これと同じ大きさで画像を表示すれば、キレイに表示できる。
逆にこれと違う大きさで画像を表示しようとすると、粗くなってしまう。しかしこれで問題が完全にクリアになったわけではない。
こちらはプレビュー画像なのだが、こちらが少し粗くなってしまった。複数の画像の大きさの設定したいときはどうすればいいのだろうか?
今気づいたのだが、選択する画像によってキレイになったり粗くなったりする。
これがなぜそうなるのかがまだわからない。
- 投稿日:2020-08-20T12:59:30+09:00
【個人アプリ開発メモ】画像のプレビュー表示
やりたいこと
アップロードしたい画像を選択したら、プレビュー表示できるようにしたい。
なぜ画像プレビュー機能が必要なのか?
フォームを作っただけでは、画像を選択してもどの画像が選択されたかアップロード前に確認することができない。
アップロードされてから違う画像をアップロードしてしまったと気づくようなことを防ぐために、この機能は必要。
どう進めるか
まず画像プレビュー用のブランチを立ち上げる。
次に「画像 rails プレビュー」で検索して参考記事を探す。https://qiita.com/Masanori_N/items/71dbf648737f32dd8588
この記事を参考に進めていこう。作業内容
jQueryで実装するため、jsファイルを作る。
javasciptディレクトリ直下に「image-preview.js」ファイルを設置した。_main.html.haml.new-post = form_with model: @post, id: 'new_post' do |f| .input-box (省略) .icon .image-upload %i.fa.fa-camera.fa-2x = f.file_field :image, class: 'image_upload' .btn-square = f.submit '投稿する', class: 'post-btn' .posts = render @posts画像ファイル選択時にイベントを発火させる
まずはファイルを選択した時にイベントが発火するようにする。
image-preview.js$(function() { $(function() { $(document).on('change', 'image_upload', function() { console.log('hoge'); }) }); });これだとイベントは発火しなかった。
image-preview.js$(document).on('change', '.image_upload', function() { console.log('hoge'); })クラス名の指定で「.」が抜けているというミスが原因だった。
'image_upload'→'.image_upload'と修正したら無事イベントは発火した。画像データの読みこみ
いよいよここから難しくなる。
よくわからない時はconsole.logで中身を確認するに限る。image-preview.js$(function() { $(function() { $(document).on('change', '.image_upload', function() { console.log('hoge'); //選択したfileのオブジェクトを取得 var file = this.files[0]; console.log(file); }) }); });
console.log(file);で選択したfileを取得できているか確認する。検証画面で確認すると、
File {name: "image4.png", lastModified: 1596487703899, lastModifiedDate: Tue Aug 04 2020 05:48:23 GMT+0900 (日本標準時), webkitRelativePath: "", size: 110024, …}と出てきたので、おそらくちゃんと画像fileを取得できている。
次にFileReaderを使ってFileReaderオブジェクトを生成する。
FileReaderとは何なのか?https://developer.mozilla.org/ja/docs/Web/API/FileReader
によると、FileReader オブジェクトを使うと、ユーザーのコンピューター内にあるファイル (もしくはバッファ上の生データ) をウェブアプリケーションから非同期的に読み込むことが出来ます。読み込むファイルやデータは File ないし Blob オブジェクトとして指定します。
とある。
なるほど、ローカルにある画像ファイルを非同期で読み込むことができるのか。
image-preview.js//FileReaderオブジェクトの生成 var reader = new FileReader();これもコンソール画面で確認してみよう。
FileReader {readyState: 0, result: null, error: null, onloadstart: null, onprogress: null, …}次は
image-preview.js//readAsDataURLで指定したFileオブジェクトを読み込む reader.readAsDataURL(file);これもよくわからないのでそのままググってみよう。
https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsDataURL
ここでもMDNが参考記事として出てきた。これもFileオブジェクトを読み込むために必要らしい。
image-preview.jsvar hoge = reader.readAsDataURL(file); console.log(hoge);こんな感じで変数に代入して中身を見てみた。
すると、undefinedとなった。image-preview.js//readAsDataURLで指定したFileオブジェクトを読み込む var hoge = reader.readAsDataURL(file); console.log(hoge[0]);これだとどうか?
これもダメだ。
これは中身を見ることはできないみたい。image-preview.js//読み込み時に発火するイベント onloadメソッドは読み込みが完了したら実行する reader.onload = function() { ここに画像の読みこみ完了後の処理を書いていく }これもわからないので調べた。
https://developer.mozilla.org/ja/docs/Web/API/FileReader/onloadFileReader.onload プロパティは、readAsArrayBuffer や readAsBinaryString、 readAsDataURL、readAsText でのコンテンツ読み込みが完了して、利用可能になると発火する load イベント時に実行されるイベントハンドラを含みます
3回読んでも完全にはわからない。
readAsDataURLで画像ファイルの読みこみが完了して、利用可能になるとloadイベントが発火する。
というところまではわかった。image-preview.js//loadイベントが発火するかを調べる reader.onload = function() { console.log('hoge'); }コンソール画面でちゃんと「hoge」と出てきたので、loadイベントは問題なく発火してるとわかった。
その次にloadイベントの結果を取得する。
image-preview.js//直前に実行したイベントが返した値を取得する var image = this.result;プレビュー用のHTMLを設置します。
ここが結構時間かかるかな?と思っていたが、一発でいけました!!ここまでのコードはこんな感じです。
image-preview.js$(function() { $(function() { // プレビューHTML生成 function buildHTML() { var html = `<div class="preview-box"> <div class="upper-box"> <img src="" alt="preview" class="upload-image"> </div> <div class="lower-box"> <div class="delete-box"> <span>削除</span> </div> </div> </div>`; return html; } $(document).on('change', '.image_upload', function() { //選択したfileのオブジェクトを取得 var file = this.files[0]; //FileReaderオブジェクトの生成 var reader = new FileReader(); //readAsDataURLで指定したFileオブジェクトを読み込む reader.readAsDataURL(file); //読み込み時に発火するイベント onloadメソッドは読み込みが完了したら実行する reader.onload = function() { console.log('hoge'); //直前に実行したイベントが返した値を取得する var image = this.result; // プレビュー用のhtmlを追加 var html = buildHTML(); $('.text').append(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); } }) }); });プレビュー画像のサイズ変更
image-preview.js// プレビューHTML生成 function buildHTML() { var html = `<div class="preview-box"> <div class="upper-box"> <img src="" alt="preview" class="upload-image" height="100px" width="100px"> </div> <div class="lower-box"> <div class="delete-box"> <span>削除</span> </div> </div> </div>`; return html; }
<img src="" alt="preview" class="upload-image" height="100px" width="100px">
imgタグのところにheightとwidthを追加すればOK?どのクラスに設定するかが大事で、親クラスに設定してもサイズは変わらないので注意です
プレビュー画像の削除
「削除」を押したらプレビュー画像が消えるようにしたい。
まず「削除」を押した時にイベントが発火するようにする。
image-preview.js$(document).on("click", '.delete-box', function(){ console.log('hogehoge'); })これで問題なく発火した。
クラス名の指定'.delete-box'が合っていればこれで発火する。削除はremoveメソッドを使えばOK。
久しぶりだから( )をつけるのを忘れていた?image-preview.js$(document).on("click", '.delete-box', function(){ $('.preview-box').remove(); })これだけだと、プレビュー画像は消えたが、まださっきの画像ファイルを選択したままになってる。
image-preview.js// 「削除」を押すと削除イベントが発火する $(document).on("click", '.delete-box', function(){ // プレビュー画像を削除 $('.preview-box').remove(); // inputタグに入ってる画像ファイルも削除 $('.image_upload').val(""); })inputタグに入ってる画像ファイルも削除しておこう。
これでプレビュー画像を削除すると、ファイルの中身が空っぽになる。2回連続ファイルを選択するとプレビュー画像が2つ表示されてしまう問題の解決
こんな感じで表示されてしまう。
これを選択するたびに入れ替わるように修正したい。
どうすればこの問題を解決できるか?考えたのは、
- すでにプレビュー画像がある場合
- プレビュー画像がない場合
に条件分岐すればいいのでは?
- プレビュー画像あり → プレビュー画像を一度削除する必要あり
- プレビュー画像なし → ここまで書いてきたコードでOK
どうやってプレビュー画像がある場合とない場合の条件分岐をするか?
image-preview.jsif($('.preview-box').length == 0){ プレビュー画像がない場合の処理 }else{ プレビュー画像がある場合の処理 }こんな感じでlengthプロパティを使って条件分岐しました。
image-preview.js// プレビュー画像がまだ場合 if($('.preview-box').length == 0){ // プレビュー用のhtmlを追加 var html = buildHTML(); $('.icon').before(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); // すでにプレビュー画像が存在する場合 }else{ // プレビュー画像を削除 ⇦ 違うのはここです $('.preview-box').remove(); // プレビュー用のhtmlを追加 var html = buildHTML(); $('.icon').before(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); }参考記事:https://www.sejuku.net/blog/34465
最終的に出来上がったコードはこちら
image-preview.js$(function() { $(function() { // プレビューHTML生成 function buildHTML() { var html = `<div class="preview-box"> <div class="upper-box"> <img src="" alt="preview" class="upload-image" height="100px" width="100px"> </div> <div class="lower-box"> <div class="delete-box"> <span>削除</span> </div> </div> </div>`; return html; } $(document).on('change', '.image_upload', function() { //選択したfileのオブジェクトを取得 var file = this.files[0]; //FileReaderオブジェクトの生成 var reader = new FileReader(); //readAsDataURLで指定したFileオブジェクトを読み込む reader.readAsDataURL(file); //読み込み時に発火するイベント onloadメソッドは読み込みが完了したら実行する reader.onload = function() { //直前に実行したイベントが返した値を取得する var image = this.result; // プレビュー画像がまだ場合 if($('.preview-box').length == 0){ // プレビュー用のhtmlを追加 var html = buildHTML(); $('.icon').before(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); // すでにプレビュー画像が存在する場合 }else{ // プレビュー画像を削除 $('.preview-box').remove(); // プレビュー用のhtmlを追加 var html = buildHTML(); $('.icon').before(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); } } }) // 「削除」を押すと削除イベントが発火する $(document).on("click", '.delete-box', function(){ // プレビュー画像を削除 $('.preview-box').remove(); // inputタグに入ってる画像ファイルも削除 $('.image_upload').val(""); }) }); });結局何をしたのか?
- プレビュー画像の表示ができた。
- プレビュー画像のサイズを小さくすることができた。
- プレビュー画像を削除できるようにした。
- 連続でファイルを選択したら、直前に選択したファイルのプレビューが消えて、新たにプレビューが表示されるようにした。
おまけ
各投稿の画像を大きくしたい。
before
after
_post.html.haml- if post.image.present? = image_tag post.image.url, class: 'post-image', width: '300px', height: '200px'各投稿は部分テンプレートで上記のHTMLで設定しており、image_tagのところにwidthとheightを追加して設定しました。
ただここで問題が。。。
見てわかるとおり、画像が粗くなってしまいました。これについては解決したらまた別の記事で書きたいと思います。
- 投稿日:2020-08-20T11:34:01+09:00
Railsで複数のSass(.scssファイル)を使用する際の読み込み方法
はじめに
RailsでSassを使用し、かつファイルを用途・適応範囲に応じて複数に分割したとき、どのような構成、読み込み方式にするかを実際の失敗談交えながら、記載したいと思います。
最初に「つまづいたこと」で、私が体験した発生した事象や原因などを書いていますが、必要なければ読み飛ばしてもらえればと思います。環境
ruby: 2.6.4
rails: Rails 6.0.3.1 (sass-rails gemを使用)まず結論
Railsで複数のSassを使用する際には、読み込みをSprocketsディレクティブ方式(require_tree)ではなく、
@importで読み込むことつまづいたこと
携わったプロジェクトではSassを使用しており、かつスタイルファイルをメンテしやすくするため、コントローラ単位で.scssファイルを作成していた。
stylesheets直下のapplication.scssで全体に共通のスタイルを設定し、stylesheets/main配下に複数の.scssファイルを配置。
application.scssでは、以下の内容を記載していた。
1. ディレクティブ方式でmain配下のscssファイルを読み込み
2. その後bootstrapなどを読み込み(@import )
3. 最後に共通スタイルを設定。application.scss〜 中略 〜 *= require_tree ./main *= require_self *= require_self */ @import "bootstrap/scss/bootstrap"; 〜 中略 〜 // 共通スタイル body { font-family: .... } 〜 中略 〜やろうとしたこと
今回、共通で使用する変数 (ex
$main-color: #3D85C6;)を作成して、各.scssファイルで使い回そうと思い、以下のように共通変数の定義を追記した。application.scss(改修後)〜 中略 〜 *= require_tree ./main *= require_self *= require_self */ @import "bootstrap/scss/bootstrap"; 〜 中略 〜 // 共通変数 $main-color: #3D85C6; // 共通スタイル body { font-family: .... } 〜 中略 〜ただ、この状態だとmain配下のscssファイルで変数を使用するとエラーになったため、変数の読み込みが必要だと思い、main配下のscssファイル側で
application.scssを@importした(今思えば、なぜこんな書き方をしたのか、後悔しかない・・・)
結果、main配下のscssファイルでも、変数を使用でき、スタイルも適用されてめでたしめでたしと思っていました。その後、起こったこと
カスタマイズを進め、applicaiton.scssに変更を加えていく中で、不可解な現象が、、、
- ブラウザの検証機能でスタイルを見ると、同じスタイルが2つある
- application.scss内のとあるクラスのスタイルから、一部プロパティを削除しても反映されない (前述のとおり同一スタイルが2つ存在し、片方しかプロパティ削除が反映されていない)
原因
すでにお気づきかもしれませんが、「やろうとしたこと」の中で、main配下のscssファイルで、application.scssをimportしてしまったたため、
そこに記載したスタイルが二重で読み込まれてしまいました。なおかつapplication.scssでimportしたbootstrap関連のscssも二重で読み込まれてました。
ただ、applicaiton.scss内のスタイルを変更しても、反映されない原因は結局わからず・・というわけで、main配下のscssでのimportを削除するとともに、railsで複数のscssファイルを利用するためには、どのような読み込み方法良いのか調べることになりました。。
railsで複数のscssファイルを利用際の適切な読み込み方法
調査した結果
railsguide, rails-sass(gem)のページに記載の通り、railsで複数のSass(.scssファイル)を使用する際には、Sprocketsディレクティブで読み込まずにSass@importルールを使用する必要があります。
ディレクティブ方式だと、宣言した変数やmixinが他のscssファイルから利用できない。(まさしく今回私が体験した内容)
Sassファイルを複数使用しているのであれば、Sprocketsディレクティブで読み込まずにSass @importルールを使用する必要があります。このような場合にSprocketsディレクティブを使用してしまうと、Sassファイルが自分自身のスコープに置かれるため、その中で定義されている変数やミックスインが他のSassから利用できなくなってしまいます。
Important Note
Sprockets provides some directives that are placed inside of comments called require, require_tree, and require_self. DO NOT USE THEM IN YOUR SASS/SCSS FILES. They are very primitive and do not work well with Sass files. Instead, use Sass's native @import directive which sass-rails has customized to integrate with the conventions of your Rails projects.最終的なapplicaiton.scssの記載内容
最終的には以下のような記述内容にしました。
これにより、common.scssで定義した変数が他のmain配下のscssファイルでも読み込むことができました。
applicaiton.scssから、reuire_tree,require_selfの記載を削除- 元々
applicaiton.scssに記載していた共通スタイルと変数は、common.scssを新たに作成して移管@importでcommon(共通スタイル)、main配下のスタイルを順に読み込みapplication.scss(最終)〜 中略 〜 * */ @import "bootstrap/scss/bootstrap"; @import "common"; @import "main/*";参考文献
あるべきscssファイルの取り込み方法を理解するために大変参考になりました。
- 投稿日:2020-08-20T05:38:54+09:00
Railsでワクワク個人開発 第4回: ネストされたリソースでCRUD処理
ルーティングの設定
わからない言葉が増えてきた。リソースというのは操作可能(作成・削除・更新・編集)なデータ群のことであり、ネストとは親子構造のことだった。私はUserとPlantをネストで扱いたかった。URLで考えると以下のようなイメージだ。
/users/(ユーザーid) /plants/ (ユーザーが所持しているplantのid)これを実現するには第3回で書いたように、モデルに対して関連付けをする必要がある。また、ルーティングに対してもいろいろやらなくてはいけない。
config/routes.rbRails.application.routes.draw do root to: 'users#index' devise_for :users resources :users do resources :plants end end
root to: 'users#index'とはトップページの設定、devise_for :usersはログイン機能に関するものだ。ここでは詳しく述べない。resources...が大事だ。このように入れ子構造にすることでネストされたリソースが実現できる。記述が終わったらターミナルで
rails routesを打ち込んで、どのようなルーティングがなされているか確認して欲しい。Userのコントローラーの追記とViewの作成
次はUserのコントローラーに追記をする。すでに以前の記事ですでにコントローラーはつくられているはずだ。以下のようにする。
app/controllers/users_controller.rbclass UsersController < ApplicationController def index @users = User.all end def show @user = User.find(params[:id]) end endここではindexアクションとshowアクションのみ作る。
次にviewファイルをつくろう。index.html.erbとshow.html.erbだ。
app/views/users/index.html.erb<h1>ユーザー一覧</h1> <% @users.each do |user| %> <p> アドレス <%= link_to user.email, user %></p> <% end %>
app/views/users/show.html.erb<h3>メールアドレス</h3> <%= @user.email %><br> <h3> 所有している植物 </h3> <% @user.plants.each do |p| %> <li><%= p.plant_name %></li> <% end %>
<%= link_to user.email, user %>によってindex.html.erbから ユーザーごとの詳細ページへ移動するリンクが貼られる。詳細ページでは p.plant_name によってユーザーが所有しているplantの名前が表示される。Plantコントローラの追記とViewの作成
今度はPlantコントローラーも書いていこう。index,newアクションなどを一気に書きくだす。(このあたりも後々解説していきたい)
class PlantsController < ApplicationController def index @user = User.find(params[:user_id]) @plants = @user.plants end def new @user = User.find(params[:user_id]) @plant = Plant.new end def create @plant = Plant.new(plant_params) current_user.plants.create(plant_params) end def show @user = User.find(params[:user_id]) @plant = @user.plants.find(params[:id]) end private def plant_params params.require(:plant).permit(:user_id, :plant_name, :img) end endViewファイルだが、今のところ使っているのはplants/create.html.erb ,plants/show.html.erb , plants/new.html.erbだけだ。 plants/index.html.erbは存在はしているが、通常のリンクではたどれない。
User一覧 > User詳細 > Plants詳細という順序で閲覧するためだ。
plants/create.html.erb<h1>登録完了</h1> <%= link_to 'マイページへ', user_path(current_user.id) %>
plants/show.html.erb<h1>植物 詳細</h1> <h3>植物の名前</h3> <%= @plant.plant_name %><br> <h3> 植物の写真 </h3> <%= @plant.img %><br>
plants/new.html.erb<h1>植物-新規登録</h1> <%= form_with model: [@user, @plant], local: true do |f| %> <p>植物名:<%= f.text_field :plant_name %></p> <%= f.submit "登録" %> <% end %>ネストされているリンク先の指定
自分が一番悩んだのがこれだ。以下の三つ。
- User詳細ページからPlant詳細ページへのリンク
- User詳細ページからPlantの新規登録ページへのリンク
- Plant新規登録ページからcreateアクション実行へのリンク
順に紹介する。
User詳細ページからPlant詳細ページへのリンク
<!-- 略 --> <% @user.plants.each do |p| %> <li><%= link_to p.plant_name ,[@user,p]%></li> <% end %> <!-- 略 -->このようにlink_toのリンク先を配列にしてやることでネストされたページへリンクを貼ることができる。
以下のページが参考になった。
User詳細ページからPlantの新規登録ページへのリンク
<!-- 略 --> <%= link_to '植物登録' ,new_user_plant_path(current_user.id) %> <!-- 略 -->current_user.idを利用することで、現在ログインしているUserのidを参照できる。これでリンクが貼れる。
Plant新規登録ページからcreateアクション実行へのリンク
<%= form_with model: [@user, @plant], local: true do |f| %> <p>植物名:<%= f.text_field :plant_name %></p> <%= f.submit "登録" %> <% end %>このform_with記法のリンク先もさきのlink_toと似ている。 今回の場合は [@user, @plant]とすることでネストされたリンクについてcreateアクションを行うことができる。
(この表現の仕方があっているのか…?)おわりに
現状のことがらはひとまず書き終えることができた。まだ、他人のIDのCRUD処理ができてしまうなど、問題は多い。間違いを質しながら個人開発をすすめていこう。
- 投稿日:2020-08-20T03:26:28+09:00
RailsでGraphQL APIからデータを取得・表示する
はじめに
初めまして、hinokageと申します。
Qiitaへの記事投稿初めて且つ、
プログラミングスクールを卒業したての知識で本記事を書きましたので
お手柔らかにお願いいたします。経緯
「Annict」という、有志が開発しているアニメ関連の情報が網羅してあるDBのGraphQLAPIが公開されています。
Railsでアプリ制作をしている際、制作していたアプリ内でこれを利用するためRailsからGraphQLAPIを叩く方法を調べたところ、「Railsで制作したアプリをAPIにする方法としてGraphQLを使用する」という内容の記事は結構な数ヒットするものの、「Railsアプリ内から公開されているGraphQLAPIを叩いてデータ取得する」という内容の記事が全くヒットせず、実装に非常に苦しみました。
私と同じように「有志が制作したGraphQLAPIをRailsで利用したい!」という方に向けて少しでも役に立てるよう本記事を投稿いたします。開発環境
- Ruby(2.6.5)
- Ruby on Rails(5.2.3)
導入
まず最初にGraphQLをrailsで使えるように
Ruby製のGraphQL Client 「graphql-client」を導入します。
(公式リファレンス:https://github.com/github/graphql-client )Gemfilegem 'graphql-client'(追記)gemfileに追記後、bundle install
bundle install次に、使用したいAPIのアクセストークンの作成を行います。
ここでは例としてGithubのGraphQL APIを使用します。GitHub APIのアクセストークンは
GitHubの自分のアイコンをクリック
Settings > Developer settings > Personal access tokens >Generate new token
から作成できます。
(ここでは詳しい作成方法は省略させていただきます。)httpアダプタ、Schema、クライアント設定のためapplication.rbに以下を追記します。
(今回はgql_testというアプリ名で制作しています。)config/application.rbmodule GqlTest(ご自身のアプリ名) ... AUTH_HEADER = "Bearer xxx" # xxx=作成した際に表示されたアクセストークン HTTP = GraphQL::Client::HTTP.new("https://api.github.com/graphql") do #上記には、接続したいAPIのエンドポイントURLを記入 def headers(context) { "Authorization": AUTH_HEADER } end end Schema = GraphQL::Client.load_schema(HTTP) # 上記を使って API サーバーから GraphQL Schema 情報を取得 Client = GraphQL::Client.new(schema: Schema, execute: HTTP) # 上記を使ってクライアントを作成 endこれで大まかな設定は完了です。
クエリ作成
次に取得したいデータのクエリを書きます。
ちなみにクエリとは
データベース管理システムに対する問合せ(処理要求)のこと。簡単に書くと
「欲しいデータを取得するためのコード」
です。GraphQLのクエリの書き方は
https://employment.en-japan.com/engineerhub/entry/2018/12/26/103000
上記のサイトが参考になると思います。もし自分でクエリを書きたい場合はご活用ください。今回はGitHubAPIを使用するのでクエリは以下のようなものを使用しました。
自分のリポジトリの新しいものから5つ分取得するクエリとなっています。query { viewer { repositories(last:5) { edges { node { name } } } } }また、自分でクエリを書きたい場合はAPIに応じたSchemaを知る必要があります。
ちなみにSchemaとは
GraphQL APIの仕様を表現するものです。スキーマ定義言語(SDL(Schema Definition Language))を使って表現します。先程のクエリを例にすると「viewer」「repositories」「node」「name」など全てSchemaです。
API作成時、作成者がデータを取り出す方法をSchemaとして文字に定義しており、
それをAPI使用者がSchemaを用いてデータを取得するために書くコードがクエリだと考えてもらえば
わかりやすいかもしれません。「クエリがどういうものなのか」を分かりやすく伝えるための自分の中で辿り着いた例えが
辞典でいう目次みたいなものがSchemaです。例えば、辞典でとある動物を調べたい、
しかしその動物の〇〇類〇〇科〇〇目などは分かるが名前はわからないみたいな時に
目次のページを見ると思います。この目次のページがSchemaです。
目次を見て〇〇類のページを開きます。
またその辞典には〇〇類の最初のページに「〇〇科一覧」みたいな目次があるかもしれません。
これもSchemaです。つまりはSchemaは
目的のデータに辿り着きたい時の検索手段
みたいなものだと考えれば分かりやすいかもしれません。
その検索手段をAPI作成者側が文字に定義しているのが
schemaだと私は解釈して使用しています。
(間違っていたら申し訳ないので、ぜひ一度GraphQL Schemaとググって調べてください)さて、Schemaの種類や使い方については
各APIの公式リファレンスを読めば書いてあると思いますが
私は「GraphiQL」というアプリケーションを使用して調べることが多いです。
GraphiQLはSchemaの確認や検索ができるほか、
APIへの接続やクエリ、返ってくるデータの確認ができるため非常に便利なツールとなっています。このアプリはブラウザ上で使用することもできますが
オススメはスタンドアローンで使用できるダウンロード版です。
https://electronjs.org/apps/graphiql少し話が逸れてしまいました本題に戻ります。
実装
先ほどのクエリをcontorollerに追加します。
その際、下記のように記述します。controllers/blogs_controller.rbclass BlogsController < ApplicationController Query = GqlTest::Client.parse <<-GRAPHQL query { viewer { repositories(last:5) { edges { node { name } } } } } GRAPHQL def index @works = result end private def result response = GqlTest::Client.query(Query) end endprivateメソッドの下層に
resultというメソッドを用意して
クエリの結果データをrailsで使用できるように加工しています。
そして、使用したいアクション内でresultを用いることで、
結果データを使用することができます。@worksというインスタンス変数にAPIからの結果データを格納したので
これをviewで使用してみます。最新5件のリポジトリ名が表示されればOKです。index.html.erb<h2>私が最近作成したリポジトリは以下の5つです</h2> <%= @works%>あれ?オブジェクトしか表示されない…?
実はこの時点では取り出したいデータを指定していなかったので
オブジェクトが表示されるだけになっています。
これは以下のようにすると表示することができます。controllers/blogs_controller.rbdef index @works = result.data.viewer.repositories.edges end private def result response = GqlTest::Client.query(Query) endこれだけだと、変数内に配列のようにリポジトリ名が複数入っている状態なので、
下記のように表示されてしまいます。
そのため、viewファイルの方でeachメソッドを使い1つずつ表示するようにします。
index.html.erb<% @works.each do |work|%> <p><%= work.node.name%></p> <% end %>…しかしこれだと、追記したコードの意味が分からないですよね。
コードの解説
実は取得したデータだけでは目的の
「自分の最新リポジトリ5件のタイトル」というデータ以外のものも含まれるので、
クエリで書いたように何の情報が欲しいのかを記述する必要があります。つまり、「自分の最新リポジトリ5件のタイトルを取得する」というクエリを書いても、
結果として返ってくるデータは「自分の最新リポジトリ5件のタイトル」だけでは無いということです。GraphiQLを使って、先程と同じクエリを送った時にどのようなデータが返ってくるのか見ると非常にわかりやすいです。
画面左側のコードが先程と同じ「最新リポジトリ5件のタイトルを取得する」クエリです
画面右側はそのクエリをAPIに送った時に返ってきた結果データです。
結果データを見るとクエリで書いたSchemaに値が入って返ってきてるのが分かると思います。
また、コードの書かれ方がハッシュと同じような形でkeyとvalueが含まれているのが分かると思います。この結果データがハッシュであった場合、リポジトリのタイトルであるnameの値を取り出そうとすると
resultという変数に結果データが格納されていた時result[:data][:viewer][:repositories][:edges].each do |work| work[:node][:name] endこのようなコードを書くと思います。
結果データはハッシュでは無いため、このような取り出し方はできないのですが
(ちなみに結果データはJSON形式で返ってきているようです。)
公式リファレンスを読むとハッシュでkeyになっていたものをメソッドのように扱うことで
上記で書いたハッシュの展開と似たような方法で取り出すことができるようです。
つまり、コードはcontrollers/blogs_controller.rbdef index @works = result.data.viewer.repositories.edges end private def result response = GqlTest::Client.query(Query) endindex.html.erb<% @works.each do |work|%> <p><%= work.node.name%></p> <% end %>と書くことで目的のデータが表示できるようになります。
(夜中に殴り書きしたので色々誤字脱字あるかもしれません…)
- 投稿日:2020-08-20T01:48:44+09:00
【Rails 6.0】Action Cable を解説しながらプログレスバーを実装する
TL;DR
- Action Cable を使用してプッシュ通信のプログレスバーを実装した
- Action Cable について解説する
ProgressChannel.broadcast_to(current_user, percent: (index+1) * 100 / pictures.length)背景
旅行の思い出投稿サイトを作ったときに写真アップロードが遅くて、UX 的にアップロードの進捗状況を表すプログレスバーがあったほうが良いのではないかとなり、Action Cable の勉強も兼ねて実装しました
モノ
Action Cable 解説
Rails に Action Cable という WebSocket と Rails のその他の部分をシームレスに統合するための機能が存在します
Rails 公式なので Rails ガイドを見るのが良いと思ったのですが、Rails ガイドの解説がとても分かりづらかったので自分でまとめ直してみました
参考になれば幸いです図が一番わかり易い
Rails ガイドがわかりづらかった理由の一つが図がなかったことだと思ったので図を作りました
どこか間違ってたら教えてください
用語解説
Rails ガイドと順番が違うかもしれませんが、一番わかり易いと思う順番にしました
究極的にはパブリッシャからサブスクライバへ通信が行われる pub/sub リンクです
Rails アプリケーションの controller でブロードキャストを行い、チャネルを通して各タブ(サブスクライバ)に配信しますプログレスバーの実装
プログレスバーを主題に実際の実装を見ていきます
実行環境
- ruby:2.6.5 (Docker image)
- Rails 6.0.2
まずは
rails g channel <チャネル名>でひな形を作成します$ rails g channel progressサーバー側
チャネル
サーバー側のプログレスチャネルです
app/channels/progress_channel.rbclass ProgressChannel < ApplicationCable::Channel def subscribed stream_for current_user end def unsubscribed # Any cleanup needed when channel is unsubscribed end end
subscribedとunsubscribedのひな形は用意されていて、それぞれサブスクリプションができた/なくなったときの挙動を書きます
一般的には、stream_from "progress_#{params[:progress_id]}"のような形で受け取るブロードキャストを指定します
しかし、ここではstream_forを使用し、ユーザーごとに変わるブロードキャストを指定しています
これで、progress:Z2lkOi8vbWVtb3BpYy9Vc2VyLzE(ブロードキャスト名) のようなブロードキャストを受信することができますコネクション
サーバー側ではこのコネクションクラスをもとに、コネクションインスタンスを作成します
app/channels/application_cable/progress_channel.rbmodule ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verified_user User.find(session['warden.user.user.key'][0][0]) rescue reject_unauthorized_connection end def session @session ||= cookies.encrypted[Rails.application.config.session_options[:key]] end end end
identified_byに指定したものを参考に、サーバーはコネクションを識別できるようになります
このコードでは、current_userに入る User オブジェクトをもとに識別しています
アプリケーションで Devise を使用しているので、ここの実装は
https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91
を参考にしましたパブリッシャ
今回は model のところにブロードキャストするコードを書きます
app/models/album_form.rbpictures.each_with_index do |picture, index| album.pictures.new(picture_name: picture) ProgressChannel.broadcast_to( user, percent: (index+1) * 100 / pictures.length ) endここでの
userはcurrent_userに当たります(実装上、変数名が違います)
ここでクライアントにわたす data (percent: (index+1) * 100 / pictures.length) を指定してプログレスバーの進めるわけです
単純に枚数で進捗の%を計算しています
このブロードキャストがチャネルのstream_for current_userにつながり、current_userがいるクライアントにデータが送信されます以上で、サーバー側の実装は終わりです
クライアント側
好きな場所にプログレスバーを置きます
app/views/albums/index.html.erb<div class="progress-percentage text-xs-center">0%</div> <progress class="progress progress-striped" max="100" value="0"></progress>クライアント側でもコネクションを作成しないといけません
app/javascript/channles/consumer.js// Action Cable provides the framework to deal with WebSockets in Rails. // You can generate new channels where WebSocket features live using the `rails generate channel` command. import { createConsumer } from "@rails/actioncable" export default createConsumer()app/javascript/channels/progress_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("ProgressChannel", { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel $('.progress-percentage').text(`${data.percent}%`) $('progress').prop('value', data.percent) } });
consumer.jsでコンシューマを作成し、progress_channel.jsでコネクション(サブスクリプション)を作成しています
今回はProgressChannelにつながるサブスクリプションです
connected()にコネクションができたときの動作、disconnected()にコネクションがなくなったときの動作を記述します
received()にはブロードキャストされたデータを受け取ったときの挙動を書きます
プログレスバーの進捗状況がサブスクライバにブロードキャストされて受け取るので、ここでプログレスバーの各要素を jQuery で書き換えます完成!
まとめ
以上のコードを実装することでユーザーごとにプッシュ通信にするプログレスバーが実装できます
Qiita 初投稿で ActionCable も学びたてなので間違っているところもあるかもしれません、その部分はご指摘・ご教授いただけるとありがたいです
また、記事のこの部分がわかりづらいというコメントもお待ちしています!参考記事
https://railsguides.jp/action_cable_overview.html
https://qiita.com/QUANON/items/0ba35cd0eb5d720f4237
https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91P.S.
『memopic』 という旅行の思い出・アルバム・写真共有サービスを作っています。是非使ってみてください!
ご意見・ご要望などあれば @memopic_bambooまで DM お願いします!
- 投稿日:2020-08-20T01:37:15+09:00
【Rails 6.0】Action Cable を解説しながらプログレスバーを実装する
TL;DR
- Action Cable を使用してプッシュ通信のプログレスバーを実装した
- Action Cable について解説する
ProgressChannel.broadcast_to(current_user, percent: (index+1) * 100 / pictures.length)背景
旅行の思い出投稿サイトを作ったときに写真アップロードが遅くて、UX 的にアップロードの進捗状況を表すプログレスバーがあったほうが良いのではないかとなり、Action Cable の勉強も兼ねて実装しました
モノ
Action Cable 解説
Rails に Action Cable という WebSocket と Rails のその他の部分をシームレスに統合するための機能が存在します
Rails 公式なので Rails ガイドを見るのが良いと思ったのですが、Rails ガイドの解説がとても分かりづらかったので自分でまとめ直してみました
参考になれば幸いです図が一番わかり易い
Rails ガイドがわかりづらかった理由の一つが図がなかったことだと思ったので図を作りました
どこか間違ってたら教えてください
用語解説
Rails ガイドと順番が違うかもしれませんが、一番わかり易いと思う順番にしました
究極的にはパブリッシャからサブスクライバへ通信が行われる pub/sub リンクです
Rails アプリケーションの controller でブロードキャストを行い、チャネルを通して各タブ(サブスクライバ)に配信しますプログレスバーの実装
プログレスバーを主題に実際の実装を見ていきます
実行環境
- ruby:2.6.5 (Docker image)
- Rails 6.0.2
まずは
rails g channel <チャネル名>でひな形を作成します$ rails g channel progressサーバー側
チャネル
サーバー側のプログレスチャネルです
app/channels/progress_channel.rbclass ProgressChannel < ApplicationCable::Channel def subscribed stream_for current_user end def unsubscribed # Any cleanup needed when channel is unsubscribed end end
subscribedとunsubscribedのひな形は用意されていて、それぞれサブスクリプションができた/なくなったときの挙動を書きます
一般的には、stream_from "progress_#{params[:progress_id]}"のような形で受け取るブロードキャストを指定します
しかし、ここではstream_forを使用し、ユーザーごとに変わるブロードキャストを指定しています
これで、progress:Z2lkOi8vbWVtb3BpYy9Vc2VyLzE(ブロードキャスト名) のようなブロードキャストを受信することができますコネクション
サーバー側ではこのコネクションクラスをもとに、コネクションインスタンスを作成します
app/channels/application_cable/progress_channel.rbmodule ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verified_user User.find(session['warden.user.user.key'][0][0]) rescue reject_unauthorized_connection end def session @session ||= cookies.encrypted[Rails.application.config.session_options[:key]] end end end
identified_byに指定したものを参考に、サーバーはコネクションを識別できるようになります
このコードでは、current_userに入る User オブジェクトをもとに識別しています
アプリケーションで Devise を使用しているので、ここの実装は
https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91
を参考にしましたパブリッシャ
今回は model のところにブロードキャストするコードを書きます
app/models/album_form.rbpictures.each_with_index do |picture, index| album.pictures.new(picture_name: picture) ProgressChannel.broadcast_to( user, percent: (index+1) * 100 / pictures.length ) endここでの
userはcurrent_userに当たります(実装上、変数名が違います)
ここでクライアントにわたす data (percent: (index+1) * 100 / pictures.length) を指定してプログレスバーの進めるわけです
単純に枚数で進捗の%を計算しています
このブロードキャストがチャネルのstream_for current_userにつながり、current_userがいるクライアントにデータが送信されます以上で、サーバー側の実装は終わりです
クライアント側
好きな場所にプログレスバーを置きます
app/views/albums/index.html.erb<div class="progress-percentage text-xs-center">0%</div> <progress class="progress progress-striped" max="100" value="0"></progress>クライアント側でもコネクションを作成しないといけません
app/javascript/channles/consumer.js// Action Cable provides the framework to deal with WebSockets in Rails. // You can generate new channels where WebSocket features live using the `rails generate channel` command. import { createConsumer } from "@rails/actioncable" export default createConsumer()app/javascript/channels/progress_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("ProgressChannel", { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel $('.progress-percentage').text(`${data.percent}%`) $('progress').prop('value', data.percent) } });
consumer.jsでコンシューマを作成し、progress_channel.jsでコネクション(サブスクリプション)を作成しています
今回はProgressChannelにつながるサブスクリプションです
connected()にコネクションができたときの動作、disconnected()にコネクションがなくなったときの動作を記述します
received()にはブロードキャストされたデータを受け取ったときの挙動を書きます
プログレスバーの進捗状況がサブスクライバにブロードキャストされて受け取るので、ここでプログレスバーの各要素を jQuery で書き換えます完成!
まとめ
以上のコードを実装することでユーザーごとにプッシュ通信にするプログレスバーが実装できます
学びたてなので間違っているところもあるかもしれません、その部分はご指摘・ご教授いただけるとありがたいです
また、記事のこの部分がわかりづらいというコメントもお待ちしています!参考記事
https://railsguides.jp/action_cable_overview.html
https://qiita.com/QUANON/items/0ba35cd0eb5d720f4237
https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91P.S.
『memopic』 という旅行の思い出・アルバム・写真共有サービスを作っています。是非使ってみてください!
ご意見・ご要望などあれば @memopic_bambooまで DM お願いします!
- 投稿日:2020-08-20T00:56:19+09:00
商品出品機能のコード全文
フリマアプリの商品出品機能
最終課題で実装した商品出品ページの全コードを記録用に書きます!
機能
・ancestry:多階層カテゴリー
・ActiveStorage:画像投稿
・jp_prefecture:都道府県を扱うGem
・active_hash:静的データ作成Active Storageのセットアップ
下記をみて導入方法を確認する
Rails ガイドルーティング
routes.rbRails.application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations', } devise_scope :user do get 'sending_destinations', to: 'users/registrations#newSendingDestination' post 'sending_destinations', to: 'users/registrations#createSendingDestination' end root "items#index" resources :items, only: [:new, :create, :update] do collection do get 'get_category_child', to: 'items#get_category_child', defaults: { format: 'json' } get 'get_category_grandchild', to: 'items#get_category_grandchild', defaults: { format: 'json' } end end endコントローラー
items_controllerclass ItemsController < ApplicationController def index @items_category = Item.where("buyer_id IS NULL AND trading_status = 0 AND category_id < 200").order(created_at: "DESC") @items_brand = Item.where("buyer_id IS NULL AND trading_status = 0 AND brand_id = 1").order(created_at: "DESC") end def new @item = Item.new @item.item_imgs.new @category_parent = Category.where(ancestry: nil) # 親カテゴリーが選択された後に動くアクション def get_category_child @category_child = Category.find("#{params[:parent_id]}").children render json: @category_child #親カテゴリーに紐付く子カテゴリーを取得 end # 子カテゴリーが選択された後に動くアクション def get_category_grandchild @category_grandchild = Category.find("#{params[:child_id]}").children render json: @category_grandchild #子カテゴリーに紐付く孫カテゴリーの配列を取得 end end def create @item = Item.new(item_params) unless @item.valid? @item.item_imgs.new render :new and return end @item.save redirect_to root_path end private def item_params params.require(:item).permit(:name, :introduction, :price, :prefecture_code, :brand_id, :pref_id, :size_id, :item_condition_id, :postage_payer_id, :preparation_day_id, :postage_type_id, :category_id, :trading_status, item_imgs_attributes: [:url, :id]).merge(seller_id: current_user.id) endHaml
sell.html.haml.sell-container = form_for @item do |f| -# 画像部分 .sell-container__content .sell-title %h3.sell-title__text 出品画像 %span.sell-title__require 必須 .sell-container__content__box %ul.output-box %div#image-input = f.fields_for :item_imgs do |image| = image.label :url, id: 'image-input__label' do = image.file_field :url, accept: "image/*", class: "js-file", data: {index: image.index}, style: 'display: none;' %pre %i.fas.fa-camera.fa-lg .error-messages %p =@item.errors.messages[:item_imgs][0] -# 商品概要部分 .sell-container__content .sell-title %h3.sell-title__text 商品名 %span.sell-title__require 必須 = f.text_field :name, {class: 'sell-container__content__name', maxlength: '40', placeholder: '商品名(必須 40文字まで)'} .error-messages %p =@item.errors.messages[:name][0] .sell-title %h3.sell-title__text 商品の説明 %span.sell-title__require 必須 = f.text_area :introduction,{class: 'sell-container__content__description', rows: '7', maxlength: '1000', placeholder: '商品説明'} .sell-container__content__word-count %p.error-messages =@item.errors.messages[:introduction][0] %span#word-count 0/1000 -# 詳細部分 .sell-container__content %h3.sell-sub-head 商品の詳細 .sell-container__content__details .sell-title %h3.sell-title__text カテゴリー %span.sell-title__require 必須 .sell-collection_select.category .select_collection_select-category = f.collection_select :category_id, @category_parent,:id, :name, {prompt: "--"},{class: 'sell-collection_select__label', id:'parent_category'} .error-messages %p =@item.errors.messages[:category_id][0] .sell-title %h3.sell-title__text サイズ %span.sell-title__require 必須 .sell-collection_select = f.collection_select :size_id, Size.all, :id, :value, {prompt: "--"} ,{class: 'sell-collection_select__label'} .error-messages %p =@item.errors.messages[:size_id][0] .sell-title %h3.sell-title__text ブランド %span.sell-title__require.arbitrary 任意 .sell-collection_select = f.collection_select :brand_id, Brand.all, :id, :name, {prompt: "--"} ,{class: 'sell-collection_select__label'} .error-messages .sell-title %h3.sell-title__text 商品の状態 %span.sell-title__require 必須 .sell-collection_select = f.collection_select :item_condition_id, ItemCondition.all, :id, :name, {prompt: "--"}, {class: 'sell-collection_select__label'} .error-messages %p =@item.errors.messages[:item_condition_id][0] -# 配送部分 .sell-container__content %h3.sell-sub-head %p 配送について .sell-container__content__details .sell-title %h3.sell-title__text 配送方法 %span.sell-title__require 必須 .sell-collection_select = f.collection_select :postage_type_id, PostageType.all, :id, :name, {prompt: "--"}, {class: 'sell-collection_select__label'} .error-messages %p =@item.errors.messages[:postage_type_id][0] .sell-title %h3.sell-title__text 配送料の負担 %span.sell-title__require 必須 .sell-collection_select = f.collection_select :postage_payer_id, PostagePayer.all.map, :id, :name, {prompt: "--"}, {class: 'sell-collection_select__label'} .error-messages %p =@item.errors.messages[:postage_payer_id][0] .sell-title %h3.sell-title__text 発送元の地域 %span.sell-title__require 必須 .sell-collection_select = f.collection_select :prefecture_code, JpPrefecture::Prefecture.all, :code, :name, {include_blank: '--'}, class: 'sell-collection_select__label' .error-messages %p =@item.errors.messages[:prefecture_code][0] .sell-title %h3.sell-title__text 発送までの日数 %span.sell-title__require 必須 .sell-collection_select = f.collection_select :preparation_day_id, PreparationDay.all, :id, :value, {prompt: "--"}, {class: 'sell-collection_select__label'} .error-messages %p =@item.errors.messages[:preparation_day_id][0] -# 価格部分 .sell-container__content %h3.sell-sub-head %p 販売価格(300〜9,999,999) .sell-container__content__price .sell-title %h3.sell-title__text 販売価格 %span.sell-title__require 必須 .sell-container__content__price__form = f.label :price, class: 'sell-container__content__price__form__label' do ¥ = f.number_field :price, {placeholder: '0', value: '', autocomplete:"off", class: 'sell-container__content__price__form__box'} .error-messages#error-price =@item.errors.messages[:price][0] .sell-container__content__commission .sell-container__content__commission__left 販売手数料 (10%) .sell-container__content__commission__right ー .sell-container__content__commission .sell-container__content__commission__left 販売利益 .sell-container__content__commission__right ー .submit-btn = f.submit '出品する', class: 'submit-btn--Btn' .submit-btn = link_to 'もどる', root_path, class: 'submit-btn--Btn return' .attention-box %p 禁止されている行為および出品物を必ずご確認ください。偽ブランド品や盗品物 などの販売は犯罪であり、法律により処罰される可能性があります。また、出品をもちまして加盟店規約に同意したことになります。必須項目が見入力の場合、エラーメッセージが出るように設定しています。
また、住所はjp_prefectureという都道府県を扱うGemを使用しました!*ポイント*
ボックスはf.collectionに統一して記述する。
f.selectを使用するとvalueが文字でDBに送られる為、idが0で登録される為、後々の商品詳細ページでデータを表示させる時にエラーが出ます。JS
items.js$(document).on('turbolinks:load', ()=> { // 画像用のinputを生成する関数 const buildFileField = (index)=> { const html = `<input accept="image/*" class="js-file" data-index="${index}" style="display: none;", type="file" name="item[item_imgs_attributes][${index}][url]" id="item_item_imgs_attributes_${index}_url">`; return html; } // プレビュー用のimgタグを生成する関数 const buildImg = (index, url)=> { const html = `<img data-index="${index}" src="${url}" width="100px" height="100px">`; return html; } // file_fieldのnameに動的なindexをつける為の配列 let fileIndex = [1,2,3,4,5,6,7,8,9,10]; $('#image-input').on('change', '.js-file', function(e) { // labelタグのfor属性を変更 $('#image-input__label').attr('for', 'item_item_imgs_attributes_' + fileIndex[0] + '_url'); // fileIndexの先頭の数字を使ってinputを作る $('#image-input').append(buildFileField(fileIndex[0])); fileIndex.shift(); // 末尾の数に1足した数を追加する fileIndex.push(fileIndex[fileIndex.length - 1] + 1) const targetIndex = $(this).parent().data('index'); // ファイルのブラウザ上でのURLを取得する const file = e.target.files[0]; const blobUrl = window.URL.createObjectURL(file); $('#image-input').before(buildImg(targetIndex, blobUrl)); }); });JSでは大きく分けて2つ。
①画像のプレビュー
②Inputタグの生成get_category_child.json.jbuilderjson.array! @category_child do |child| json.id child.id json.name child.name endget_category_grandchild.json.jbuilderjson.array! @category_grandchild do |grandchild| json.id grandchild.id json.name grandchild.name endモデルは割愛します。
今回は以上です。
間違いなどあればご指摘いただけますと幸いです^^
- 投稿日:2020-08-20T00:07:24+09:00
rails csv ファイル読み込み トライ
【Ruby on Rails】CSVインポート
Rails上で、CSVファイルを読み込んでみた
【Rails】CSVファイルからデータをインポートこれを参考にトライ
結果その通りにやったらできました。
(投稿者様、ありがとうございます)だが、rubyの文法でつまづく。
だれかわかる方いらっしゃたら助けていただけるとありがたいです。app/controllers/users_controller.rbdef index @users = User.all end def import # fileはtmpに自動で一時保存される User.import(params[:file]) redirect_to users_url endこのimportコントローラーUser.importのimportメソッドは、user Modelを操作するために、下記で、クラスメソッドとして定義されている、importメソッドであることはわかった。
app/model/user.rbclass User < ApplicationRecord def self.import(file) CSV.foreach(file.path, headers: true) do |row| # IDが見つかれば、レコードを呼び出し、見つかれなければ、新しく作成 user = find_by(id: row["id"]) || new # CSVからデータを取得し、設定する user.attributes = row.to_hash.slice(*updatable_attributes) # 保存する user.save end end # 更新を許可するカラムを定義 def self.updatable_attributes ["id", "name", "age"] end endまず、
user = find_by(id: row["id"]) || new
でつまづいた。
調べると、
https://www.sejuku.net/blog/19044
nil判定を行っていることは理解
find_by(id: row["id"])がnilなら newをuserに代入している。でも "new"とはなんだろう、インスタンスか?
公式document
https://docs.ruby-lang.org/ja/latest/class/CSV=3a=3aRow.html
をみると、
CSV::Rowクラスの、newメソッドなのではないかと予想
であれば、
row.newを省略して書いている??もう一個
row.to_hash.slice(*updatable_attributes)
がわからない。rowは、csvの特定の行
それをto_hashでハッシュにしている
sliceで ["id", "name", "age"]のみ取扱たいが、
to_hashメソッドではを使うと、["id", "name", "age"]がハッシュのキー(あるいは値)として使われるのだろうか?
https://docs.ruby-lang.org/ja/latest/class/CSV=3a=3aRow.htm謎は深まる。
あと、*updatable_attributes
引数にアスタリスク付きのメソッドを指定している記法がわからない。
- 投稿日:2020-08-20T00:05:09+09:00
AWSEC2でただrails sをするとこうなってしまう!解決策は・・・
ターミナル[ec2-user@ip-172-31-38-149 fleamarket_sample_80d]$ rails s Usage: rails new APP_PATH [options] Options: [--skip-namespace], [--no-skip-namespace] # Skip namespace (affects only isolated applications) -r, [--ruby=PATH] # Path to the Ruby binary of your choice # Default: /home/ec2-user/.rbenv/versions/2.6.5/bin/ruby -m, [--template=TEMPLATE] # Path to some application template (can be a filesystem path or URL) -d, [--database=DATABASE] # Preconfigure for selected database (options: mysql/postgresql/sqlite3/oracle/frontbase/ibm_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc) # Default: sqlite3 [--skip-gemfile], [--no-skip-gemfile] # Don't create a Gemfile -G, [--skip-git], [--no-skip-git] # Skip .gitignore file [--skip-keeps], [--no-skip-keeps] # Skip source control .keep files -M, [--skip-action-mailer], [--no-skip-action-mailer] # Skip Action Mailer files [--skip-action-mailbox], [--no-skip-action-mailbox] # Skip Action Mailbox gem [--skip-action-text], [--no-skip-action-text] # Skip Action Text gem -O, [--skip-active-record], [--no-skip-active-record] # Skip Active Record files [--skip-active-storage], [--no-skip-active-storage] # Skip Active Storage files -P, [--skip-puma], [--no-skip-puma] # Skip Puma related files -C, [--skip-action-cable], [--no-skip-action-cable] # Skip Action Cable files -S, [--skip-sprockets], [--no-skip-sprockets] # Skip Sprockets files [--skip-spring], [--no-skip-spring] # Don't install Spring application preloader [--skip-listen], [--no-skip-listen] # Don't generate configuration that depends on the listen gem -J, [--skip-javascript], [--no-skip-javascript] # Skip JavaScript files [--skip-turbolinks], [--no-skip-turbolinks] # Skip turbolinks gem -T, [--skip-test], [--no-skip-test] # Skip test files [--skip-system-test], [--no-skip-system-test] # Skip system test files [--skip-bootsnap], [--no-skip-bootsnap] # Skip bootsnap gem [--dev], [--no-dev] # Setup the application with Gemfile pointing to your Rails checkout [--edge], [--no-edge] # Setup the application with Gemfile pointing to Rails repository [--rc=RC] # Path to file containing extra configuration options for rails command [--no-rc], [--no-no-rc] # Skip loading of extra configuration options from .railsrc file [--api], [--no-api] # Preconfigure smaller stack for API only apps -B, [--skip-bundle], [--no-skip-bundle] # Don't run bundle install --webpacker, [--webpack=WEBPACK] # Preconfigure Webpack with a particular framework (options: react, vue, angular, elm, stimulus) [--skip-webpack-install], [--no-skip-webpack-install] # Don't run Webpack installこのような表記になってしまいますが解決策は
rails s RAILS_ENV=production です。以上。






















