- 投稿日:2019-08-16T22:05:30+09:00
rails_helper.rb内でrake taskを実行する方法
環境
- Rails 5.2.3
やり方
- その一: 特定のファイルを読み込んで、それを実行する。
spec/rails_helper.rbrequire 'rake' load Rails.root.join('lib', 'tasks', 'foo.rake') # 実行したいrakeファイル Rake::Task.define_task(:environment) Rake::Task['foo:bar:buz'].invoke
- その二: 全てのrakeタスクを読み込んで、特定のタスクを実行する。
spec/rails_helper.rbrequire 'rake' Rails.application.load_tasks Rake::Task['foo:bar:buz'].invoke
- 投稿日:2019-08-16T20:19:32+09:00
現役ポールダンサー初めてのwebサービスをRuby on railsで作りました
qiita初投稿です!
私の本業はポールダンス、エアルリアルダンスをメインとするパフォーマンス業です。(時々インストラクター的なこともやってます)
そんな私がこの度Rubyonrailsでwebサービスを初めて作って公開しました。
なぜダンサーがプログラミングを?わざわざ、、、?などなどはこちらのブログに書いてますが、最初は単純にキャリアチェンジを考えていたところからプログラミング学習は始まりました。そこからやはり自分でサービスを作ってみよう!ということになって作ったサービスがこちらです。
https://poletricks-world.herokuapp.com/
デザインや機能共にまだまだ修正すべきところはありますし、目新しい機能というものはありませんが
機能、デザイン、エラーで困ったところなどを綴ります。
アイディア
練習するときに見ることができるアプリがあればいいな(インスタだと、検索が面倒、技で検索できない、達成度が感じれられない、動画止められない)
・動画が止められて
・達成感が味わえて
・わかりやすい動画
・且つ無料
私が練習を始めた頃に欲しいなと思った機能です。これらを網羅したアプリを作りたかったのです。
プロトタイプを作ることを始めました。開発環境
Rails 5.2
ruby 2.4.0
cloud9(途中からAWSのcloud9に移行)プロトタイプを作る
プロトタイプは結構大まかでもいいのかなーと思っていましたが、私のメンターさんはできるだけ細かくデザインして、その機能をあとは乗せていくだけの方がいいよ、とのことで
イラレを使って作成していきました。
ちなみにイラストレーターを使ったのは今年のお正月が初めてです。ここは独学でアドビのイラストレーター初心者の動画をひたすら見ながら練習したり、ググったりして覚えました。すごいたくさんのことは全然できませんが
プロトタイプを作るくらいまでならサクサクできるように1ヶ月ほどでなりました。大まかなページ展開
・トップページ
・サインイン、ログインページ
・動画一覧ページ
・動画詳細ページ
・ブログページ
サインイン、のところはgemのdeviseを使いました。
動画は最初、AWSを使う予定でしたが、こんがらがるのでまずはYoutubeにアップロードして動画リンクを入れていくことにしました。動画一覧にはyoutubeのサムネイル表示がされるようにしています。Topページ
ナビゲーションバーのなかにリンク(ブログとコンタクト)、ログインログアウト、サインアップページ
ヘッダー画像とアカウント作成ボタン
動画例
使い方
アバウト
プロフィール
フッター(リンク、SNS)という構成です。動画一覧ページ
ユーザーができた技数が表示される(マスターできた数/存在する技数 の計算式)
動画リンク(サムネイル+マスターボタン+カテゴリーラベル+動画リンク)
ここの動画は全てadminの私がnewで追加していくように実装しました。(newにアクセスすれば動画を自分で足すことも可能だけど今の所私のみ、動画をアップできるようにする)
ブックマークの色が変わるところはAjax
検索機能はgemのransackを使いました。動画詳細ページ
動画のyoutube
コメント欄(削除可能)←技のポイントなどを書き込めたら面白いなあと思い。
関連動画表示(ここはマスターボタンなどは乗せないで、カテゴリーで紐づかせて表示されるようにしました)
動画自体はループで表示されるようにした方がいいか、迷いどころなのでテストユーザーのフィードバックで都度変更していこうと思います。Bookmarkページ
ここはかなり時間がかかりました。動画一覧ページでブックマークしたものをユーザーごとのマイページとしてブックマークされるようにしました。masterボタンをキャンセルすると動画一覧ページも色がかわります。
同じくajaxを使い、masterした技のボタンがmaster→cancelと変更される様にしました。主につまづいたところ
つまづいたことが多すぎてもうわからないくらいですが
。デザインがうまくいかない(現時点でもまだ修正中!!)
Bootstrapを使ってますが、使ったら使ったで自由度がない部分もあり、折り合いが難しいです。
ヘッダーの画像は最初イラレのみで私の写真を切りはりしたのですが、汚く見えてしまったので
Photosopで加工→イラレでサイズ、バックの修正などを行いました。多分もっといい方法はあると思いますが現時点ではこれが一番でした。herokuデプロイできない問題
テックアカデミーをやっていた時に使ったことがあるので余裕や!と思っていたけれど、
ふつーにエラーでなかなかデプロイできませんでした。
そもそもですが、別のブランチをmasterブランチにマージしていなかったため、まずはそこがエラーが出ていたようです。
とりあえずメンターに相談したらherokuの設定をする順番が間違っていたみたい。Herokuでよくエラーが出た時の解決方法
何度かテストユーザーさんに使ってもらう上で、cloud9上で問題なく表示され、動作できたのでherokuにアップするとよく
We're sorry, but something went wrong.
表示が出ていました。
大体が以下のことで解決です。
・git commit してないのでする
・heroku db:migrationする
・heroku restartする
でほぼほぼ解決しました、一回表示するべきコードが消えていた時があって、その時はコードを付け足し直してエラーは解決しました。AWSのcloud9
途中から以前のcloud9からAWSのcloud9に移行しました。
移行自体はスムーズに行ったのですが、、、
ある日急に容量がいっぱいであるというエラーが出て、ターミナルが使えなくなりました。
githubにコードはギリギリまであげていたので、再度git cloneして新たなワークスペースで作成。
そしてなんとかまたできるようになったもののまたある程度コードを書くと警告が出て、
またもやターミナルが使えなくなり、サポートに問い合わせるも具体的なことはわかりかねるよでした。
次にAWSでもう一度ワークスペースを作る際、変更したのはEC2インスタンスです。(ここがキモでした)今までmicroを使っていたのですがこれをsmallに変更
サーバーはubuntuです。
これで無事AWSに新たなワークスペースが作ることができました。
そのあと、AWSのアクセスキー、シークレットキーを登録しエラーと格闘しながら無事にまたheroku上で公開する事ができました。テストユーザーに利用してもらう
わたしの周りはインストラクターやショーにすでに出ている人たちだったのでポールダンスを習ってそこまで間もない生徒さんが必要でした。
友人の経営しているスタジオの生徒さんに協力してもらいフィードバックをいただくことになりました。
特に自分で作ってると裏側がわかるので導線などは、当たり前にここからログインするよね?
探しにいくよね?という前提で作っていたので、
動画ページに移動の方法に迷った、削除ボタンの位置がわかりにくい、戻り方がわからない、、
などなど導線をわかりやすくする工夫が必要になりました。
あとは重要なチェックボタンを押したあと、取り消しボタンがないこと、取り消しボタンがないため、同じボタンを押すと、Master数がどんどん同じ技で計上されてしまうようになっていたのでそこを修正する必要がありました。まとめ
細かいエラーなどはまた別途ブログで書いていこうと思いますが
6か月弱かけてなんとかここまで作る事ができました。
プロの方からするとはっきり言ってかなり単純なwebサービスとなっているように思われるかもしれませんが、個人的にはかなり頑張りました。自分で初めてプロダクトを作って見て、何度も投げ出しそうになったけど(というかまだデザイン終わってない)形にできて本当によかったし、もっと色々手を出して見たくなりました。
引き続き改良を試みつつ、技術のアップデートしたいです!
https://poletricks-world.herokuapp.com/
※2019年8月現在、エラー復旧中にAWSのcloud9のターミナルが使えなくなり、諸々修正が不可になりgit cloneするもバージョンコンフリクトなどで解消せず、、ローカルでぽちぽち作り直してます。一難去ってまた一難。。とりあえずこのurlとは別で新たにappを作っていこうと思います。
- 投稿日:2019-08-16T17:27:59+09:00
Rails ActiveRecordの結合時のクエリの内容について
参考
以下のページを参考にさせていただきました。ありがとうございます。
ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い
【Rails】テーブル結合データ構造
companiesテーブル
id name 1 A社 2 B社 servicesテーブル
id company_id name 1 1 Aアプリ1 2 1 Aアプリ2 3 1 Aアプリ3 4 2 Bの野望 パターン1 all(pluckなし)
companies = Company.all companies.each do |company| company.services endSELECT `companies`.* FROM `companies` SELECT `services`.* FROM `services` WHERE `services`.`company_id` IN (1,2)パターン2 : all
companies = Company.all companies.each do |company| model.services.pluck(:name).join(',') endSELECT `companies`.* FROM `companies` SELECT `services`.`name` FROM `services` WHERE `services`.`company_id` = 1 SELECT `services`.`name` FROM `services` WHERE `services`.`company_id` = 2パターン3 : join
companies = Company.joins(:services) companies.each do |company| model.services.pluck(:name).join(',') endSELECT `companies`.* FROM `companies` INNER JOIN `services` ON `services`.`company_id` = `companies`.`id` SELECT `services`.`name` FROM `services` WHERE `services`.`company_id` = 1 SELECT `services`.`name` FROM `services` WHERE `services`.`company_id` = 1 SELECT `services`.`name` FROM `services` WHERE `services`.`company_id` = 1 SELECT `services`.`name` FROM `services` WHERE `services`.`company_id` = 2パターン4 : preload
companies = Company.preload(:services) companies.each do |company| model.services.pluck(:name).join(',') endSELECT `companies`.* FROM `companies` SELECT `services`.* FROM `services` WHERE `services`.`company_id` IN (1,2)パターン5 : includes
companies = Company.includes(:services) companies.each do |company| model.services.pluck(:name).join(',') endSELECT `companies`.* FROM `companies` SELECT `services`.* FROM `services` WHERE `services`.`company_id` IN (1,2)パターン6 : eager_load
companies = Company.includes(:services) companies.each do |company| model.services.pluck(:name).join(',') endSELECT **省略** FROM `companies` LEFT OUTER JOIN `services` ON `services`.`company_id` = `companies`.`id`
- 投稿日:2019-08-16T17:27:44+09:00
Ruby on Railsでプロジェクト管理WEBアプリを開発
はじめに
UdemyでRuby on Railsのコースを学習したので、そこで学んだ知識を生かしてWEBアプリを開発しました。
ソースコード
GitHubで公開しています
https://github.com/Ryota7101/milestone.git主な機能
- miliaによるマルチテナント機能
- タスク機能
- タスクへのカテゴリ付け
- ファイルアップロード機能
- プレミアム会員機能
- 管理者機能
アプリ詳細
サインアップ
サインアップ時にフリープランかプレミアムプランかを選択できます。
フリープランの場合はカード情報入力欄が表示されません。
プレミアムプランに切り替えると、クレジットカード情報入力欄が表示されます。
※情報を登録して処理する機能を設定してないので、ここで入力した情報は保存されません。
プロジェクト作成
ファイルアップロード機能
テキストファイルや画像をアップできます。
※ただし、クラウドなど保存先を設定していないので、現状ではどこにもファイルは保存されません。
タスク
管理者機能
Organization作成者は自動的に管理者として登録されます。
管理者がログインしてる場合は、ナビゲーションバーに
* メンバー追加
* プラン変更
* タスクのカテゴリ管理
のボタンが表示されます。タスクのカテゴリ
タスクのカテゴリ管理(作成や削除)は管理者のみが行えます。
タスクを作成したり、タスクにカテゴリを設定するのは非管理者でも行えます。カテゴリを作成すると、カテゴリ一覧ページに表示されます。
使用する場合は、タスク作成時にチェックを入れます。
プラン変更
メンバーを招待する
管理者はメンバーをプロジェクトに招待できます。
招待したい人の氏名やメールアドレスを入力すると、リンク付きのメールが相手に送信されます。
届いたメールのリンクをクリックすると、パスワード設定画面に移動するので、パスワードを入力します
管理者がプロジェクトページを見ると、招待した相手の情報が表示されるので、Addボタンを押すと招待が完了します。
招待が完了すると、メンバー欄にメンバーの情報が表示されます。
招待された相手がログインすると、管理者が作成したプロジェクトやファイル、タスクなどが表示されます。
※招待されたメンバーは管理者でないので、ナビゲーションバーにカテゴリ管理などのボタンは表示されません。
別の組織を作成
試しに別の組織でサインアップして、これまで作成した情報が表示されないことを確認します。
これまでに作成した情報が表示されず、初期画面となっているため、組織ごとにデータが分かれてることがわかります。
終わりに
Udemyのコースの内容が2015年ごろのもので、その通りにコードを描いても動かないことが多く、苦戦しました。(コースではRails4、著者は5で作成)
相変わらずコード書いて実際にweb上で動かして確認して・・とやってしまい、テストはおざなりになってしまった。。
実戦でRails使いたいです
- 投稿日:2019-08-16T17:27:44+09:00
Ruby pn Railsでプロジェクト管理WEBアプリを開発
はじめに
UdemyでRuby on Railsのコースを学習したので、そこで学んだ知識を生かしてWEBアプリを開発しました。
ソースコード
GitHubで公開しています
https://github.com/Ryota7101/milestone.git主な機能
- miliaによるマルチテナント機能
- タスク機能
- タスクへのカテゴリ付け
- ファイルアップロード機能
- プレミアム会員機能
- 管理者機能
アプリ詳細
サインアップ
サインアップ時にフリープランかプレミアムプランかを選択できます。
フリープランの場合はカード情報入力欄が表示されません。
プレミアムプランに切り替えると、クレジットカード情報入力欄が表示されます。
※情報を登録して処理する機能を設定してないので、ここで入力した情報は保存されません。
プロジェクト作成
ファイルアップロード機能
テキストファイルや画像をアップできます。
※ただし、クラウドなど保存先を設定していないので、現状ではどこにもファイルは保存されません。
タスク
管理者機能
Organization作成者は自動的に管理者として登録されます。
管理者がログインしてる場合は、ナビゲーションバーに
* メンバー追加
* プラン変更
* タスクのカテゴリ管理
のボタンが表示されます。タスクのカテゴリ
タスクのカテゴリ管理(作成や削除)は管理者のみが行えます。
タスクを作成したり、タスクにカテゴリを設定するのは非管理者でも行えます。カテゴリを作成すると、カテゴリ一覧ページに表示されます。
使用する場合は、タスク作成時にチェックを入れます。
プラン変更
メンバーを招待する
管理者はメンバーをプロジェクトに招待できます。
招待したい人の氏名やメールアドレスを入力すると、リンク付きのメールが相手に送信されます。
届いたメールのリンクをクリックすると、パスワード設定画面に移動するので、パスワードを入力します
管理者がプロジェクトページを見ると、招待した相手の情報が表示されるので、Addボタンを押すと招待が完了します。
招待が完了すると、メンバー欄にメンバーの情報が表示されます。
招待された相手がログインすると、管理者が作成したプロジェクトやファイル、タスクなどが表示されます。
※招待されたメンバーは管理者でないので、ナビゲーションバーにカテゴリ管理などのボタンは表示されません。
別の組織を作成
試しに別の組織でサインアップして、これまで作成した情報が表示されないことを確認します。
これまでに作成した情報が表示されず、初期画面となっているため、組織ごとにデータが分かれてることがわかります。
終わりに
Udemyのコースの内容が2015年ごろのもので、その通りにコードを描いても動かないことが多く、苦戦しました。(コースではRails4、著者は5で作成)
相変わらずコード書いて実際にweb上で動かして確認して・・とやってしまい、テストはおざなりになってしまった。。
実戦でRails使いたいです
- 投稿日:2019-08-16T16:11:03+09:00
【AWS】開発環境では動くが本番だと�動かない事例集
Qiita初投稿になります。
何か不備や誤りがあったらご指摘をいただけるととてもありがたいです!経緯
スクールでの課題や個人アプリの開発でAWSを使っており、デプロイも双方担当をさせていただいておりました。
その中で、苦戦をした大きな比率を占めるのが、開発環境だと動くのに何故か本番環境だと動かない事例です。
本番で動かないと実際のサービスだと何も意味がないにも関わらず、このパターンが結構ありましたので、これから対処をする方がどういう方法や考え方で解いていけばいいのかをまとめます。
デプロイで苦戦をすると本当にイライラしてしまうので、イライラを少しでも沈めるのに貢献をできたらなと思います。開発環境(個人アプリもスクールの課題も同一です)
言語:Ruby 2.5.1
フレームワーク:Rails 5.2.3
データベース:MySQL5.6
ライブラリ:JQuery
自動デプロイ:capistrano
Webサーバー(本番):nginx
APサーバー(本番):unicorn
Web・APサーバー(開発):puma
今回は双方ともにEC2、S3を用いております。前提とエラーの基本の見方
EC2インスタンスの再起動が1番今まで救われたケースが多いです。まずは試してみましょう。(MySQLとnginxの再起動も必要です)
そして、エラーを解決をする基本はエラーログを見て忠実に動くことです。これは本番環境でも開発環境でも一緒です。
エラーが出る場合、開発環境だとターミナルやビューに出てきます。ただ、本番環境だと出てこないため、本番環境だと別でコマンドで見ていきます。
エラーログの本番環境の場所はlinuxコマンドでログインをして確認をします。注意をするのは自動デプロイの前後でエラーが吐き出される場所が異なることです。自動デプロイをしたにも関わらず元のままの位置でファイルを開いても意味がないです。#手動デプロイ後 /var/www/アプリ名/log/production.log #capistranoでの自動デプロイ後 /var/www/アプリ名/current/log/production.loglogにまで移動をしたらcat等のコマンドでエラーログを見ることができます。
①pumaとの性能の違いで、拡張子がついていなかった時
ActionView::Template::Error (The asset "bannar_image" is not present in the asset pipeline.):
※本番環境のエラーログです。
これが一番はじめのエラーでした。こちらに関しては上記のエラー文を見ると、bannar_imageが間違っているのが何となく分かると思います。
このbannar_imageという記述があったのが、image_tagになるので、image_tagに関して調べてみました。index.erb#エラーが起きたコードのイメージ <%= image_tag 'bannar_image' %> #他のサイトで見た見本のコード <%= image_tag 'flower.png' %>実際にビューファイルを比べてみると何か違和感が、、、上のコードは実は拡張子がなかったのです。
そのため、本番環境では動かなかったのです。ちなみにこの時はそれまで自動デプロイが初めてできた瞬間に出たエラーだったため、自動デプロイのcapistranoのエラーだと勘違いをしてしまっておりました。
自動デプロイのエラーばかり見ていたので、何もエラーログがないのに原因不明のエラーが起こっていると勘違いして、だいぶハマりました。
デプロイがどういう流れになっていてどういう仕組みになっているのかを理解をしていないとこういうところで非常に苦戦をします。というかpumaは何故拡張子がないのに動くのか、、、
②application.jsにJQueryが2つあった時
こちらはそこまで難しくないエラーです。お互いに干渉してしまうため、エラーになるようです。
今出せないのですが、エラーログも出ていて、そんなに複雑なエラーログではありませんでした。application.js=require rails-ujs =require jquery-ujs下のコードはヴァージョンが古い時に使っていたようでして、Rails5.1以降は上のコードを使うみたいです。
これはスクールの発表で先輩グループの発表の際に触れていたので、すぐに分かりました。
①もだけどそもそもpumaの時点でエラー起きてくれないのかな。。。③データベース関連で開発環境とmigrationの状況が異なってしまった時
ActionView::Template::Error (Unknown database 'freemarket_sample_54c_production'):
チーム開発でmearge済みのmigrationファイルを変更をしてその後に再度meargeをしたり、
rake db:reset等を繰り返しているといつか起きてしまうかと思います。
私たちのチームではどうしようもなくなってしまい、結局一から本番環境のデータベースを構築をし直したのですが、これは本来あまりやっていいことではないかと思っています。
もちろん開発の初期段階ですので正直困りはしなかったのですが、実際のサービスだと既に重要な顧客データが多数入っているためです。
こちらは他に何かやり方あれば教えていただけると非常に嬉しいです。ちなみにcapistrano導入後はデータベースをcreateしたり、dropをさせるときもエラーを見るのと同じくcurrent上でコマンドは打ちます。
rake:db:seedも同様で、capistrano導入後はディレクトリの位置が変更をされるためです。④credentials.yml関連
ActiveSupport::MessageEncryptor::InvalidMessage
このエラーは序盤結構多かったです。こちらに関しては他に詳しい記事も多数あるかと思いますので割愛します。
credentials.ymlに関しては最初は結構苦戦をしましたが、慣れるとAPIkeyの管理がすごく簡単でした。
怖がるものではないかと思います。⑤データベース内に数値が入っていなかった時
ActionView::Template::Error (undefined method `id' for nil:NilClass):
すごく単純なようで起きがちなエラーです。
特にテーブルを増やしてカラムに新しく外部キーを増やすと起こりがちです。
私はデータベースをSQLで直接いじっていましたが、Sequelpro等で本番でも見れるようにすることができるので、入れたら楽に設定をできると思います。
※ベーシック認証をしてからだと思うのですが、途中からSequelproは使えなくなりました。おそらく弾かれてしまったのだと思います。もしかするとベーシック認証を使っていてもできるのかもしれませんが、私は調べきれませんでした。⑥JQueryで発火をしなかった※データが入っていない時
エラーログは出ません。
インクリメンタルサーチをしようとしたのですが、本番環境のデータで名前を検索をする際に、そもそも該当の名前のデータが入っていませんでした。。。これは完全に単なる勘違いです。即解決できましたが、全くエラーログが出てこないので原因が不明になるので少しの時間、不安になりました。
これも上記の⑤と一緒でSequelproとか入れたらわかりやすそうです。まとめ
基本はエラーログをしっかりと見ていけば何とかなりました。ただ、データベース関連は視覚化ができていなかったので、苦労をしました。
これに関してはエラーログの構造等を理解しだしてから、格段に理解が深まったので、細かいWebアプリの仕組みに関しては理解をしといたほうがいいなと感じました。
今回挙げた事例に関しては正直かなり簡単なほうだと思っています。これから先も出てくる事例は多いと思うので、また何かあれば記事に挙げていきます。参考記事
https://www.javadrive.jp/rails/template/index11.html
https://www.bokukoko.info/entry/2017/10/27/231129
https://qiita.com/scivola/items/cc06ddbfd94d3118f693
- 投稿日:2019-08-16T16:11:03+09:00
【AWS】開発環境では動くが本番だと動かない事例集
Qiita初投稿になります。
何か不備や誤りがあったらご指摘をいただけるととてもありがたいです!経緯
スクールでの課題や個人アプリの開発でAWSを使っており、デプロイも双方担当をさせていただいておりました。
その中で、苦戦をした大きな比率を占めるのが、開発環境だと動くのに何故か本番環境だと動かない事例です。
本番で動かないと実際のサービスだと何も意味がないにも関わらず、このパターンが結構ありましたので、これから対処をする方がどういう方法や考え方で解いていけばいいのかをまとめます。
デプロイで苦戦をすると本当にイライラしてしまうので、イライラを少しでも沈めるのに貢献をできたらなと思います。開発環境(個人アプリもスクールの課題も同一です)
言語:Ruby 2.5.1
フレームワーク:Rails 5.2.3
データベース:MySQL5.6
ライブラリ:JQuery
自動デプロイ:capistrano
Webサーバー(本番):nginx
APサーバー(本番):unicorn
Web・APサーバー(開発):puma
今回は双方ともにEC2、S3を用いております。前提とエラーの基本の見方
EC2インスタンスの再起動が1番今まで救われたケースが多いです。まずは試してみましょう。(MySQLとnginxの再起動も必要です)
そして、エラーを解決をする基本はエラーログを見て忠実に動くことです。これは本番環境でも開発環境でも一緒です。
エラーが出る場合、開発環境だとターミナルやビューに出てきます。ただ、本番環境だと出てこないため、本番環境だと別でコマンドで見ていきます。
エラーログの本番環境の場所はlinuxコマンドでログインをして確認をします。注意をするのは自動デプロイの前後でエラーが吐き出される場所が異なることです。自動デプロイをしたにも関わらず元のままの位置でファイルを開いても意味がないです。#手動デプロイ後 /var/www/アプリ名/log/production.log #capistranoでの自動デプロイ後 /var/www/アプリ名/current/log/production.loglogにまで移動をしたらcat等のコマンドでエラーログを見ることができます。
①pumaとの性能の違いで、拡張子がついていなかった時
ActionView::Template::Error (The asset "bannar_image" is not present in the asset pipeline.):
※本番環境のエラーログです。
これが一番はじめのエラーでした。こちらに関しては上記のエラー文を見ると、bannar_imageが間違っているのが何となく分かると思います。
このbannar_imageという記述があったのが、image_tagになるので、image_tagに関して調べてみました。index.erb#エラーが起きたコードのイメージ <%= image_tag 'bannar_image' %> #他のサイトで見た見本のコード <%= image_tag 'flower.png' %>実際にビューファイルを比べてみると何か違和感が、、、上のコードは実は拡張子がなかったのです。
そのため、本番環境では動かなかったのです。ちなみにこの時はそれまで自動デプロイが初めてできた瞬間に出たエラーだったため、自動デプロイのcapistranoのエラーだと勘違いをしてしまっておりました。
自動デプロイのエラーばかり見ていたので、何もエラーログがないのに原因不明のエラーが起こっていると勘違いして、だいぶハマりました。
デプロイがどういう流れになっていてどういう仕組みになっているのかを理解をしていないとこういうところで非常に苦戦をします。というかpumaは何故拡張子がないのに動くのか、、、
②application.jsにJQueryが2つあった時
こちらはそこまで難しくないエラーです。お互いに干渉してしまうため、エラーになるようです。
今出せないのですが、エラーログも出ていて、そんなに複雑なエラーログではありませんでした。application.js=require rails-ujs =require jquery-ujs下のコードはヴァージョンが古い時に使っていたようでして、Rails5.1以降は上のコードを使うみたいです。
これはスクールの発表で先輩グループの発表の際に触れていたので、すぐに分かりました。
①もだけどそもそもpumaの時点でエラー起きてくれないのかな。。。③データベース関連で開発環境とmigrationの状況が異なってしまった時
ActionView::Template::Error (Unknown database 'freemarket_sample_54c_production'):
チーム開発でmearge済みのmigrationファイルを変更をしてその後に再度meargeをしたり、
rake db:reset等を繰り返しているといつか起きてしまうかと思います。
私たちのチームではどうしようもなくなってしまい、結局一から本番環境のデータベースを構築をし直したのですが、これは本来あまりやっていいことではないかと思っています。
もちろん開発の初期段階ですので正直困りはしなかったのですが、実際のサービスだと既に重要な顧客データが多数入っているためです。
こちらは他に何かやり方あれば教えていただけると非常に嬉しいです。ちなみにcapistrano導入後はデータベースをcreateしたり、dropをさせるときもエラーを見るのと同じくcurrent上でコマンドは打ちます。
rake:db:seedも同様で、capistrano導入後はディレクトリの位置が変更をされるためです。④credentials.yml関連
ActiveSupport::MessageEncryptor::InvalidMessage
このエラーは序盤結構多かったです。こちらに関しては他に詳しい記事も多数あるかと思いますので割愛します。
credentials.ymlに関しては最初は結構苦戦をしましたが、慣れるとAPIkeyの管理がすごく簡単でした。
怖がるものではないかと思います。⑤データベース内に数値が入っていなかった時
ActionView::Template::Error (undefined method `id' for nil:NilClass):
すごく単純なようで起きがちなエラーです。
特にテーブルを増やしてカラムに新しく外部キーを増やすと起こりがちです。
私はデータベースをSQLで直接いじっていましたが、Sequelpro等で本番でも見れるようにすることができるので、入れたら楽に設定をできると思います。
※ベーシック認証をしてからだと思うのですが、途中からSequelproは使えなくなりました。おそらく弾かれてしまったのだと思います。もしかするとベーシック認証を使っていてもできるのかもしれませんが、私は調べきれませんでした。⑥JQueryで発火をしなかった※データが入っていない時
エラーログは出ません。
インクリメンタルサーチをしようとしたのですが、本番環境のデータで名前を検索をする際に、そもそも該当の名前のデータが入っていませんでした。。。これは完全に単なる勘違いです。即解決できましたが、全くエラーログが出てこないので原因が不明になるので少しの時間、不安になりました。
これも上記の⑤と一緒でSequelproとか入れたらわかりやすそうです。まとめ
基本はエラーログをしっかりと見ていけば何とかなりました。ただ、データベース関連は視覚化ができていなかったので、苦労をしました。
これに関してはエラーログの構造等を理解しだしてから、格段に理解が深まったので、細かいWebアプリの仕組みに関しては理解をしといたほうがいいなと感じました。
今回挙げた事例に関しては正直かなり簡単なほうだと思っています。これから先も出てくる事例は多いと思うので、また何かあれば記事に挙げていきます。参考記事
https://www.javadrive.jp/rails/template/index11.html
https://www.bokukoko.info/entry/2017/10/27/231129
https://qiita.com/scivola/items/cc06ddbfd94d3118f693
- 投稿日:2019-08-16T15:54:57+09:00
Docker環境でHeadless Chromeを使ったSystemt Specを実行する
この記事なんやねん
みなさん、Rspecで統合テスト(System Spec)書いてますか?
すっかりSeleniumを使ってヘッドレスブラウザを使った統合テストが主流になりましたが、
DockerとHeadless Chromeを使ったやり方が意外とヒットしなかったので投稿します。手順
ここから早速始めていきましょう。因みに、以下の環境は揃っているという前提で進めます。
- Dockerとdocker-composeをインストール済み
- docker-composeを使ってRails環境を構築済み
- Rspec実行環境を構築済み
もし、 環境構築まだやねん って状態だったら、こちらの記事を参考に開発環境を作ってみてください。
イメージの準備
Dockerfileにheadless chromeドライバをインストールしてもいいのですが、正直めんどくさいです。
brewで簡単にインストールできるのにDocker使った方が環境構築めんどくさいのも本末転倒な感じするので、Dockerhubにイメージ上がってないか検索したら案の定ありました。https://hub.docker.com/r/selenium/standalone-chrome
seleniumがイメージを提供してくれてるようですね。因みに、googleはイメージ提供していないようなので、seleniumさんのイメージをありがたく使いましょう。
サービス追加
docker-composeに以下のように既存サービスの一番下にサービスを追加してください。
docker-copompose.ymlversion: '3' services: ### 省略 ### chrome: image: selenium/standalone-chrome ports: - "4444:4444"これだけです。やっぱりイメージ使えば楽ですなあ。
Rspecの設定
次に、Rspecの設定をしていきます。Capybaraとrails_helperを設定するだけで済むので簡単です。
Gemfileの修正
もしまだライブラリをインストールしていなければ以下を追記してから、
bundle installしてください。Gemfilegroup :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem "database_cleaner" gem "factory_bot_rails" gem "rspec-rails" # 下記を追加 gem 'capybara' gem 'selenium-webdriver' gem 'rspec-retry' endCapibaraの設定
下記のファイルを作成してください。ポート番号は他でもいいですよ。
spec/support/capybara.rbCapybara.default_driver = :selenium_chrome Capybara.javascript_driver = :selenium_chrome Capybara.server_host = Socket.ip_address_list.detect(&:ipv4_private?).ip_address Capybara.server_port = 3001 Capybara.default_max_wait_time = 5 Capybara.ignore_hidden_elements = true Capybara.register_driver :selenium_chrome do |app| opts = { desired_capabilities: :chrome, browser: :remote, url: "http://chrome:4444/wd/hub", } Capybara::Selenium::Driver.new(app, opts) endrails_helperの修正
下記のようにrails_helperを修正してください。
なお、spec/support/system_support.rbというSystemスペック用のヘルパーを
作成しなければ任意ってコメントしてる設定は不要です。spec/rails_helper.rb### 省略 ### # コメントアウトされてるので、コメントアウトを外してください Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } RSpec.configure do |config| ### 省略 ### # Systemスペック用のヘルパー。任意です。 config.include SystemSupport, type: :system # 名称を一意にするために設定。任意かな。 config.before(:all, type: :system) do timestamp! end # Systemスペックは不安定なのでリトライ用の設定。 config.verbose_retry = true config.display_try_failure_messages = true config.default_retry_count = 3 # 下記を追加 config.before(:each, type: :system) do driven_by Capybara.default_driver end config.before(:each, type: :system, js: true) do driven_by Capybara.javascript_driver host! "http://#{Capybara.server_host}:#{Capybara.server_port}" end endヘルパーの作成(任意)
Systemスペックは共通の処理(ログイン処理とか)が多いので、ヘルパーを用意しておくと便利です。
任意と書いていますが、作成しておいて損はないと思います。spec/support/system_support.rbmodule SystemSupport # 一意の名称を作成するために実行時のtimestampを、 # 数字でインスタンス変数に格納するsetterです。地味に便利。 def timestamp!(timestamp = Time.now.to_i) @timestamp = timestamp end # getterです def timestamp @timestamp end # ブロックの結果がtrueになるまでループするメソッド。すげえ使う。 def wait_until(wait_time = Capybara.default_max_wait_time) Timeout.timeout(wait_time) do loop until yield end end # 特定のcssが登場する、もしくは、なくなるまでループするメソッド def wait_for_css(selector, wait_time = Capybara.default_max_wait_time, non_display: false) Timeout.timeout(wait_time) do loop until send((non_display ? :has_no_css? : :has_css?), selector) end yield if block_given? end # 非同期通信が終わるまでループするメソッド def wait_for_ajax(wait_time = Capybara.default_max_wait_time) Timeout.timeout(wait_time) do loop until page.evaluate_script("jQuery.active").zero? end yield if block_given? end endSystem Specの作成
ここまでで設定は完了してるので実際にSystem Specを書いていきましょう。
私のサンプルソースではこんな感じで書いてます。自分のソースコードに合わせて適宜書き直してください。require "rails_helper" # typeはsystemを設定、Javascriptも使うのでjsもtrueにしておく。 RSpec.describe "HelloWorlds", type: :system, js: true do # 最初にテストデータ作成 before(:all) { create(:hello_world, country: "JP", hello: "こんにちわ世界", priority: 1, file_name: "jp.jpeg") create(:hello_world, country: "US", hello: "Hello World", priority: 2) create(:hello_world, country: "CN", hello: "你好 世界", priority: 3) } before(:each) { visit root_path # コンテンツが全て表示されるまで待つ wait_until { (page.all("div.portfolio-item").count == 3) } } context "when go to index page" do it "show contents" do expect(page).to have_css("h1", text: "Hello World") content = page.first("div.portfolio-item") expect(content.first("p.card-text").text).to eq("こんにちわ世界") expect(content.first("h4.card-title")).to have_link("日本") expect(content.first("img.card-img-top")[:src]).to match(/jp\.jpeg/) end end context "when go to Create page" do it "create content" do page.first("#new_hello_world").click # タイトル出るまで待つ wait_until { page.has_css?("h3", text: "New Helloworld") } select "ドイツ", from: "hello_world_country" # 一意の名称で検索してテストデータを作成 fill_in "hello_world_hello", with: "Hallo Welt #{timestamp}" fill_in "hello_world_priority", with: 4 click_on "Submit" # メッセージ出るまで待つ wait_until { page.has_content?("Hello world was successfully created.") } content = page.first("div.form").all("label.form-control") expect(content[0].text).to eq("ドイツ") expect(content[1].text).to eq("Hallo Welt #{timestamp}") expect(content[2].text).to eq("4") end end endテスト実行
早速テストを実行してみましょう。
下記のwebはサービス名なので適宜自分の設定してるサービス名に変更して実行してください。% docker-compose run --rm web rspec spec/system/hello_worlds_spec.rb Starting hr_chrome_1 ... done Starting hr_db_1 ... done Capybara starting Puma... * Version 4.1.0 , codename: Fourth and One * Min threads: 0, max threads: 4 * Listening on tcp://192.168.176.4:3001 .. Finished in 1 minute 1.58 seconds (files took 3.71 seconds to load) 2 examples, 0 failures通りましたね、やったぜ
まとめ
Dockerでも簡単にHeadless Chromeを使った統合テスト環境を実現できました。
テストだけじゃなくてスプレイピングにも利用できるので試してみてください。因みに、今回使ったサンプルソースはこちらのリポジトリになるので参考までにどうぞ。
- 投稿日:2019-08-16T15:45:13+09:00
Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #10 リメンバーミー機能編
こんな人におすすめ
- プログラミング初心者でポートフォリオの作り方が分からない
- Rails Tutorialをやってみたが理解することが難しい
こんなことが分かる
- リメンバーミー機能の実装方法
一緒に勉強していこう
注意:リメンバーミー機能は前回(#9)の導入を済ませた上で機能します。今回の流れ
- ログイン画面にリメンバーミーのチェックボックスを表示する
- リメンバーミー機能を実装する
チェックボックスを表示する
まずはログイン画面にチェックボックスを表示する。
クラス名をつける際、Bootstrap4と3では異なるので注意する。
スタイリングはいい感じだったのでほぼそのまま。app/views/sessions/new.html.erb<% provide(:title, "ログイン") %> <div class="container form-container login-container"> <div class="row"> <div class="col"> <div class="form-logo-img"> <%= link_to image_tag('lantern_lantern_logo.png', width: 100), root_path, class: "logo-img" %> </div> <h1 class="form-title">ログイン</h1> <%= form_with(scope: :session, url: login_path, local: true) do |form| %> <div class="form-group"> <%= form.email_field :email, class: 'form-control', placeholder: "メールアドレス" %> </div> <div class="form-group"> <%= form.password_field :password, class: 'form-control', placeholder: "パスワード" %> </div> <div class="form-group form-check"> <%= form.check_box :remember_me, class: 'form-check-input' %> <%= form.label :remember_me, '次から保存(ログイン省略)', class: 'form-check-label' %> </div> <div class="form-group"> <%= form.submit "ログイン", class: "btn btn-info btn-lg form-submit" %> </div> <% end %> <p class="form-go-to-signup-or-login">新しくはじめる方は<%= link_to "こちら", signup_path %></p> </div> </div> </div>/app/assets/stylesheets/application.scss.erb// 中略 .form-submit { width: 100%; // margin-top: 1rem; // 削除 }リメンバーミー機能を実装する
チェックボックスはparams[:session][:remember_me]に保存される。
それぞれオン→'1'、オフ→'0'。
だから実装は超簡単。app/controllers/sessions_controller.rbclass SessionsController < ApplicationController # 中略 def create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) log_in user params[:session][:remember_me] == '1' ? remember(user) : forget(user) redirect_to user else flash.now[:danger] = 'メールアドレスかパスワードが正しくありません' render 'new' end end # 中略これでリメンバーミー機能の実装は完了。
今日は短いけどここまで。
- 投稿日:2019-08-16T15:36:28+09:00
RSpecでchromedriverとChromeのバージョンが合わない
はじめに
RSpecでsystem specを書いている時にエラーが出て解決までに2時間ぐらいかかりました。
普段から使っているgemを把握していたり、ログを見ていれば、すぐ解決出来たのはずなのに...
いい勉強になりました。エラー
rspecを実行すると...
1.2) Failure/Error: e = error Selenium::WebDriver::Error::SessionNotCreatedError: session not created: Chrome version must be between 71 and 75 (Driver info: chromedriver=2.46.628411 (3324f4c8be9ff2f70a05a30ebc72ffb013e1a71e),platform=Mac OS X 10.14.6 x86_64)このようなエラーが出ます。
Chromeとchromedriverのバージョンが合っていないみたいです。
とりあえず、chromedriverのバージョンを上げてみたり、homebrewで新たにインストールして見ましたが解決しませんでした。インストール時の参考記事↓
MacにChromeDriverを入れる
How to Install Chrome Driver on Mac (2019 Update)解決策
次にgemで関係するものないかなと思い、
gem listでgemを覗いてみるとchromedriver-helperというそれぽいのがあったのでググってみるとお目当ての記事にたどり着きました。以下記事参照↓
サポートが終了したchromedriver-helperからwebdrivers gemに移行する手順-Qiitaそもそもデフォルトでchromedriver-helperが入っているみたいです。gemの存在すら知りませんでした。
chromedriver-helperのサポートが終了しいるため、chromedriverとChromeのバージョンが合わないというエラーが出たみたいです。
また、chromedriver-helperのインストール時にちゃんと警告も出ているみたいです。ちゃんと見ていなかったので気づきませんでした?警告通りgemをgem 'webdrivers'に入れ替えたらあっさり解決しました。
- 投稿日:2019-08-16T13:01:03+09:00
【備忘録】Rails5でモデル名を変更する方法
前提条件
OS:Mac OS X
Rails:5.2.3
groupモデルをroomモデルに変更します。モデル名の変更方法
最初に結論から。
そのあとそれぞれのコマンドの解説をしています。
※置換で一気に変更する際は、migrationファイルなど変更したくないファイルには気をつけてください。1. 変更するモデルに対応するDBのテーブル名を変更
20190815230119_rename_groups.rbdef change # :groupsが変更前 rename_table :groups, :rooms end変更内容をDBに反映
ターミナル$ rails db:migrate2.モデルに関連するファイル名を全て変更
ターミナル# find 対象ディレクトリ | xargs rename -s 変更前のファイル名 変更後のファイル名 $ find ./ | xargs rename -s group room $ find ./ | xargs rename -s Group Room■ 実行結果の例 ■
groups_controller.rb
↓
rooms_controller.rb3.モデルが使用されてるファイルの中身を全て変更
ターミナル# find 対象ディレクトリ -name "検索する文字列" | LC_ALL=C xargs sed -i -e "s/変換前/変換後/g" $ find ./ -name "*room*" | LC_ALL=C xargs sed -i -e "s/group/room/g" $ find ./ -name "*room*" | LC_ALL=C xargs sed -i -e "s/Group/Room/g"■ 実行結果の例 ■
class Admin::GroupsController < ApplicationController
↓
class Admin::RoomsController < ApplicationController4.バックアップとしてできたファイルを削除
なぜかファイル名の末尾に-eとつくバックアップのファイルが生成されたため削除する。
ターミナル# find 対象ディレクトリ -name "検索する文字列" | xargs rm $ find ./ -name "*.*-e" | xargs rm■ 実行結果 ■
下記のようなファイル名の末尾に-eとつくファイルが削除された。
groups_controller.rb-e使用したコマンドの解説
findコマンド
ファイルやディレクトリを検索できる。
オプション コマンドの説明 -name ファイル名を指定して検索できる xargsコマンド
前に実行したコマンドの結果を引き継げる。
ターミナル# find 対象ディレクトリ | xargs rename -s 変更前のファイル名 変更後のファイル名 $ find ./ | xargs rename -s group room上記の例だと、「find ./」で自分がいる階層のファイルとディレクトリを検索している。
xargsを使ってその検索結果を引き継ぎ、引き継いだ検索結果の中からファイル名がgroupの
ものをroomに変更している。sedコマンド
ファイルの中身のテキストを置換できる。
オプション コマンドの説明 -i ファイルを上書きする s 正規表現で置換処理をする g マッチした全ての文字列を置換する -eコマンドはちょっとよくわかんない。
ご存知の方、ご教授いただけると助かります。LC_ALL=C
出力する言語や時刻などのフォーマットを設定する。
LC_ALL=Cはデフォルトの言語(英語)で出力される。デフォルトの言語を日本語にしているせいかこれつけないとillegal byte sequence
っていうエラーがでるからつけてます。終わりに
モデル名の変更に時間がかかったので自分用のメモとしてこの記事を書きました。
皆様のモデル名の変更するときの参考になれば幸いです。なぜバックアップのファイルが出力されたのか謎。
- 投稿日:2019-08-16T09:47:20+09:00
Railsチュートリアル 第3章 - ほぼ静的なページの実装を題材に、テスト駆動開発してみる
前提
Railsチュートリアル 第3章 - rails createの裏側で何が行われているのかの続きです。待ちに待ったテストの実装パートがやってきました。
テストの実装事始め
Railsには、テストの実装を支援するための仕組みが実装されています。「
rails generate controllerを実行すると、コントローラテストも一緒に生成される」という動作もその一つです。前のエントリで作成下StaticPagesコントローラにも、現時点できちんとテストは作成されています。test/controllers/static_pages_controller_test.rbというファイルがそれです。test/controllers/static_pages_controller_test.rbrequire 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest test "should get home" do get static_pages_home_url assert_response :success end test "should get help" do get static_pages_help_url assert_response :success end end内容については、Railsチュートリアルにおける説明を読んでもらいたいと思います。現時点では「テストは2つある」ということを認識しておきましょう。
いずれにせよ、「現時点でテストスイートを実行すると、問題なくパスする」という状態になっています。実際にテストを走らせてみましょう。開発環境で
rails testというコマンドを実行するのです。bash# rails test ...略 # Running: .. Finished in 1.759569s, 1.1366 runs/s, 1.1366 assertions/s. 2 runs, 2 assertions, 0 failures, 0 errors, 0 skips「テストが2つ実行され、2つとも成功した」という意味のメッセージが表示されています。
初めてのテスト駆動開発
テスト駆動開発のサイクルは、以下のプロセスの積み重ねで進んでいきます。
- 失敗するテストを最初に書く
- アプリケーションのコードを書き、テストを成功させる
- 必要ならばリファクタリングする
失敗するテストを書く
実際に、失敗するテストを最初に書いてみましょう。テーマは「Aboutページの実装」です。
test/controllers/static_pages_controller_test.rbに、テストコードを書いていきます。前述のコードとの差分は以下のとおりです。test/controllers/static_pages_controller_test.rbclass StaticPagesControllerTest < ActionDispatch::IntegrationTest assert_response :success end + test "should get about" do + get static_pages_about_url + assert_response :success + end + end見事テストが失敗する
開発環境上で、改めて
rails tastを実行します。bash# rails test ...略 # Running: .E. Finished in 0.860237s, 3.4874 runs/s, 2.3249 assertions/s. 1) Error: StaticPagesControllerTest#test_should_get_about: NameError: undefined local variable or method `static_pages_about_url' for #<StaticPagesControllerTest:0x000055649d2c1ee0> test/controllers/static_pages_controller_test.rb:15:in `block in <class:StaticPagesControllerTest>' 3 runs, 2 assertions, 0 failures, 1 errors, 0 skipsめでたく(?)テストが失敗しました。「1 errors」というのがその結果を示していますね。さあここから、「3 runs, 3 assertions」にしていきましょう。
テストを成功させるまで
undefined local variable or method `static_pages_about_url'...略というエラーメッセージが表示されていますね。「url」という文字列があるということは、config/routes.rbを変更するのでしょう。というわけで、以下の変更を加えていきます。config/routes.rbdiff --git a/config/routes.rb b/config/routes.rb index 56f06f7..c9da274 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,5 +3,7 @@ Rails.application.routes.draw do get 'static_pages/help' + get 'static_pages/about' + root 'application#hello' end開発環境にて、再び
rails testを実行してみます。bash# rails test ...略 # Running: E.. Finished in 1.112838s, 2.6958 runs/s, 1.7972 assertions/s. 1) Error: StaticPagesControllerTest#test_should_get_about: AbstractController::ActionNotFound: The action 'about' could not be found for StaticPagesController test/controllers/static_pages_controller_test.rb:15:in `block in <class:StaticPagesControllerTest>' 3 runs, 2 assertions, 0 failures, 1 errors, 0 skipsやはりテストは失敗しますね。
ただ、エラーメッセージの中身には変化がありました。
The action 'about' could not be found for StaticPagesControllerというやつです。「StaticPagesController」という文字列があるということは、app/controllers/static_pages_controller.rbを変更するのでしょう。というわけで、以下の変更を加えていきます。app/controllers/static_pages_controller.rbdiff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index c76b925..19f79a9 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -4,4 +4,7 @@ class StaticPagesController < ApplicationController def help end + + def about + end end開発環境にて、再び
rails testを実行してみます。bash# rails test ...略 # Running: E.. Finished in 1.832907s, 1.6367 runs/s, 1.0912 assertions/s. 1) Error: StaticPagesControllerTest#test_should_get_about: ActionController::UnknownFormat: StaticPagesController#about is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot. test/controllers/static_pages_controller_test.rb:15:in `block in <class:StaticPagesControllerTest>' 3 runs, 2 assertions, 0 failures, 1 errors, 0 skipsまた失敗です。
ただ、またエラーメッセージが変わりましたね。今度は
ActionController::UnknownFormat: StaticPagesController#about is missing a template for this request format and variant.というメッセージになっています。「missing a template」というのは、「コントローラが要求するビューがない」という趣旨のメッセージです。今回の場合であれば、「
app/views/static_pages/about.html.erb」があればOKなはずです。早速当該ファイルを作成しましょう。Railsチュートリアル本文に倣い、内容は以下の通りとします。app/views/static_pages/about.html.erb<h1>About</h1> <p> <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> is a <a href="https://railstutorial.jp/#ebook">book</a> and <a href="https://railstutorial.jp/#screencast">screencast</a> to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p>bash# rails test ...略 # Running: ... Finished in 0.719320s, 4.1706 runs/s, 4.1706 assertions/s. 3 runs, 3 assertions, 0 failures, 0 errors, 0 skipsついにテストが成功しました!
この時点でRailsのdevelopmentサーバーを起動し、
/static_pages/aboutをWebブラウザで表示すると、結果は以下のとおりになりました。リファクタリングしてみよう
Railsチュートリアル本文においては、ここで「Home、Help、Aboutそれぞれのページを編集し、各ページ異なるタイトルを表示する」という機能追加を行います。そのときの具体的な実装手順として、以下の手順で行うことが示されています。
- ページタイトルの簡単なテストを書く(RED)
- 3つのページにタイトルを追加する(GREEN)
- レイアウトファイルを活用してコードの重複を解決する(REFACTOR)
「テストとリファクタリングという一連の手順を実際にやってみよう」ということですね。「テストとリファクタリング」という考え方は大変重要ですし、実際に手を動かすことは覚えることへの早道なので、その手順をなぞってみることにします。
まず、この後の手順を進めるため、ソースコード内にある既存レイアウトファイルを一旦リネームします。
zsh>>> pwd ~/docker/rails_tutorial_test/sample_app >>> mv app/views/layouts/application.html.erb layout_fileページタイトルの追加 - テストを成功させるまで
続いて、テストを以下のように書き換えます。
test/controllers/static_pages_controller_test.rbclass StaticPagesControllerTest < ActionDispatch::IntegrationTest test "should get home" do get static_pages_home_url assert_response :success + assert_select "title", "Home | Ruby on Rails Tutorial Sample App" end test "should get help" do get static_pages_help_url assert_response :success + assert_select "title", "Help | Ruby on Rails Tutorial Sample App" end test "should get about" do get static_pages_about_url assert_response :success + assert_select "title", "About | Ruby on Rails Tutorial Sample App" end endこの時点における
rails testの結果は以下のとおりです。bash# rails test ...略 # Running: FFF Finished in 0.573597s, 5.2302 runs/s, 10.4603 assertions/s. 1) Failure: StaticPagesControllerTest#test_should_get_home [/var/www/sample_app/test/controllers/static_pages_controller_test.rb:7]: Expected at least 1 element matching "title", found 0.. Expected 0 to be >= 1. 2) Failure: StaticPagesControllerTest#test_should_get_about [/var/www/sample_app/test/controllers/static_pages_controller_test.rb:19]: Expected at least 1 element matching "title", found 0.. Expected 0 to be >= 1. 3) Failure: StaticPagesControllerTest#test_should_get_help [/var/www/sample_app/test/controllers/static_pages_controller_test.rb:13]: Expected at least 1 element matching "title", found 0.. Expected 0 to be >= 1. 3 runs, 6 assertions, 3 failures, 0 errors, 0 skips失敗が3つ出ていますね。「Home、About、Helpそれぞれのビューにおいて、title要素が存在しない」という趣旨のメッセージが書かれています。
Home、About、Helpそれぞれのビューを以下のように書き換えます。
app/views/static_pages/home.html.erb-<h1>Sample App</h1> -<p> - This is the home page for the - <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> - sample application. -</p> +<!DOCTYPE html> +<html> + <head> + <title>Home | Ruby on Rails Tutorial Sample App</title> + </head> + <body> + <h1>Sample App</h1> + <p> + This is the home page for the + <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> + sample application. + </p> + </body> +</html>app/views/static_pages/help.html.erb-<h1>Help</h1> -<p> - Get help on the Ruby on Rails Tutorial at the - <a href="https://railstutorial.jp/help">Rails Tutorial help page</a>. - To get help on this sample app, see the - <a href="https://railstutorial.jp/#ebook"><em>Ruby on Rails Tutorial</em> - book</a>. -</p> +<!DOCTYPE html> +<html> + <head> + <title>Help | Ruby on Rails Tutorial Sample App</title> + </head> + <body> + <h1>Help</h1> + <p> Get help on the Ruby on Rails Tutorial at the + <a href="https://railstutorial.jp/help">Rails Tutorial help + page</a>. + To get help on this sample app, see the + <a href="https://railstutorial.jp/#ebook"> + <em>Ruby on Rails Tutorial</em> book</a>. + </p> + </body> +</html>app/views/static_pages/about.html.erb-<h1>About</h1> -<p> - <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> - is a <a href="https://railstutorial.jp/#ebook">book</a> and - <a href="https://railstutorial.jp/#screencast">screencast</a> - to teach web development with - <a href="http://rubyonrails.org/">Ruby on Rails</a>. - This is the sample application for the tutorial. -</p> +<!DOCTYPE html> +<html> + <head> + <title>About | Ruby on Rails Tutorial Sample App</title> + </head> + <body> + <h1>About</h1> + <p> + <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> + is a <a href="https://railstutorial.jp/#ebook">book</a> and + <a href="https://railstutorial.jp/#screencast">screencast</a> + to teach web development with + <a href="http://rubyonrails.org/">Ruby on Rails</a>. + This is the sample application for the tutorial. + </p> + </body> +</html>再び
rails testを実行すると、今度はテストが成功します。bash# rails test ...略 # Running: ... Finished in 0.557244s, 5.3836 runs/s, 10.7673 assertions/s. 3 runs, 6 assertions, 0 failures, 0 errors, 0 skips演習 - テストケースの追加と、テストが成功することの確認
Railsチュートリアル本文によれば、演習の内容は以下のとおりです。
StaticPagesコントローラのテスト (リスト 3.24) には、いくつか繰り返しがあったことにお気づきでしょうか? 特に「Ruby on Rails Tutorial Sample App」という基本タイトルは、各テストで毎回同じ内容を書いてしまっています。そこで、setupという特別なメソッド (各テストが実行される直前で実行されるメソッド) を使って、この問題を解決したいと思います。まずは、リスト 3.30のテストが green になることを確認してみてください (リスト 3.30では、2.2.2で少し触れたインスタンス変数や文字列の式展開というテクニックを使っています。それぞれ4.4.5と4.2.2で詳しく解説するので、今はわからなくても問題ありません)。
まず、
test/controllers/static_pages_controller_test.rbを書き換えます。test/controllers/static_pages_controller_test.rbrequire 'test_helper' class StaticPagesControllerTest < ActionDispatch::IntegrationTest + + def setup + @base_title = "Ruby on Rails Tutorial Sample App" + end + test "should get home" do get static_pages_home_url assert_response :success - assert_select "title", "Home | Ruby on Rails Tutorial Sample App" + assert_select "title", "Home | #{@base_title}" end test "should get help" do get static_pages_help_url assert_response :success - assert_select "title", "Help | Ruby on Rails Tutorial Sample App" + assert_select "title", "Help | #{@base_title}" end test "should get about" do get static_pages_about_url assert_response :success - assert_select "title", "About | Ruby on Rails Tutorial Sample App" + assert_select "title", "About | #{@base_title}" end end再び
rails testを実行します。bash# rails test ...略 # Running: ... Finished in 0.487921s, 6.1485 runs/s, 12.2971 assertions/s. 3 runs, 6 assertions, 0 failures, 0 errors, 0 skipsRailsチュートリアル本文記載のとおり、テストが成功することが確認できました。
ERBを使って、ページタイトルの一部を変数で与えるようにしてみる
今度は、「Home、Help、Aboutそれぞれのビューを編集し、ページタイトルの一部を変数で与えるようにする」という機能を実装してみます。各ビューの内容の差分は以下のとおりです。
app/views/static_pages/home.html.erb+<% provide(:title, "Home") %> <!DOCTYPE html> <html> <head> - <title>Home | Ruby on Rails Tutorial Sample App</title> + <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>Sample App</h1>app/views/static_pages/help.html.erb+<% provide(:title, "Help") %> <!DOCTYPE html> <html> <head> - <title>Help | Ruby on Rails Tutorial Sample App</title> + <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>Help</h1>app/views/static_pages/about.html.erb+<% provide(:title, "About") %> <!DOCTYPE html> <html> <head> - <title>About | Ruby on Rails Tutorial Sample App</title> + <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> </head> <body> <h1>About</h1>
rails testを実行します。bash# rails test ...略 # Running: ... Finished in 0.694081s, 4.3223 runs/s, 8.6445 assertions/s. 3 runs, 6 assertions, 0 failures, 0 errors, 0 skipsテストも無事成功していますね。
リファクタリング - HTMLの重複した構造をDRYにする
まず、項目「リファクタリングしてみよう」の冒頭で退避させていた
app/views/layouts/application.html.erbの内容を元に戻します。zsh>>> pwd ~/docker/rails_tutorial_test/sample_app >>> mv layout_file app/views/layouts/application.html.erb
app/views/layouts/application.html.erbを以下の内容で書き換えます。app/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> <%= csrf_meta_tags %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <%= yield %> </body> </html>以降、Sampleアプリは
app/views/layouts/application.html.erbでHome、Help、AboutそれぞれのビューのHTML構造を定義する動作になります。各ビューにbodyの内容以外は不要になるので、不要な部分は削除します。差分は以下のとおりです。app/views/static_pages/home.html.erb<% provide(:title, "Home") %> -<!DOCTYPE html> -<html> - <head> - <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> - </head> - <body> - <h1>Sample App</h1> - <p> - This is the home page for the - <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> - sample application. - </p> - </body> -</html> +<h1>Sample App</h1> +<p> + This is the home page for the + <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> + sample application. +</p>app/views/static_pages/help.html.erb<% provide(:title, "Help") %> -<!DOCTYPE html> -<html> - <head> - <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> - </head> - <body> - <h1>Help</h1> - <p> Get help on the Ruby on Rails Tutorial at the - <a href="https://railstutorial.jp/help">Rails Tutorial help - page</a>. - To get help on this sample app, see the - <a href="https://railstutorial.jp/#ebook"> - <em>Ruby on Rails Tutorial</em> book</a>. - </p> - </body> -</html> +<h1>Help</h1> +<p> Get help on the Ruby on Rails Tutorial at the + <a href="https://railstutorial.jp/help">Rails Tutorial help section</a>. + To get help on this sample app, see the + <a href="https://railstutorial.jp/#ebook"><em>Ruby on Rails Tutorial</em> + book</a>. +</p>app/views/static_pages/about.html.erb<% provide(:title, "About") %> -<!DOCTYPE html> -<html> - <head> - <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title> - </head> - <body> - <h1>About</h1> - <p> - <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> - is a <a href="https://railstutorial.jp/#ebook">book</a> and - <a href="https://railstutorial.jp/#screencast">screencast</a> - to teach web development with - <a href="http://rubyonrails.org/">Ruby on Rails</a>. - This is the sample application for the tutorial. - </p> - </body> -</html> +<h1>About</h1> +<p> + <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a> + is a <a href="https://railstutorial.jp/#ebook">book</a> and + <a href="https://railstutorial.jp/#screencast">screencast</a> + to teach web development with + <a href="http://rubyonrails.org/">Ruby on Rails</a>. + This is the sample application for the tutorial. +</p>テストが成功することを確認するのも忘れてはいけません。
# rails test ...略 # Running: ... Finished in 0.651065s, 4.6078 runs/s, 9.2157 assertions/s. 3 runs, 6 assertions, 0 failures, 0 errors, 0 skipsソースコードに対する変更をGitリポジトリにコミットするのも忘れずに。
zsh>>> git add -A >>> git commit -am "Add application.html.erb" >>> git push演習 - サンプルアプリケーションにContact (問い合わせ先) ページを作成してみる
Railsチュートリアル本文によれば、演習の内容は以下のとおりです。
サンプルアプリケーションにContact (問い合わせ先) ページを作成してください16 (ヒント: まずはリスト 3.15を参考にして、/static_pages/contactというURLのページに「Contact | Ruby on Rails Tutorial Sample App」というタイトルが存在するかどうかを確認するテストを最初に作成しましょう。次に、3.3.3でAboutページを作ったときのと同じように、Contactページにもリスト 3.40のコンテンツを表示してみましょう。)。
まず、
test/controllers/static_pages_controller_test.rbに、Contactビューに対するテストコードを追加します。test/controllers/static_pages_controller_test.rbclass StaticPagesControllerTest < ActionDispatch::IntegrationTest assert_select "title", "About | #{@base_title}" end + test "should get contact" do + get static_pages_contact_url + assert_response :success + assert_select "title", "Contact | #{@base_title}" + end + endこの時点でテストを実行します。
bash# rails test ...略 # Running: .E.. Finished in 1.085848s, 3.6838 runs/s, 5.5256 assertions/s. 1) Error: StaticPagesControllerTest#test_should_get_contact: NameError: undefined local variable or method `static_pages_contact_url' for #<StaticPagesControllerTest:0x000055b6c4f0ba88> test/controllers/static_pages_controller_test.rb:28:in `block in <class:StaticPagesControllerTest>' 4 runs, 6 assertions, 0 failures, 1 errors, 0 skips当然テストは失敗します。
undefined local variable or method `static_pages_contact_url'というメッセージがポイントですね。urlとあるだけに、config/routes.rbにstatic_pages/contactへのルーティングが設定されていないことが問題のようです。早速config/routes.rbを書き換えます。config/routes.rbget 'static_pages/about' + get 'static_pages/contact' + root 'application#hello' end再びテストを実行します。
bash# rails test ...略 # Running: .E.. Finished in 1.048572s, 3.8147 runs/s, 5.7221 assertions/s. 1) Error: StaticPagesControllerTest#test_should_get_contact: AbstractController::ActionNotFound: The action 'contact' could not be found for StaticPagesController test/controllers/static_pages_controller_test.rb:28:in `block in <class:StaticPagesControllerTest>' 4 runs, 6 assertions, 0 failures, 1 errors, 0 skips失敗しましたが、メッセージの内容が変わっています。
The action 'contact' could not be found for StaticPagesControllerとありますね。StaticPagesコントローラにcontactというアクションが存在しないことが原因のようです。app/controllers/static_pages_controller.rbに、StaticPages#contactというアクションの定義を追加します。app/controllers/static_pages_controller.rbclass StaticPagesController < ApplicationController def about end + + def contact + end end再びテストを実行します。
bash# rails test ...略 # Running: ..E. Finished in 1.480814s, 2.7012 runs/s, 4.0518 assertions/s. 1) Error: StaticPagesControllerTest#test_should_get_contact: ActionController::UnknownFormat: StaticPagesController#contact is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] ...略 4 runs, 6 assertions, 0 failures, 1 errors, 0 skipsまた失敗しましたが、またメッセージの内容が変わっています。
StaticPagesController#contact is missing a template for this request format and variant.というメッセージですね。内容が定義されたapp/views/static_pages/contact.html.erbが必要になる、ということなのでしょう。早速当該ファイルを作成し、内容を定義します。zsh>>> pwd ~/docker/rails_tutorial_test/sample_app >>> touch app/views/static_pages/contact.html.erbapp/views/static_pages/contact.html.erb<% provide(:title, "Contact") %> <h1>Contact</h1> <p> Contact the Ruby on Rails Tutorial about the sample app at the <a href="https://railstutorial.jp/contact">contact page</a>. </p>再びテストを実行します。
bash# rails test Running via Spring preloader in process 473 /var/www/sample_app/db/schema.rb doesn't exist yet. Run `rails db:migrate` to create it, then try again. If you do not intend to use a database, you should instead alter /var/www/sample_app/config/application.rb to limit the frameworks that will be loaded. Run options: --seed 32838 # Running: .... Finished in 0.908276s, 4.4039 runs/s, 8.8079 assertions/s. 4 runs, 8 assertions, 0 failures, 0 errors, 0 skipsテストが成功しました。Webブラウザで/static_pages/contactを表示した結果は以下のとおりです。
ルーティングの設定
config/routes.rbを書き換え、/にアクセスした際に/static_pages/homeが表示されるようにします。config/routes.rbRails.application.routes.draw do + root 'static_pages#home' get 'static_pages/home' - get 'static_pages/help' - get 'static_pages/about' - get 'static_pages/contact' - - root 'application#hello' endこの時点でWebブラウザで/を表示すると、結果は以下のとおりになります。
演習 - rootルーティングに対するテスト
1.rootルーティングのテストを書いてみてください。
テストコードは以下のとおりです。
test/controllers/static_pages_controller_test.rbclass StaticPagesControllerTest < ActionDispatch::IntegrationTest @base_title = "Ruby on Rails Tutorial Sample App" end + test "should get root" do + get root_url + assert_response :success + end + test "should get home" do get static_pages_home_url assert_response :successテストを実行します。
# rails test ...略 # Running: ..... Finished in 1.082391s, 4.6194 runs/s, 8.3149 assertions/s. 5 runs, 9 assertions, 0 failures, 0 errors, 0 skipsテストは成功しました。
2. rootルーティングをコメントアウトして見て、 redになるかどうか確かめてみましょう。
config/routes.rbRails.application.routes.draw do - root 'static_pages#home' + #root 'static_pages#home' get 'static_pages/home' get 'static_pages/help' get 'static_pages/about' get 'static_pages/contact' end# rails test ...略 # Running: ..E.. Finished in 0.862515s, 5.7970 runs/s, 9.2752 assertions/s. 1) Error: StaticPagesControllerTest#test_should_get_root: NameError: undefined local variable or method `root_url' for #<StaticPagesControllerTest:0x0000556f1b7f69c8> test/controllers/static_pages_controller_test.rb:10:in `block in <class:StaticPagesControllerTest>' 5 runs, 8 assertions, 0 failures, 1 errors, 0 skipsテストは失敗しました。
undefined local variable or method `root_url' ...略 `block in <class:StaticPagesControllerTest>'というメッセージが表示されていますね。「rootへのルーティングが定義されていない」という趣旨のエラーメッセージなので、確かに先ほどrootルーティングをコメントアウトしたのが原因のようです。演習が終わったら、rootルーティングのコメントアウトを元に戻すのを忘れずに。
最後に、本番環境へのデプロイ
まず、ブランチ
static-pagesの内容をmasterにマージするzsh>>> git checkout master >>> git merge static-pages >>> git push以上のコマンドを順に実行します。以下の操作が順に実行されます。
- 作業対象のブランチを
masterに変更する- ブランチ
static-pagesに適用された変更を、現在の作業対象のブランチにマージする- 現在の作業対象のブランチに適用された変更をGitHubにプッシュする
ローカルの
masterに適用された変更をHerokuにプッシュするここまで実行し、現在の作業対象のブランチが
masterであることを確認したら、いよいよHerokuにここまでの内容をデプロイします。zsh>>> git branch * master static-pages >>> git push heroku Total 0 (delta 0), reused 0 (delta 0) ...略 To https://git.heroku.com/warm-woodland-62915.git ffe2338..2ffdc50 master -> masterHerokuで/にアクセスしてみます。
/homeの内容が表示されました。
余談…リモートブランチを削除する
間違ってブランチ
static-pagesをHerokuにプッシュしてしまったという場合は、当該リモートブランチを削除しておきましょう。「ローカルブランチを指定せずにリモートブランチにプッシュする」という操作を行うことによって、リモートブランチを削除することができます。zsh>>> git push heroku :static-pages remote: Pushed to non-master branch, skipping build. To https://git.heroku.com/warm-woodland-62915.git - [deleted] static-pages
- 投稿日:2019-08-16T06:17:37+09:00
Pay.jpのキー
Ruby 2.5.1
Rails 5.2.3スクールの課題(チーム開発で)デプロイ担当となってしまった私は、Pay.jpのAPIのキーがないというエラーに直面しましたので、少しでも参考になればとやったことを書いておきます。
Pay.jp利用のための公開鍵、秘密鍵の登録
local環境ではdotenv-railsを利用して環境変数を管理していましたが、本番でAPIキーがないことを示すエラーが出ていました。環境変数の読み込みがうまくいっていないということですね。
何はともあれ.bash_profileに書き込む
ローカル、リモート共にキーの情報をbashに直書き!
$ vim ~/.bash_profileAPIの秘密鍵、公開鍵の情報をコピー&ペースト。
$ source ~/.bash_profile有効化するには上記コマンドが必要。記述が変だとエラーが出るので注意しましょう。
結局失敗。
.env.developmentを作成しなかったためでは?
envのメリットは環境ごとに扱う変数の管理ができること。
.env.development
.env.test
.env.developmen
と種類がある。私のチームでは.env.developmentしかなかった。よって、prodductionを作成。
①作成 失敗
②touch コマンドで本番(EC2)current以下に作成 失敗リモートの/env.environment以下に書き込む
ターミナルで以下の操作
$ sudo vim /etc/environment環境変数を有効にするため一旦ログアウト、サイドリモートに接続し直す。
$ env | grep キーの名前これで値が取れているか確認できます。
私の場合はさらにunicornを再起動したらうまくいきました。
- 投稿日:2019-08-16T03:14:29+09:00
Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #9 永続セッション, cookie編
こんな人におすすめ
- プログラミング初心者でポートフォリオの作り方が分からない
- Rails Tutorialをやってみたが理解することが難しい
- ポートフォリオを作成しながら勉強したい
前回:#8 ログイン/ログアウト, FactroyBot編
次回:#10 リメンバーミー機能編こんなことが分かる
- 永続化するメソッドが知れる
- cookieについて分かる
- attr_accessorについて分かる
- クラスメソッドとインスタンスメソッドの違いが分かる
一緒に勉強しませう
今回の流れ
- 永続化の手順をざっくり解説
- 各単語やメソッドを確認
- 永続化を実装
- バグを除去
こういう流れでやって行きます。
どうやってログインを永続化させる?
ログインを永続化させるリメンバーミー機能。
(リメンバーミー機能の実装は次回)
流れ的にはこうらしい(以下Tutorial 9.1 Remember me 機能を引用)
- 記憶トークンにはランダムな文字列を生成して用いる
- ブラウザのcookiesにトークンを保存するときには、有効期限を設定する
- トークンはハッシュ値に変換してからデータベースに保存する
- ブラウザのcookiesに保存するユーザーIDは暗号化しておく
- 永続ユーザーIDを含むcookiesを受け取ったら、そのIDでデータベースを検索し、記憶トークンのcookiesがデータベース内のハッシュ値と一致することを確認する
なんのこっちゃ。
それによく分からない言葉やメソッドやらがいっぱいある。
- cookie
- remember_digest
- remember_token
- User.new_token
- User.digest
- remember
- attr_accessor ← これはRubyの話だけど
これも1つずつ解説しよう。
永続化の仕組みをざっくり解説
これから以下の工程で永続化を行います。
超ざっくりにまとめるとこんな感じ。
- サーバに記憶トークンとIDの暗号作ってもらう
- パソコンに保存する
- 次来た時保存した暗号をサーバと照らし合わせる
- OKだったら勝手にログインする
クッキー(cookie)とは?
クッキーはパソコンとサーバの橋渡しです。
...質素すぎますね。クッキーちゃんというキャラを想像してみよう(強引)
...ここにいました。
はサーバと私たちのパソコンを繋ぐメモ係です。
私たちが「次もログインしたままがいいなあ、サーバさんに伝えといてくれる?」
とお願いすると、は「分かった!」と言ってくれました。
しかし
はか弱いです。
サーバに重要な情報を渡す途中で盗まれたりしたら大変!
なので名前をつけた情報(remember_token)を暗号(remember_digest)にしちゃいました。これで安全!
はサーバさんに情報を伝えるおつかいに行ったのでした。
分かりやすい解説↓
クッキー(cookie)とは?初心者でも分かるように図解remember_token / remember_digestとは?
remember_tokenを暗号化 → remember_digestに代入
こういう関係性。User.new_token / User.digest / rememberとは?
User.new_token → remember_tokenを作るメソッド
User.digest → remember_tokenとかの値を暗号にするメソッド
remember → 暗号をremember_digestに代入するメソッドUser.digestと関連する解説↓
ハッシュ値 (hash value)とはremember_digestに保存するまでを実装する
ここまでくると各メソッドがどういう風に動くかが見えてくる。
- User.new_tokenメソッドでremember_tokenを作る
- User.digestメソッドでremember_tokenを暗号化する
- rememberメソッドで暗号化したremember_tokenをremember_digestに保存する
では早速実装しよう。
その前に1つ。
User.digestやUser.new_tokenはこんな風に書き換えできる。class << self def digest(string) end def new_token end endこれを踏まえた上で実装する。
まずUserモデルにremember_digest属性を追加。bash$ rails generate migration add_remember_digest_to_users remember_digest:string $ rails db:migrateその後user.rbにメソッドを書く。
app/models/user.rbclass User < ApplicationRecord attr_accessor :remeber_token # 中略 class << self def digest(string) cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost BCrypt::Password.create(string, cost: cost) end def new_token SecureRandom.urlsafe_base64 end end def remember self.remember_token = User.new_token update_attribute(:remember_digest, User.digest(remember_token)) end endattr_accessorは何をしているのか
勘のいい方は分かるかもしれませんが、
本来remember_tokenなんて属性はUserモデルにありません。
(あるのはさっきmigrationで作ったremember_digestだけ)だからremember_token属性に代入できるわけがない。
それをいい感じにやってくれるのがattr_accessor。attr_accessor :remember_tokenこうするとremember_token属性を仮で作ってくれる。
だから存在しないはずのremeber_tokenに代入することができる。分かりやすい解説↓
Rubyのattr_accessorって何?[和訳]
Railsから入った人へ【attr_accessor】って?
永続cookiesでガチセッションするRailsチュートリアル9章どうしてUser.digestやUser.new_tokenにはUser.がつくのか
これも素朴な疑問です。
これらはクラスメソッドと呼ばれるもので、今まで定義してきたインスタンスメソッドとは少々異なります。
- クラスメソッド → クラスオブジェクトからでしか呼び出せない
- インスタンスメソッド → インスタンスオブジェクトからでしか呼び出せない
例えばよくこんなことをしますよね。
user = User.newここでのuserはインスタンスオブジェクト。
この時、↓できるのがインスタンスメソッド。user.hoge今のができず、↓するのがクラスメソッド。
User.hogeどうしてTutorialでは使い分けているのか
User.digestやUser.new_tokenはユーザーオブジェクトが不要です。
つまり暗号化するだけ、トークンを作るだけの処理にユーザ情報は不要。
必要なのは作ってからremember_digestで固有のユーザに代入するときだけ。
(だからrememberはインスタンスメソッドです)それを明示的に示すためにクラスメソッドを使用したのです。
クラスメソッドはクラスそのものの変更や参照する役割にも使用される。
そのような場合にもクラスメソッドを使用する余地がありそう。分かりやすい解説↓
【Ruby】クラスメソッドとインスタンスメソッドについてザクッと分かりやすく説明してみる
Rubyのクラスメソッドとインスタンスメソッドの例クッキーに保存する
続いてIDとトークンをクッキーに保存する。
クッキーを保存するcookiesメソッドがあるので簡単。クッキーの保存期間を20年にするpermanentメソッド、
IDは暗号化していないので暗号化するsignedメソッドも使用する。cookies.permanent.signed[:user_id] = user.id cookies.permanent[:remember_token] = user.remember_tokenそしてSessions内で保存までを一気に行うメソッドがあると便利。
だからここまで作ってきたメソッドをrememberメソッドとしてSessionsヘルパーに組み込もう。app/helpers/sessions_helper.rbmodule SessionsHelper def log_in(user) # 中略 def remember(user) user.remember cookies.permanent.signed[:user_id] = user.id cookies.permanent[:remember_token] = user.remember_token end def current_user # 中略さっき作ったusers.rbに作ったrememberメソッドとは違う。
メソッド内に入っているuser.rememberこそが、user.rbのrememberメソッドです。たった今作ったSessionsヘルパーのrememberメソッドの方は、
- remember_tokenとremember_digestと作成
- IDとremember_tokenとクッキーに保存
これを一気に行うものなのですね。
クッキーのremember_tokenとUserモデル属性のremember_digestを照らし合わせる
じゃあSessionsコントローラに実装しよう。
def create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) log_in user remember user redirect_to user else # 中略 end endこれでひとまず新規ユーザ作成したらクッキーに保存されるようになった。
ただこのままだとcurrent_user(#8参照)がクッキーを参照しない。そのためには、
- 一時セッションの場合
- 永続セッションの場合
これらを分けた上で処理を書く必要がある。
簡潔にまとめていただいている記事があるので、
→ 35歳だけどRailsチュートリアルやってみた。[第4版 9章 9.1 Remember me 機能 まとめ&解答例]から内容をお借りします。永続セッションの場合
session[:user_id]が存在すれば、一時セッションからユーザーを取得
cookies[:user_id]が存在すれば、永続セッションからユーザーを取得if (user_id = session[:user_id]) # 一時セッションからユーザーを取り出す elsif (user_id = cookies[:user_id]) # 永続セッションからユーザーを取り出す if # ユーザーが存在し、永続セッションの中の記憶トークンがDBの値と一致する # ログイン処理 end endというわけで処理を書いていくわけだけど、
- 記憶トークンがデータベース内の記憶ダイジェストの値が一致するか
このメソッドがない。
BCryptを参考にして書こう。app/models/user.rbclass User < ApplicationRecord # 中略 def authenticated?(remember_token) BCrypt::Password.new(remember_digest).is_password?(remember_token) end endこのメソッドの、
* 引数remember_tokenはあくまでただのローカル変数
* 引数remember_digestはUserモデルの属性(始めにmigrationで生成したもの)なので混同しないよう注意が必要。
準備ができたのでcurrent_userを編集する。
app/helpers/sessions_helper.rbmodule SessionsHelper # 中略 def current_user if (user_id = session[:user_id]) @current_user ||= User.find_by(id: user_id) elsif (user_id = cookies.signed[:user_id]) user = User.find_by(id: user_id) if user && user.authenticated?(cookies[:remember_token]) log_in user @current_user = user end end end # 中略これで永続セッションのログインが完了になる。
永続セッションからログアウトする
今の状態だと、ログアウトが正しく機能しない。
log_outメソッドが一時セッションにしか対応していないからだ。
実装の手順はこう。
- Userモデルにremember_digestを破棄するメソッドを定義
- Sessionsヘルパーに実際のログアウトを行うメソッドを定義
というわけで実装しよう。
remember_digestを破棄するメソッドを定義する
remember_digestにnilを代入することでログイン情報を破棄できる。
それをforgetメソッドとして定義。app/models/user.rbclass User < ApplicationRecord # 中略 def forget update_attribute(:remember_digest, nil) end end実際にログアウトを行うメソッドを定義
Sessionsヘルパーのlog_outメソッドに永続セッションを破棄する処理を追加。
その処理にforgetメソッドを新たに定義する。app/helpers/sessions_helper.rbmodule SessionsHelper # 中略 def forget(user) user.forget cookies.delete(:user_id) cookies.delete(:remember_token) end def log_out forget(current_user) session.delete(:user_id) @current_user = nil end endSessionsヘルパーのforgetメソッドもまた、Userモデルのforgetメソッドと異なるので注意。
(Userモデルのforgetメソッドは、Sessionsヘルパーのforgetメソッドの中のuser.forget)これで一応ログアウトが可能になる。
細かなバグ修正
現状2つのバグが残っている。
- 複数のタブで開いたサイトからログアウトが二重に行われる時のエラー
- 複数のブラウザのうち1つはログアウト、もう1つはログアウトせず終了後再び画面を開く時のエラー
それぞれ対応しよう。
複数タブのエラー対処
エラーの理由:
1度目のログアウトでcurrent_userがnilなのにも関わらず、log_outでforget(current_user)しようと試みるから。よって、ログインしているか確認した上でログアウトするように変更する。
app/controllers/sessions_controller.rbclass SessionsController < ApplicationController # 中略 def destroy log_out if logged_in? redirect_to root_url end end複数ブラウザのエラー対処
エラーの理由:
1つ目のブラウザでremember_digestをnilしたのにも関わらず、もう1つのブラウザではクッキーが残っているので、authenticated?に例外が起こるから。よってauthenticated?内でremember_digestがnilなら即座に認証を終了させる。
app/models/user.rbclass User < ApplicationRecord # 中略 def authenticated?(remember_token) return false if remember_digest.nil? BCrypt::Password.new(remember_digest).is_password?(remember_token) end # 中略rubyの慣習的に、1文で済むif文は処理の後に書く。
よってreturn falseを先に、ifはその後。これでようやくバグも取り除いた。
次回はリメンバーミー機能を実装
Tutorialは9.2 [Remember me] チェックボックス直前まできた。
次はリメンバーミー機能を実装する。
Tutorialの解説記事になっている?同じ工程だから仕方ない!!
(そのうちポートフォリオ感出てくることを信じて...)
- 投稿日:2019-08-16T01:37:34+09:00
Docker環境でAtomからRubocopを使う方法
この記事なんやねん
最近はDockerを使って環境構築をするケースが多いと思いますが、Atomなどのテキストエディタのコードフォーマットで使うRubocop系のライブラリを動作させるための日本語の記事が意外となかったので投稿します。
Atomのlinter-rubocopをDocker経由で動作させるのを目的に書いていきます。
docker-composeでも動きますよ。手順
ここから早速始めていきましょう。因みに、以下の環境は揃っているという前提で進めます。
- Dockerとdocker-composeをインストール済み
- Atomをインストール済み
- docker-composeを使ってRails環境を構築済み
もし、 環境構築まだやねん って状態だったら、こちらの記事を参考に開発環境を作ってみてください。
また、自分の使ってる
.rubocop.ymlはこちらになります。まだ用意してなければなければ使ってみてください。ラッパースクリプトの配置
まずは、ラッパースクリプトを作成しましょう。
配置場所はどこでもいいですが、プロジェクト配下にbin/rubocopというパスで配置するのが分かりやすいかと思います。今回の例ではそうします。
docker execしていますがdocker runコマンドでも動作します。
ただ、execの方が早いので、コンテナを起動しておいてexecコマンドにてrubocopを叩く方が使い勝手がいいかと思います。
ここら辺はお好みで変えてください。bin/rubocop#!/bin/bash CMD_ARGS="" for arg in $@ do if [ -f "$arg" ] then CMD_ARGS="$CMD_ARGS ${arg#$PWD/}" else CMD_ARGS="$CMD_ARGS $arg" fi done docker exec -i <コンテナ名> rubocop $CMD_ARGS
<コンテナ名>にrubocopがインストールされてるコンテナ名を記載してください。docker-compose psコマンドで確認できます。
因みに、私の環境では下記のようにhr_web_1がrailsとrubocopがインストールされてるコンテナになります。% dc ps Name Command State Ports --------------------------------------------------------------- hr_chrome_1 /opt/bin/entry_point.sh Exit 143 hr_db_1 docker-entrypoint.sh postgres Exit 0 hr_web_1 bundle exec rails s -p 300 ... Exit 1記述したら、
chmod +x bin/rubocopで実行権限を付与するのを忘れないでください。スクリプトの実行準備が整ったら、
docker-compose up -dでコンテナを起動しておきましょう。プラグインの設定
linter-rubocopから
Installボタンを押して、プラグインをAtomにインストールしてください。次に、AtomのLinter Rubocopの設定画面から、Command欄に
./bin/rubocopと入力してください。これで設定は終わりです。
まとめ
全てDocker内で完結できるとスッキリしていいですね。
因みに、いくつかRailsプロジェクトを掛け持ちしてると、あるプロジェクトではDocker使ってなくてローカルでRubocop使う場合もありますよね。そんな時はわざわざAtomの設定を変更しなくても、ラッパースクリプトの最後の行のコマンドをこのように変更すれば大丈夫です。
rubocop $CMD_ARGSはい、単純にローカルにインストールした
rubocopを実行してるだけですね。それではDockerと過ごすRubocopライフを楽しんでください。
- 投稿日:2019-08-16T00:14:48+09:00
Railsのredirect_toとrenderの違い
redirect_toとは
redirect_toメソッドはHTTPレスポンスを返し、自動的にページを切り替えるメソッドになります。これによりRailsのコントローラーでURLにリダイレクトさせる処理を書くことが出来ます。
そもそもリダイレクトとは、指定されたURLに自動でアクセスするように返され、リダイレクト先のURLに永続的を示すステータスコード301か一時的を示すステータスコード307かを示すステータス情報を送信します。
renderとは
renderは文字列、HTML、XML、JSONなどを指定して出力し、主にRailsではコントローラーからviewを呼び出し、画面に表示します。このようにrederは表示するものを指定し画面に表示するメソッドになります。
redirect_toとrenderの違い
では実際にredirect_toとrenderがどのように違う働きをするのかを見ていきましょう。まず2つのメソッドが実際に画面に表示するまでの動きとして、
・render : controller → view
・redirect_to : controller → URL → route → controller → view上の図を見ると分かりますが、renderはコントローラーからそのまま画面を描画します。一方、redirect_toはブラウザでHTTPリクエストを受けたのと同じ処理がされます。
まとめ
単純に画面の遷移を行う場合はrenderを行い、HTTPリクエストがPOSTでデータを作成、更新、削除を行ったあとに画面を遷移する場合はredirect_toを使うというように使い分けると良いでしょう。
- 投稿日:2019-08-16T00:13:14+09:00
Rails技術者認定資格試験対策 Rails命名規則まとめ
数日後にRails技術者認定資格試験を受けるにあたって、基本的な部分ではありますがそういった基本を問うような問題も多いと思われるので、改めてRailsの命名規則をまとめたいと思います。
今回はコントーラーやモデルを仮にUserをベースに考えてみたいと思います。
Controller命名規則
Controllerは複数形になり、UsersControllerになります。この命名規則に従うとresourcesなどのデフォルトのルーティングジェネレータがそのまま利用出来て、名前のルールが決まっているとソースを確認しなくてもrouteファイルにどんどんルーティング情報を書いて行くことが出来ます。
Model命名規則
Modelは単数形になり、Userになります。モデルには作成するインスタンスの情報を定義する設計書のような役割があり、インスタンスは複数でも設計書は1つなので単数形となる、と覚えます。
Routingの命名規則
resources :users
複数のルーティングを一度に作成するため複数形と覚えると良いかもしれません。
データベース関連の命名規則
Railsと関連づけて、データベースの命名規則についても確認しますが、データベースに関してテーブル名、マイグレーションファイル名は複数形になります。データベースは基本的に複数のカラムを持つためテーブル名が複数形になり、そのテーブルを操作するマイグレーションファイルも複数形になると考えます。
最後に
命名規則は守らないと直ちにエラーが起こるという訳ではなく、実際にも意図があってコントローラー名が単数形になっていたりすることもありますが、せっかくRailsというシステムの恩恵を受けて効率良く開発が出来る部分が大きいと思いますので、よほどの意図がない限りは素直にRailsの命名規則には従った方が良いと思われます。
- 投稿日:2019-08-16T00:09:58+09:00
Rails技術者認定資格試験対策 ActionPack
ActionPackとは
ActionPackはRailsの中でもかなり広範囲にわたる機能を提供している部分の1つです。主な機能としてはジェネレータ、セッションなどで、フラッシュ、ActionViewやキャッシュ機能などもActionPackが提供しています。
ジェネレータ
ActionPackが提供するジェネレータとしてcontrollerとscaffoldがあります。まずcontrollerのジェネレータコマンドとして、
rails g controller usersというコマンドを実行すると、
create app/controllers/user_controller.rb invoke erb create app/views/user invoke test_unit create test/controllers/user_controller_test.rb invoke helper create app/helpers/user_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/user.coffee invoke scss create app/assets/stylesheets/user.scssというように大量のファイルが作成されます。もしこのコマンドを取り消したい時には、以下のコマンドを実行して下さい。
rails d(またはdestroy) controller users続いてscaffoldの場合を見ていきましょう。scaffoldをジェネレータで作成するコマンドは以下になります。
rails g scaffold entriesこちらのコマンドを実行すると、
invoke active_record create db/migrate/20190802064905_create_entries.rb create app/models/entry.rb invoke test_unit create test/models/entry_test.rb create test/fixtures/entries.yml invoke resource_route route resources :entries invoke scaffold_controller create app/controllers/entries_controller.rb invoke erb create app/views/entries create app/views/entries/index.html.erb create app/views/entries/edit.html.erb create app/views/entries/show.html.erb create app/views/entries/new.html.erb create app/views/entries/_form.html.erb invoke test_unit create test/controllers/entries_controller_test.rb create test/system/entries_test.rb invoke helper create app/helpers/entries_helper.rb invoke test_unit invoke jbuilder create app/views/entries/index.json.jbuilder create app/views/entries/show.json.jbuilder create app/views/entries/_entry.json.jbuilder invoke assets invoke coffee create app/assets/javascripts/entries.coffee invoke scss create app/assets/stylesheets/entries.scss invoke scss create app/assets/stylesheets/scaffolds.scsscontrollerをジェネレートした場合より、さらに生成されるファイルが多いです。増えたファイルとしてはモデルファイルや ビューファイルがindex、edit、show、new、_formなど複数生成されていることです。今までなんとなく使っていたrailsのジェネレータコマンドですが元々はActionPackで定義された機能だったのです。
セッション
セッションとはHTTPとは状態を保持できないステートレスですが、セッションを使うと状態を保持するステートフルに変更することが出来ます。主にログイン情報を保持したりすることにWebアプリケーションでは使われています。これにより、ログインしている状態であることを保持することが出来て、ログインを毎回全て行う手間から解放されます。
問題としてはセッションで情報を識別する際にセッションIDというものを使用するのですが、このセッションIDを第三者に盗まれてしまうと第三者が自分としてログインすることが可能になったり、セキュティ的には注意して実装する必要がある部分になります。
- 投稿日:2019-08-16T00:06:15+09:00
Active Recordまとめ1
Active Recordとは
Active RecordとはMVCフレームワークにおけるM、モデルに当たる部分で、データとロジックを表します。データベースに保存されたデータは削除しない限りは永続的に保存されます。
ORMフレームワークとしてのActive Record
ORM=オブジェクト・リレーショナル・マッピングとはアプリケーションがもつオブジェクトをMySQLなどのリレーショナルデータベースのテーブルに繋げることが出来ます。これによってMySQLなどでSQL文を直接記述する必要がなくなり、Rubyでアクセスコードを書くだけでアプリケーションのオブジェクトをデーターベースに保存したり、読み出すことが出来るようになります。またActive Recordの役割として以下のような点もあります。
・モデル同士の関連付け
・関連付けられているモデル間の継承階層を表現
・データをデータベースで保存する前にRailsモデルファイル内でバリデーションを実行
Active Recordの命名規則
Railsのモデル名は単数形でUser、Newsのように表現するルールですが、テーブル名はusersやpostsなど複数形で名前を作るルールになっている。また語が複数の単語から成り立つ場合、user_advicesのようにアンダースコアで区切られます。
またデータベースのテーブルで使うカラム名の命名ルールとしては主キーの場合はidという名前のintegerカラムがテーブルを作成すると自動で作成され、外部キーを設定する場合は「テーブル名の単数形id」という名前のidを設定する決まりになっています。よってuseridやpost_id、user_advice_idといった名前のid名になります。
Active Recordでのデータの読み書き
Active Recordでのデータの読み書きはCRUDと呼ばれるデータベースを操作する「Create」「Read」「Update」「Delete」メソッドの頭文字を繋げた言葉で表わされます。
・Create …. createメソッドを実行すると新しいオブジェクトが返され、データベースが保存されます。(newメソッドと似ていますがnewメソッドは単にオブジェクトを返すだけです。)
・Read …. データベース内の情報にアクセス出来る。以下は主なアクセスメソッドです。
user = User.all # 全てのユーザーを返す user = User.first # 最初のユーザーを返す user = User.find_by(name: 'toda') # todaという名前の最初のユーザーを返す・Update …. オブジェクトの情報を取得して更新することが出来ます。
・Delete …. オブジェクトの情報を取得して削除してデータベースから削除することが出来ます。
- 投稿日:2019-08-16T00:02:44+09:00
Rails技術者認定資格試験対策 ActionView
始めに
Rails 技術者認定資格を受験するにあたって今回は試験内容であるActionViewの項目をRailsチュートリアルを中心に自分なりにまとめていきたいと思います。ActionViewという言葉は聞いたことがありますが、実際にどの部分がActionViewであるのかも含めて今回は調べていきます。
ActionViewとは
RailsにおけるWebリクエストはActionControllerとActionViewで扱われます。ActionViewはActionControllerがデータベース処理やCRUD(Create/Read/Update/Delete)アクションを実行したレスポンスをwebページに反映する役割になります。ActionViewはテンプレートとしてデフォルトではERBを使用してビューコードを書くことが出来ます。またgemを組み合わせればERBだけでなく、Slimテンプレートを使用することが出来たりします。
あと自分が実際にActionViewという言葉をよく見かけていたのが、ビューファイルでの入力フォームをスッキリ書けるようにサポートしてくれるtext_field、datetime_selectメソッドなどのフォームヘルパーです。また文字列や日付にもヘルパーが提供されていて、ビューを現在のロケールによって、言語や日時などのローカライズを簡単に行うことが出来ます。またこれらのヘルパーは自分のアプリケーションに応じて機能拡大して独自のヘルパーを作成出来ます。
テンプレート、パーシャル、テンプレート
ActionViewが提供してくれるビューファイルの要素として、テンプレート、パーシャル、レイアウトがあり、ビューファイルはこれら3つの要素から成り立ちます。
テンプレート....ERBやSlimなどのテンプレートエンジンを利用してビューファイルの中にRubyコードでロジックを埋め込むことが出来ます。テンプレートの種類は拡張子で判断され、.erbや.slimの拡張子でRailsでどのテンプレートを使用しているかを表しています。
パーシャル....ビューファイルが肥大化するのを防ぐためにビューを部分的に分割して、複数のビューファイルで使い回すことも出来ます。よくある例としてはヘッダーやフッター、フォームなどをパーシャルとして分割したりします。またパーシャルファイル名はform.html.erbのように一文字目をにすることでパーシャルとして認識され、他のビューファイル内で以下のように記述して呼び出すことが出来ます。
<%= render "form" %>レイアウト....複数のページで共有のデフォルトデザインを表現するための仕組みで、同じようなデザインの大枠を使い回すことが出来ます。デフォルトではapplicationビューファイルがテンプレートになりますが、applicationをテンプレートとして使いたくない場合は、変更したいページのコントローラー内の上部で、以下のように指定します。
layout 'admin_page'最後に
以上が大まかにはなりますが、ActionViewの全体的な役割になります。実際にはActionViewのフォームヘルパーを使う機会が多いはずですので、業務でよく使用している主要なフォームヘルパーの紹介などを、また別の記事で紹介したいと思います。
































