- 投稿日:2020-09-12T23:58:24+09:00
ActiveHashを解説してみた
ActiveHash
職業選択などの変更されないデータをモデルファイル内に直接記述することで、データベースへ保存せずにデータを取り扱うことができる。つまり、Active_Hashを用いることで、モデルファイルに直接記述した変更されないデータに対して、ActiveRecordのメソッドを用いることができる。
導入方法
Gemfile
qiita.rbgem 'active_hash'記述したらbundle installを実行する。
モデル作成時に押さえておくべきこと
結論、--skip-migrationを使用すること。
理由は、データベースを作らない。すなわちマイグレーションファイルは不要となるからだ。ここでrails g modelコマンドを使用してしまうと、マイグレーションファイルも同時に作成されてしまうので注意が必要。% rails g model モデル名 --skip-migrationActiveHash::Base
ActiveRecordと同様のメソッドが使用できる。
つまり、ActiveHash::Baseを継承することで、モデルに定義したオブジェクトに対してActiveRecordのメソッドが使用できるようになる。qiita.rbclass ShippingFee < ActiveHash::Base self.data = [ { id: 0, name: '---' }, { id: 1, name: '着払い(購入者負担)' }, { id: 2, name: '送料込み(出品者負担)' } ] endself.dataでテーブルを作成しているイメージ。データは配列にハッシュ形式で格納されている。
belongs_to_active_hash
通常であればbelongs_to :モデル名となるが、ActiveHashを使って作成したモデルに対してアソシエーションを設定する場合は、belongs_to_active_hashメソッドを使用する。
collection_select
データをプルダウン形式で表示することができるメソッド
記述順 詳細 具体例 第一引数 メソッド名 カラム名 第二引数 オブジェクト 配列データの指定 第三引数 id 参照DBのカラム名 第四引数 name 実際のカラム名 第五引数 prompt プルダウンで一番上に表示したい内容 オプション クラス名 -- qiita.rb<%= f.collection_select(:shipping_fee_id, Shipping_fee.all, :id, :name, {}, {class:"select-box", id:"item-shipping-fee-status"}) %>実装例
代表的なものをいくつかあげてみた
・都道府県
・職業選択
・クローズドクエスチョン(yes or noで答えられるようなもの)
・アンケート
・カテゴリー
・商品のステータス個人的にはこれがあることで利用者側のストレスを大幅に減らすことができ、かつ効率的に情報収集をすることができると感じた。
相手に意見を求めたいときなどに使うといいかもしれない。最後に
ここまで記事を読んでいただき、ありがとうございました。
普段何気なく使っているものにもActiveHashが取り入れられているのですね。
実装も簡単なので、積極的に使っていきたいと感じました。今後も学習を進めていく中で、役立つ情報をどんどん発信していきたいと思うので、よろしくお願いします。
ここまで記事を読んでいただき、本当にありがとうございました‼
- 投稿日:2020-09-12T23:02:15+09:00
【Rails】ActiveRecordのよく使うメソッド集(モデル検索、テーブル結合など)
ActiveRecordには、データベースからオブジェクトを取り出す
検索メソッドが多数用意されています。検索メソッドはwhereやgroupといったコレクションを返したり、
ActiveRecord::Relationのインスタンスを返します。またfindやfirstなどの1つのエンティティを検索するメソッドの場合、
そのモデルのインスタンスを返します。単一のオブジェクトを取り出す
find
主キーで検索
# 主キーが1のユーザーを検索 User.find(1) # SELECT * FROM users WHERE (users.id = 1) LIMIT 1findメソッドで複数のオブジェクトを取得
# 主キーが1と10のユーザーを検索 User.find(1,10) # User.find([1,10])も可 # SELECT * FROM users WHERE (users.id IN (1,10))findメソッドでマッチするレコードが見つからない場合、
ActiveRecord::RecordNotFound例外が発生します。first
デフォルトでは主キー順の最初のレコードを取得
User.first # SELECT * FROM users ORDER BY users.id ASC LIMIT 1firstメソッドで返すレコードの最大数を指定して取得
User.first(3) # SELECT * FROM users ORDER BY users.id ASC LIMIT 3orderを使って順序を変更した場合、
firstメソッドはorderで指定された属性にしたがって最初のレコードを取得User.order(:name).first # SELECT * FROM users ORDER BY users.name ASC LIMIT 1firstメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。last
デフォルトでは主キーの順序にしたがって最後のレコードを取得
User.last # SELECT * FROM users ORDER BY users.id DESC LIMIT 1lastメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。find_by
与えられた条件にマッチするレコードのうち、最初のレコードを取得
User.find_by(name: "hoge") # SELECT * FROM users WHERE (users.name = "hoge") LIMIT 1find_byメソッドは、モデルにレコードが1つもない場合にnilを返します。
このとき例外は発生しません。条件
where
返されるレコードを制限するための条件を指定します。
SQL文でいうWHEREの部分に相当します。User.where(name: "hoge") # SELECT * FROM users WHERE (users.name = "hoge")・NOT条件
SQLのNOTクエリはwhere.notで表せます。User.where.not(name: "hoge") # SELECT * FROM users WHERE (users.name != "hoge")・OR条件
1つ目のリレーションでorメソッドを呼び出し、そのメソッドの引数に
2つ目のリレーションを渡します。User.where(name: "hoge").or(User.where(name: "foo")) # SELECT * FROM users WHERE (users.name = "hoge") OR (users.name = "foo")並び順
データベースから取り出すレコードを特定の順序で並び替え
# ひとかたまりのレコードを取り出し、created_atの昇順で並び替え User.order(:created_at) # SELECT * FROM users ORDER BY users.created_at ASC複数のフィールドを指定して並び替え User.order(name: :asc, created_at: :desc) # SELECT * FROM users ORDER BY users.name ASC, users.created_at DESC取り出すレコード数の上限を指定
limit
主キー順の最初から数えて5件のレコードを取得
User.limit(5) # SELECT * FROM users LIMIT 5テーブルの結合
joins
デフォルトは内部結合(INNER JOIN)
内部結合は「結合条件に一致するレコードのみを取得」
この場合だとpostsテーブルのuser_idと、usersテーブルのidを指定# モデル.joins(:関連名) User.joins(:posts) # SELECT users.* FROM users INNER JOIN posts ON (posts.user_id = users.id)left_joins
左外部結合(LEFT OUTER JOIN)
「結合条件に一致するレコード」と「左側のテーブルにしかないレコード」を取得User.left_joins(:posts) # SELECT users.* FROM users LEFT OUTER JOIN posts ON (posts.user_id = users.id)関連付けを一括読み込みする
includes
一括読み込み(eager_loading)とは、Model.findによって返されるオブジェクトに関連付けられたレコードを読み込むためのメカニズムであり、できるだけクエリの回数を減らすようにします。
・N+1クエリ問題
以下のコードは、ユーザーを10人検索して郵便番号を表示します。
usersテーブルとaddressesテーブルの関係は1対1とします。users = User.limit(10) users.each do |user| puts user.address.postcode endこのコードには問題があります。実行されたクエリの数が無駄に多いことです。
最初にユーザーを10人検索するのにクエリを1回発行し、次にそこから住所を取り出すのに
クエリを10回発行するので、合計11回のクエリが発行されます。・N+1クエリ問題を解決する
ActiveRecordは、読み込まれるすべての関連付けを事前に指定することができます。
これはincludesを指定することで実現できます。
includesを指定すると、ActiveRecordは指定されたすべての関連付けが最小限のクエリ回数で
読み込まれるようにしてくれます。上の例でいうと、User.limit(10)を書き直して、住所が一括で読み込まれるようにします。
users = User.includes(:address).limit(10) users.each do |user| puts user.address.postcode end最初の例では11回もクエリが発行されましたが、今回の例では2回にまで減りました。
SELECT * FROM users LIMIT 10 SELECT addresses.* FROM addresses WHERE (addresses.user_id IN (1,2,3,4,5,6,7,8,9,10))参考
- 投稿日:2020-09-12T22:07:01+09:00
mailcatcherが起動しない場合にやること
はじめに
現在、「現場で使えるRuby on Rails5 速習実践ガイド」いわゆる、現場Railsをざっと通して学習しています。途中でmailcatcherを使うシーンが出てくるのですが、そこで詰まったのでメモとして置いておきます。
そもそもmailcatcherって
(訳)公式サイトの訳です
mailcatcherは、ウェブインターフェースから送信されるあらゆるメッセージをキャッチしてくれるシンプルなsmtpサーバです。mailcatcherを起動し、検証したいアプリの送信先をsmtp://127.0.0.1:1025に設定することで、mailcatcherで用意したブラウザでメールを確認することができます。言ってしまえば、シンプルなメール検証gemです。
インストールと設定
インストールはgem installを公式が推奨しています。
$gem install mailcatcherインストール後以下の設定を行います。(公式のrails欄を引用)
environments/development.rbconfig.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { :address => '127.0.0.1', :port => 1025 } config.action_mailer.raise_delivery_errors = false #ここはすでに記載がある箇所起動方法とトラブルシューティング
この設定を行えば
mailcatcher
で起動できると公式にも書いてあるのですが、$ mailcatcher -bash: mailcatcher: command not foundコマンドが見つかりませんになってしまいました。
(1)rbenv rehash
$ rbenv rehash rbenv: cannot rehash: /Users/local/.rbenv/shims/.rbenv-shim existsファイルが存在してるためrehashできないと言われる。
(2)rails sを止める
これは完全に凡ミスでした。sを止めないとそもそもrbenvができない。(3)ファイルの削除
rehashができない原因のファイルの削除を行う$rm /Users/local/.rbenv/shims/.rbenv-shim改めてrehashすると、通ったようなので、改めて
$ mailcatcher Starting MailCatcher ==> smtp://127.0.0.1:1025無事に通りました!
参考サイト様
以下を参考にさせていただきました。
ありがとうございます。
https://k-koh.hatenablog.com/entry/2020/02/20/095445
https://qiita.com/ironsand/items/2f9f20e6e77ab1877160
- 投稿日:2020-09-12T21:19:30+09:00
RailsでJavaScript(バニラ)が反応しない。
rails5では初期からjQueryのgemが導入されており、削除しない限りはデフォルトで利用できる状態です。
Railsを学び始めて、いつの間にか素のJavaScript(以降”バニラ”)よりもjQueryを使う方が慣れてしまっていたので、今回は改めてバニラを学習するためあえてjQueryを使わない方法を模索しました。環境
Ruby 2.5.7
Rails 5.2.4経緯
改めてバニラでコードを書き始めると割とハマるところが多くありました。
jQueryで書けるならそれでいいと言われればそれまでなのですが、そのままにしておくのは気持ち悪かったので少し向き合うことにしました。即時関数が効かない
手始めにクリックイベントでコンソールログを確認しようとしました。
jQueryでは下記のように$(function() {処理});
という書き方をする必要があります。(理由は後述します。)application.js// jQuery $(function() { $("セレクタ名").on('クリック', function() { console.log('クリックされました。'); }); });jQueryを使わないバニラの即時関数は下記のような書き方になります。
application.js// バニラ (function() { document.getElementById("セレクタ名").addEventListener('click', function() { console.log('クリックされました。'); }); }());上記二つのコードはどちらも処理は同じなのですが、バニラの方はなぜかエラーになり、コンソールログに出力されません。。。
以前はここでjQueryに切り替えていたのですが、どうしても解決したくて今回の模索に至りました。
なぜjQueryは動いて、バニラは動かないのか?
それは
$(function() {});
に隠されていました。(隠されていません。)
$(function() {});
は省略された書き方で、本来の形は下記のようになります。application.js$(document).ready(function { //処理 });readyメソッドはHTMLの読み込みが終わったタイミングで中の処理が行われるメソッドです。
つまりjsの処理を全て$(function() {処理});
の中に書くことで、何も考えなくても処理を行うことができるということになります。バニラではこの"htmlの読み込み待ち"をjQueryを使わずに書く必要があるということです。
DOMContentLoaded
バニラではaddEventListenerメソッドのイベントに
DOMContentLoaded
を指定することで、jQueryのreadyメソッドと同じことができるようになり、コードは以下のようになります。application.jswindow.addEventListener('DOMContentLoaded', function() { // 処理 });バニラでクリックイベントをするには?
ここで最初のjQueryとバニラを比較したコードを振り返ってみます。
application.js// jQery $(function() { $("セレクタ名").on('click', function() { console.log('クリックされました。'); }); });このjQueryは省略せずに書くと
application.js// jQery $(document).ready(function() { $("セレクタ名").on('クリック', function() { console.log('クリックされました。'); }); });という感じになります。
次にバニラをみていきます。
application.js// バニラ (function() { document.getElementById("セレクタ名").addEventListener('click', function() { console.log('クリックされました。'); }); }());これは即時関数なのですが、実はこの書き方でも動作する場合もあります。
方法1.外部jsファイル読み込みのscriptタグをbody要素の一番下に記述する。
通常、Railsでは外部jsファイルの読み込みタグはheadタグの中に書かれていることが多いです。
しかし、headの中でjsファイルを読み込んでしまうと、bodyの中にあるエレメントをとる前にjsの関数が発動してしまうため、エラーになってしまいます。
なので、外部jsファイルの読み込みタグをbodyタグの最後に書く方法です。application.html.erb<head> ... <%#= javascript_include_tag 'application' %> ... <head> <body> ... <%= javascript_include_tag 'application' %> </body>こうすることで、bodyが読み込み終わったタイミングでjsファイルが読み込みを開始するので、要素の読み込みもできるということになります。
しかし、そのほかの外部ファイルやサービス、APIの読み込み設定などは全てheadタグ内に書かれるので、できれば外部jsファイルの読み込みもhead内で統一したいところです。方法2.DOMContentLoadedを使う
先ほども触れましたが、addEventListenerメソッドのDOMContentLoadedイベントを使う方法です。
jsの外部ファイル読み込みタグはhead内のままに、jsファイルの方だけを書き換える必要があります。application.js// バニラ window.addEventListener('DOMContentLoaded', function() { document.getElementById("セレクタ名").addEventListener('click', function() { console.log('クリックされました。'); }); });これでHTMLが読み込まれてからfunctionが実行される形になったので、問題なく動作するようになりました。
注意
ちなみに、DOMContentLoadedイベントはHTMLの読み込み完了を待つだけなので、cssや画像ファイルの読み込みは含まれていません。
画像やcssの操作がある場合はaddEventListenerメソッドのloadイベントを使えば、全ての読み込みが完了してからfunctionを発火させることができます。まとめ
ここまで散々バニラで記述する方法をお伝えしてきましたが、jQueryと相性がいいならjQueryのままでも良いと、個人的には思います。
実際バニラを書いてみて、jQueryならどれほど簡単に書くことができるのかがわかったので・・・笑
これはRailsにも標準搭載されているという観点からも推察できます。質問や解釈の違い、記述方法に違和感ありましたら、コメント等でご指摘いただけると幸いです。
最後まで読んでいただきありがとうございました。
参考サイト
JavaScriptで即時関数を使う理由
【jQuery】$(function() {...}) について 「意味や実行されるタイミング」
DOMContentLoadedイベントとloadイベントの違い[タイミング]
- 投稿日:2020-09-12T21:01:35+09:00
(ギリ)20代の地方公務員がRailsチュートリアルに取り組みます【第7章】
前提
・Railsチュートリアルは第4版
・今回の学習は3周目(9章以降は2周目)
・著者はProgate一通りやったぐらいの初学者基本方針
・読んだら分かることは端折る。
・意味がわからない用語は調べてまとめる(記事最下段・用語集)。
・理解できない内容を掘り下げる。
・演習はすべて取り組む。
・コードコピペは極力しない。第7章はログインと認証システムの開発・第2段回目、ユーザー登録機能を追加していきます。
雨が激しい昼刻(執筆時)、本日のBGMはこちらです。
羊文学 "Blue.ep"
再び羊文学。最近のお気に入り。一曲目の「雨」、いいかんじです。
【7.1.1 デバッグとRails環境 演習】
1. ブラウザから /about にアクセスし、デバッグ情報が表示されていることを確認してください。このページを表示するとき、どのコントローラとアクションが使われていたでしょうか? paramsの内容から確認してみましょう。
→ controller: static_pages
action: about
2. Railsコンソールを開き、データベースから最初のユーザー情報を取得し、変数userに格納してください。その後、puts user.attributes.to_yamlを実行すると何が表示されますか? ここで表示された結果と、yメソッドを使ったy user.attributesの実行結果を比較してみましょう。
→ 下記。この書き方がYAMLということ。結果は一緒です。>> user = User.first User Load (0.6ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "nakamura", email: "mhartl@example.com", created_at: "2020-09-10 02:37:56", updated_at: "2020-09-10 03:07:11", password_digest: "$2a$10$A5n.HFBigQfwnWVJZw2N0e4M9sxPaR8ndLZwqtZWYS7..."> >> puts user.attributes.to_yaml --- id: 1 name: nakamura email: mhartl@example.com created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2020-09-10 02:37:56.040628000 Z zone: &2 !ruby/object:ActiveSupport::TimeZone name: Etc/UTC time: *1 updated_at: !ruby/object:ActiveSupport::TimeWithZone utc: &3 2020-09-10 03:07:11.190666000 Z zone: *2 time: *3 password_digest: "$2a$10$A5n.HFBigQfwnWVJZw2N0e4M9sxPaR8ndLZwqtZWYS7gJGH/Ulohe" => nil >> y user.attributes --- id: 1 name: nakamura email: mhartl@example.com created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2020-09-10 02:37:56.040628000 Z zone: &2 !ruby/object:ActiveSupport::TimeZone name: Etc/UTC time: *1 updated_at: !ruby/object:ActiveSupport::TimeWithZone utc: &3 2020-09-10 03:07:11.190666000 Z zone: *2 time: *3 password_digest: "$2a$10$A5n.HFBigQfwnWVJZw2N0e4M9sxPaR8ndLZwqtZWYS7gJGH/Ulohe" => nil
【7.1.2 Usersリソース 演習】
1. 埋め込みRubyを使って、マジックカラム (created_atとupdated_at) の値をshowページに表示してみましょう (リスト 7.4)。
2. 埋め込みRubyを使って、Time.nowの結果をshowページに表示してみましょう。ページを更新すると、その結果はどう変わっていますか? 確認してみてください。
→ まとめてドン!(くっついて表示されるのがうっとうしかったのでpタグで改行してます) ページ更新すると現時刻が更新されて表示されます。show.html.erb<p> <%= @user.name %>, <%= @user.email %> </p> <p> <%= @user.created_at %>, <%= @user.updated_at %> </p> <p> <%= Time.now %> </p>
【7.1.3 debuggerメソッド メモと演習】
よく分からない挙動があったら、トラブルが起こっていそうなコードの近くにdebeggerを差し込もう!
1. showアクションの中にdebuggerを差し込み (リスト 7.6)、ブラウザから /users/1 にアクセスしてみましょう。その後コンソールに移り、putsメソッドを使ってparamsハッシュの中身をYAML形式で表示してみましょう。ヒント: 7.1.1.1の演習を参考にしてください。その演習ではdebugメソッドで表示したデバッグ情報を、どのようにしてYAML形式で表示していたでしょうか?
→ 下記(byebug) puts @user.attributes.to_yaml --- id: 1 name: nakamura email: mhartl@example.com created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2020-09-10 02:37:56.040628000 Z zone: &2 !ruby/object:ActiveSupport::TimeZone name: Etc/UTC time: *1 updated_at: !ruby/object:ActiveSupport::TimeWithZone utc: &3 2020-09-10 03:07:11.190666000 Z zone: *2 time: *3 password_digest: "$2a$10$A5n.HFBigQfwnWVJZw2N0e4M9sxPaR8ndLZwqtZWYS7gJGH/Ulohe" nil
2. newアクションの中にdebuggerを差し込み、/users/new にアクセスしてみましょう。@userの内容はどのようになっているでしょうか? 確認してみてください。
→ 下記(byebug) @user Started GET "/users/new" for 49.104.6.138 at 2020-09-10 06:51:27 +0000 #<User id: 1, name: "nakamura", email: "mhartl@example.com", created_at: "2020-09-10 02:37:56", updated_at: "2020-09-10 03:07:11", password_digest: "$2a$10$A5n.HFBigQfwnWVJZw2N0e4M9sxPaR8ndLZwqtZWYS7...">
【7.1.4 Gravatar画像とサイドバー 演習】
ここでエラーが。Gravatarの画像が表示されない。念のため手書きしたコードをコピペで上書きしても表示されない。ということは…。
SCSSでdisplay: none;でもしてたか?と思いつつ、検索してみるとやっぱり。第5章の演習で画像を消したときのが残ってました。kittenの仕業じゃ!!でも可愛いから許す!!imgタグのコードを消したら解決しました。1. (任意) Gravatar上にアカウントを作成し、あなたのメールアドレスと適当な画像を紐付けてみてください。メールアドレスをMD5ハッシュ化して、紐付けた画像がちゃんと表示されるかどうか試してみましょう。
→ 画像登録して、コンソールで新しいユーザー作ったら表示されました。
2. 7.1.4で定義したgravatar_forヘルパーをリスト 7.12のように変更して、sizeをオプション引数として受け取れるようにしてみましょう。うまく変更できると、gravatar_for user, size: 50といった呼び出し方ができるようになります。重要: この改善したヘルパーは10.3.1で実際に使います。忘れずに実装しておきましょう。
→ リスト7.12のとおり書くだけ。
3. オプション引数は今でもRubyコミュニティで一般的に使われていますが、Ruby 2.0から導入された新機能「キーワード引数 (Keyword Arguments)」でも実現することができます。先ほど変更したリスト 7.12を、リスト 7.13のように置き換えてもうまく動くことを確認してみましょう。この2つの実装方法はどういった違いがあるのでしょうか? 考えてみてください。
→ んー、いろいろ調べたけど挙動の違いがいまいち分からない。表記を簡潔にできるのがメリットだってのは分かった。それぞれの用語の意味も調べてわかった。キーワード引数:引数にキーを設定した引数。何を引数に入れてるのか明確。
オプション引数:柔軟にいろいろな値を渡せる。拡張性が高い。が、ゆえに渡す値が増えると後々のメンテが大変に。可読性も下がる。んで、キーワード引数は後のバージョンから導入されたということは、コードを簡潔に書くことが目的にあるのか。オプション引数の方の書き方だと、sizeの定義で余分にコード書いてるもんね。そんな回答で良かろうか。
【7.2.1 form_forを使用する 演習】
1. 試しに、リスト 7.15にある:nameを:nomeに置き換えてみましょう。どんなエラーメッセージが表示されるようになりますか?
→ undefined method `nome' for #User:0x00007f6b8d40b370
2. 試しに、ブロックの変数fをすべてfoobarに置き換えてみて、結果が変わらないことを確認してみてください。確かに結果は変わりませんが、変数名をfoobarとするのはあまり良い変更ではなさそうですね。その理由について考えてみてください。
→ 単純に書くのがめんどくさいのと、fはformのfでしょ。脈絡のない単語を使うと読みにくくなる。(証拠は貼ってないけど、わざわざ全部foobarに書き直したよ!)
【7.2.2 フォームHTML 演習】
1. Learn Enough HTML to Be DangerousではHTMLをすべて手動で書き起こしていますが、なぜformタグを使わなかったのでしょうか? 理由を考えてみてください。
→ 読んでへんから知らんけど、入力・送信を使用してないからですって。(そら使わんやろ)
【7.3.2 Strong Parameters メモと演習】
7.3.1で出てきたマスアサインメントの脆弱性はこちらの記事が分かりやすいかも。
1. /signup?admin=1 にアクセスし、paramsの中にadmin属性が含まれていることをデバッグ情報から確認してみましょう。
→ これか。
--- !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
admin: '1'
controller: users
action: new
permitted: false
【7.3.3 エラーメッセージ メモと演習】
empty?メソッド:オブジェクトが空であればtrue、それ以外はfalse
any?メソッド:要素が一つでもあればtrue、ない場合false
pluralizeメソッド:単語の意味は「複数(形)にする」。意味のとおり、来所に与えた引数の整数に基づいて、後に与えた引数の英単語を複数形にしてくれる。1. 最小文字数を5に変更すると、エラーメッセージも自動的に更新されることを確かめてみましょう。
→ 下記。Password is too short (minimum is 5 characters)が表示されます。/models/user.rbvalidates :password, presence: true, length: { minimum: 5 }
2. 未送信のユーザー登録フォーム (図 7.12) のURLと、送信済みのユーザー登録フォーム (図 7.18) のURLを比べてみましょう。なぜURLは違っているのでしょうか? 考えてみてください。
→ 送信前: ~/signup ※ ~はAWSのアドレス
送信後: ~/users
ユーザー登録のためにPOSTリクエストしてるから、usersリソースに応じて/usersに飛んでるって理解でいいかな?(チュートリアル中の表7.1参照)
【7.3.4 失敗時のテスト メモと演習】
countメソッドはあらゆるActive Recordクラスで使用可能。
ここらへんからテストの内容がややこしくなってきます。一つ一つのコードの意味を追っていきましょう。ここの演習はヘビーですが、じっくり考えてみます。(間違っててもご容赦を)1. リスト 7.20で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.25にテンプレートを用意しておいたので、参考にしてください。
→ テンプレに従うと、エラーメッセージのCSSidとclassが表示されているかテストするわけだから、該当箇所を下記のとおり書き換えればテストはGREENです。users_signup_test.rbassert_select 'div#error_explanation' assert_select 'div.field_with_errors'
2. ユーザー登録フォームのURLは /signup ですが、無効なユーザー登録データを送付するとURLが /users に変わってしまいます。これはリスト 5.43で追加した名前付きルート (/signup) と、RESTfulなルーティング (リスト 7.3) のデフォルト設定との差異によって生じた結果です。リスト 7.26とリスト 7.27の内容を参考に、この問題を解決してみてください。うまくいけばどちらのURLも /signup になるはずです。あれ、でもテストは greenのままになっていますね...、なぜでしょうか? (考えてみてください)
→ リスト7.26のルーティング設定によって、登録失敗後に2つの目的地がある状態(URLが/usersと/signup)。それにより、どっちもテストで検知できる状態なので、REDにならない。
3. リスト 7.25のpost部分を変更して、先ほどの演習課題で作った新しいURL (/signup) に合わせてみましょう。また、テストが greenのままになっている点も確認してください。
→ users_pathをsignup_pathに変更。テストで検知するものを変えただけなのでGREENのまま。users_signup_test.rbtest "invalid signup information" do get signup_path assert_no_difference 'User.count' do post signup_path, params: { user: { name: "", # (以下略)
4. リスト 7.27のフォームを以前の状態 (リスト 7.20) に戻してみて、テストがやはり greenになっていることを確認してください。これは問題です! なぜなら、現在postが送信されているURLは正しくないのですから。assert_selectを使ったテストをリスト 7.25に追加し、このバグを検知できるようにしてみましょう (テストを追加して redになれば成功です)。その後、変更後のフォーム (リスト 7.27) に戻してみて、テストが green になることを確認してみましょう。ヒント: フォームから送信してテストするのではなく、'form[action="/signup"]'という部分が存在するかどうかに着目してテストしてみましょう。
→ form_for(@user, url: signup_path)から「, url: signup_path」を取り除く。この状態で実際に登録失敗するとURLは/usersに。これじゃダメだよってことで、検知できるように下記の一文をテストに追加。結果はREDに。そこでformの目的地を再び/singupにすべくリスト7.27の状態に戻すと、テストはGREEN。
理解するのに時間がかかりました。演習2が理解できないと、その続きも理解できませんね。あきらめず理解できるまで調べて考えて解いていきましょう。users_signup_test.rbassert_select 'form[action="/signup"]'
【7.4.1 登録フォームの完成 メモと演習】
redirect_to @user → redurect_to user_url(@user)
とRailsfが勝手に解釈して実行してくれる。ここで、redirect_toとrederの違いって何なん?って気になりませんか?気になりますよね。気になったので調べました。
redirec_toはURLを指定するため、ルーターをとおす一連の動作を行っているみたいですね。データ更新等がある場合に使用。renderは直でviewを表示していると。データ変更を伴わない単純な処理の場合です。1. 有効な情報を送信し、ユーザーが実際に作成されたことを、Railsコンソールを使って確認してみましょう。
→ 実際の登録画面からユーザー登録し、コンソールで適当にfindすればOK。2. リスト 7.28を更新し、redirect_to user_url(@user)とredirect_to @userが同じ結果になることを確認してみましょう。
→ 同じ結果になります。
【7.4.2 flash 演習】
1. コンソールに移り、文字列内の式展開 (4.2.2) でシンボルを呼び出してみましょう。例えば"#{:success}"といったコードを実行すると、どんな値が返ってきますか? 確認してみてください。
→ 下記。文字列が返ってきます。>> "#{:success}" => "success"
2. 先ほどの演習で試した結果を参考に、リスト 7.30のflashはどのような結果になるか考えてみてください。
→ いまいち何を求めているのか分からないけど、こういうこと?>> flash = { success: "It worked!", danger: "It failed." } => {:success=>"It worked!", :danger=>"It failed."} >> "#{flash[:success]}" => "It worked!" >> "#{flash[:danger]}" => "It failed."
【7.4.3 実際のユーザー登録 演習】
1. Railsコンソールを使って、新しいユーザーが本当に作成されたのかもう一度チェックしてみましょう。結果は、リスト 7.32のようになるはずです。
→ やってみるだけ。
2. 自分のメールアドレスでユーザー登録を試してみましょう。既にGravatarに登録している場合、適切な画像が表示されているか確認してみてください。
→ これもやってみるだけ。
【7.4.4 成功時のテスト 演習】
1. 7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.34に最小限のテンプレートを用意しておいたので、参考にしてください (FILL_INの部分を適切なコードに置き換えると完成します)。ちなみに、テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。筆者の場合、flashが空でないかをテストするだけの場合が多いです。
→ FILL_INにはempty?メソッドを入れればOK。2. 本文中でも指摘しましたが、flash用のHTML (リスト 7.31) は読みにくいです。より読みやすくしたリスト 7.35のコードに変更してみましょう。変更が終わったらテストスイートを実行し、正常に動作することを確認してください。なお、このコードでは、Railsのcontent_tagというヘルパーを使っています。
→ 指示通り実行。
3. リスト 7.28のリダイレクトの行をコメントアウトすると、テストが失敗することを確認してみましょう。
→ 失敗します。
4. リスト 7.28で、@user.saveの部分をfalseに置き換えたとしましょう (バグを埋め込んでしまったと仮定してください)。このとき、assert_differenceのテストではどのようにしてこのバグを検知するでしょうか? テストコードを追って考えてみてください。
→ 下記のエラーが。ユーザー数増えてねえよってことか。"User.count" didn't change by 1. Expected: 1 Actual: 0
【7.5.3 成功時のテスト 演習】
1. ブラウザから本番環境 (Heroku) にアクセスし、SSLの鍵マークがかかっているか、URLがhttpsになっているかどうかを確認してみましょう。
→ 鍵かかってました。
2. 本番環境でユーザーを作成してみましょう。Gravatarの画像は正しく表示されているでしょうか?
→ 表示されてました。
第7章まとめ
・やっぱりSass便利。
・RESTfullなルートと個別設定のルートの違い・使い分けに気をつけよう。
・Gravatarってどこまで一般的なの?wordpressやから普及してるの?
・form_forはform_withに置き換わってるようなので参考までに。
・renderとredirect_toの使い分け。
・flashメッセージを表示。
・Strong Parametersでマスアサインメントの脆弱性対策。
・SSLでセキュリティ向上。
この章は演習が歯応えありました。なぜそうなるのか、一つ一つのコードと動作を考える必要がありますね。考えることをあきらめずに取り組んでいきましょう。
次は第8章、ログイン機構を備えていきましょう。
⇦ 第6章はこちら
学習にあたっての前提・著者ステータスはこちら
なんとなくイメージを掴む用語集
・assert_difference(assert_no_difference)
(この章の使い方で想定すると)引数で与えられた数値(結果)が、ブロックの処理を実行する前後で違いがあるかどうかテストする。前者が違いがある、no_differenceは違いがないことを確かめている。・SSL(Secure Sockets Layer)
セキュリティを要求される通信を行うためのプロトコル。現在はTLS(Transport Layer Security)に置き換わっているが、昔の名残でSSLと呼ばれる。
- 投稿日:2020-09-12T20:04:58+09:00
<超初学者向け>「Talk API」を利用してチャットボットを作ってみませんか??【Ruby on Rails】
◆この記事の対象者
☆Rails on Railsを学び始めた初学者
☆ハンズオン形式でLINEBOTを作成してみたい方
☆WEBシステム開発に挫折してしまった方(※フロントエンド側の挫折)
◆はじめに
こんにちは!タイトルの通り、チャットボットを開発したので、その過程を記載しています。
この記事を投稿する経緯にもつながりますが、昔の僕はProgateを1周して、さっそくWEB開発を進めていこうとやる気に満ちていました。しかし、必然であったかのように挫折します。原因は、「フロントエンド側まで手が回らない」でした。
バックエンドのプログラミング言語を学ぶ目的で、Ruby、Railsの基礎を学び、理解度を深めるために何か作ってみようぐらいの気持ちなのに、そこに新たに「フロントエンド」という壁が生まれるのはしんどかったです。Bootstrapを利用して極力手抜きにしようと思いましたが、僕はそれにすら耐えられない、ゆとり世代なんです。
一度挫折して、心を入れ替えて再挑戦しましたが、2度目の挫折を味わいました、、、そんな時に目を付けたのがLINEBOTです。「LINEBOTの開発はフロントエンドを意識する必要がないし、LINEのアプリで成果をすぐに確認できる点も、何かやった気になりたい僕にはぴったりじゃん!!」と思いましたw
この記事は、「とりあえずこの記事と同じ環境で同じ作業をすると誰でも成果物の作成ができますよ!」という点に特化しています。
それなので技術的な内容の説明は薄いです。(そもそも僕も初学者なので、詳しい説明はできないです。)フロントエンドがネックでWEB開発に挫折してしまった方にこの記事を読んでもらいたいです。一緒にチャットボットを作ってみませんか??
◆前提条件
以下に記載していることを前提に記事を作成しています。
・AWSに登録できること(クレジットカード情報の登録が必須になります。)
※僕がCloud9を使用していたので前提条件にしました。ローカルに開発環境を利用しても問題ありませんが、初学者に環境問題のエラーで苦しんでほしくないので、Cloud9を利用することをお勧めします。◆作業手順
◇LINE Developersに登録してLINE BOT用のチャネルを作成する
①LINE Developersに登録、チャネルを作成
ここの説明は省略させてもらいます。以下に載せているURLから公式サイトのページに飛べます。これ確認しながら作業してください。チャネルの名前は何でも問題ありませんが、友達登録したときに表示される名前です。こだわりがなければ「チャットボット君」でいいと思います。。
https://developers.line.biz/ja/docs/messaging-api/getting-started/#using-consoleここまででチャネルの作成が完了していると思うので、引き続きチャネルの設定を変更していきます。
②チャネルアクセストークン(長期)発行
赤枠内のボタン通してアクセストークンを発行してください。
チャネルアクセストークンとは??となっている方は以下のページで確認してください。
https://developers.line.biz/ja/docs/messaging-api/channel-access-tokens/
つまりまとめると、「アプリからAPIを呼び出すための情報」です。③応答設定の変更
次は応答設定を変更していきます。ひとまず何も考えず、画像の通り設定変更してください。
Webhookとは??という好奇心旺盛な方はこの記事を読んでみてください。「子供でも分かる」というのが大げさでないくらい、丁寧に教えてもらえます。
https://kintone-blog.cybozu.co.jp/developer/000283.html◇「Talk API」のAPIキーを取得する
タイトルにある通り、株式会社リクルートテクノロジーズさんが開発した「Talk API」というAPIを利用してチャットボットを開発しています。オウム返しやアンケートBOTでもよかったのですが、APIを利用してみたい!!という初学者である僕なりの目線でチャットボットにしました。
①公式サイトにアクセスしてAPIキー発行
APIを使用するにはAPIキーというものが必要になります。今回はメールアドレスの登録のみでキーが取得できます。
以下の公式サイトから特に詰まることなく取得できると思うので、取得してみてください。
https://a3rt.recruit-tech.co.jp/product/talkAPI/
開発環境の構築
①AWSアカウントの作成
ここも公式サイトにまとめられているので、詳細はこちらで確認して作業をしてください。
https://aws.amazon.com/jp/register-flow/
②Cloud9で開発環境を作成
基本的にはデフォルトのままで問題ありませんが、プラットフォームのみ「Ubuntu Server 18.04 LTS」に変更してください。
次からはチャットボットを実装するため、コードを修正していきます。ここで作成した開発環境を利用して進めていってください。
◇チャットボットの開発
①PostgreSQL用パッケージのインストール
今回は無料利用枠で利用できるHerokuにデプロイするため、データベースはPostgreSQLを利用します。以下コマンドを実行してください。
bashsudo apt install libpq-dev②新規Railsプロジェクトの作成
以下コマンドでインストールするデータベースを指定してプロジェクトを作成してください。
bashrails new <プロジェクト名> -d postgresql
③LINEBOT用のコントローラを作成
"②"の手順で作成したプロジェクトの階層に移動して、以下コマンドを実行してください。
bashrails generate controller linebot④Gemfileの修正
以下をGemfileの末尾に追加して、「bundle install」を実行してください。
Gemfilegem 'line-bot-api' gem 'dotenv-rails'bashbundle install
⑤ルーティング設定
対象のファイルに以下内容をコピペしてください。
config/routes.rbRails.application.routes.draw do post '/callback' => 'linebot#callback' end⑥コントローラ設定
対象のファイルに以下内容をコピペしてください。app/controllers/linebot_controller.rbclass LinebotController < ApplicationController require 'line/bot' protect_from_forgery except: [:callback] def client @client ||= Line::Bot::Client.new do |config| config.channel_secret = ENV['LINE_CHANNEL_SECRET'] config.channel_token = ENV['LINE_CHANNEL_TOKEN'] end end def callback body = request.body.read signature = request.env['HTTP_X_LINE_SIGNATURE'] head :bad_request unless client.validate_signature(body, signature) events = client.parse_events_from(body) events.each do |event| case event when Line::Bot::Event::Message case event.type when Line::Bot::Event::MessageType::Text require 'net/http' require 'uri' require 'json' key = ENV['TALK_API_KEY'] uri = 'https://api.a3rt.recruit-tech.co.jp/talk/v1/smalltalk' params = { apikey: key, query: event.message['text'] } uri = URI.parse(uri) response = Net::HTTP.post_form(uri, params) data = JSON.parse(response.body) message = { type: 'text', text: data['results'][0]['reply'] } client.reply_message(event['replyToken'], message) end end end head :ok end end◇Gitのセットアップをする
①インストール状態の確認
以下コマンドを実行して、Gitのインストール状態を確認します。インストールされていない場合はインストールしてください。
bashgit --version
②Gitの設定を追加
以下コマンドを実行して、Gitの設定を追加してください。
bashgit config --global user.name "ユーザ名" git config --global user.email "メールアドレス" git init◇Herokuにアプリを作成する
⓪Herokuのアカウント作成
今回は作成したアプリをHerokuにデプロイします。まずは以下サイトからアカウントを作成してください。
https://id.heroku.com/login
①Herokuのインストール
以下コマンドでインストール状態を確認してください。bashheroku --version
インストールされていない場合は以下コマンドを実行して、再度インストール状態を確認してください。
bashcurl https://cli-assets.heroku.com/install-ubuntu.sh | sh②Herokuにログイン
以下コマンドを実行して、ログインします。Herokuに登録した際のメールアドレスとパスワードを求められるので、入力してください。
bashheroku login --interactive
③アプリ作成
以下コマンドを実行して、アプリを作成します。アプリケーション名は省略するとランダムな名前で作成されます。
bashheroku create <アプリケーション名>※ここでアプリのURLが作成されるので、控えておいてください。
◇チャネルの設定にアプリの情報を追加する
①先ほど控えたURLをチャネルの設定に追加
②callbackアクションを呼び出すため、URLの末尾に/callbackを追加
◇Herokuにデプロイ
①環境変数の追加
以下コマンドを実行して環境変数を追加します。
bashheroku config:set LINE_CHANNEL_SECRET=チャネル設定画面で確認できるChannel Secret heroku config:set LINE_CHANNEL_TOKEN=チャネル設定画面で確認できるアクセストークン heroku config:set TALK_API_KEY=Talk APIの利用申請で取得したキー②Herokuにデプロイ
以下コマンドを実行してHerokuにデプロイしてください。
bashgit add . git commit -m "linebot" git push heroku master◇動作確認してみよう
①友達追加
QRコードから作成したチャットボットを友達追加してください。
②動作確認
以下のようにメッセージに応じて返信が帰ってきたら成功です!!
◆参考にした記事
僕がLINEBOTを作成していた時に、とても有益な情報となった記事や資料を紹介します。
□【Rails】1時間ぐらいで簡単にLINEのBot開発をしよう-アンケート集計Bot基礎-【画像付き】
https://qiita.com/noriya1217/items/00d6461e9f54900377a3
□LINE Messaging API SDKリポジトリ(Ruby)
https://github.com/line/line-bot-sdk-ruby
□RubyのHTTPリクエストをできるだけシンプルに実装する
https://qiita.com/takano-h/items/dd10818eb7e09161bc29最後に
最後まで見ていただき、ありがとうございます。気になる点などはコメントで指摘いただけるとありがたいです。
- 投稿日:2020-09-12T18:51:05+09:00
mailcatcher はなぜthinサーバを使っているのか
mailcatcher とは
開発環境でメール送信するとlocalhostでメールを受け取り、受け取ったメールを読むためのWEBインターフェースを提供してくれるgemです。
つまり、今までだと「開発環境でメールを送信するとログに出力されていたテキストを拾っていた」のを、「WEBメーラーで見れる」ようになるのです。thinとは
rackサーバと呼ばれる種類のgemで、同じ箱だとwebrick, unicon, puma, passenger, falconなどがあります。
それぞれのrackサーバには特徴があるのですが、rails5ではpumaが同梱されるようになったり、本番環境での使用実績も着実に積んできているしで、デファクトスタンダードはpumaになりつつあると思っている。正直、thinは最近下火のように思う。では、mailcatcherはなぜpumaを使わずにthinを使っているのか。
thinを使っている理由
thinを使っている理由はsinatraでwebsocketを動かすためなのです。
(mailcatcherは、sinatraを使ったWEBインターフェースにwebsocketを使ってメールを受信するとリアルタイムに画面が更新されるようになっている。)sinatraとpumaの組み合わせではwebsocketが動きません。thinだとwebsocketが動く理由は、EventMathineを使ったイベントループ型だからです。
https://github.com/sinatra/sinatra/issues/1035所感
ここからは私の想像と感想なんですが、pumaのようなワーカースレッドモデルの場合、workerごとにリソースを確保するためwebsocketのコネクションを保持しておく場所を確保することができず、websocketのコネクションを張ってもリクエストを終えると破棄してまっている。一方、thinの場合は、イベントループ型なので切断されるまでコネクションを維持が可能、ということなんだと思いました。sinatraとunicornでも同じことが起きるのではないでしょうか。
あれ、railsのActionCableってpuma使ってない?pumaとwebsocketって動かないでしょ?と思ったので調べました。
ActionCableには、Redisのpubsubを使ってフロントとバックエンドを繋ぎ、websocketの接続をrails層で保持し続けるEventMathine相当の仕組みが実装されていました。イベントループ型とワーカースレッド型はどっちを使うべき?
railsがワーカースレッド型の使用を前提とした機能を盛り込んできているあたりを見ると、これからもワーカースレッド型のサーバが使われていくように思います。
また、この意思決定の背景には、GVLが関係していると思っていて、イベントループ型のサーバはシングルプロセスなのでサーバのリソースを使い切りにくい、という事情も感じます。おわり
- 投稿日:2020-09-12T15:46:33+09:00
packsファイルの.jsに記載したのに反応しない
【概要】
1.結論
2.どのように使うか
3.なぜjsファイルに書き込まなかったのか
4.ここから学んだこと
1.結論
直にhtml.erbファイルで"script"を記述しscript内にjavascript言語を記載する!
2.どのように使うか
任意のhtml.erb内で
****.html.erb<script> . . . </script>と記載してJavascriptを記載するだけです!
注意点としてapplication.html.erbで
<%=yiled%>を使用してヘッダーフッダーを適用している場合は反映させる順番に注意です。
HTMLが読み込まれるよりも前にJavascriptを適用させてしまうとブラウザの検証ツールのconsoleでエラーがでます。
3.なぜjsファイルに書き込まなかったのか
結論からいうと、jsファイルを読み込んでくれなかったり、リロードを一回挟まないと読み込んでくれませんでした。
内容としてはパスワード表示非表示を行っていましたが、検索するとたくさん出てくるのでここでは割愛します。Rails6.0
Vscode
を使用しています。app/javascript/packs/application.jsrequire("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("./security") #➡︎該当のファイルと記載し、secrurity.jsにjavascript言語を記載し、
app/layouts/application.html.erb<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>と記載してあるのでjsファイルが適用されるはずと思いきや全く反映されませんでした。
また、jsファイルにてパスワードの表示非表示をプログラムしていましたが反応がないのでいろいろ参考にしました。下記のプログラムを最初に記載しました。
app/javascript/packs/security.jsdocument.addEventListener("DOMContentLoaded", function(){}パスワードの表示非表示はできましたが、一度リロードを押さないと反映がされませんでした。なのでjsファイル問題は解決していませんが、とりあえずやりたいことはできたのでよしとします。(可読性はよろしくないですが)
4.ここから学んだこと
このエラーの中でhtml/css/javascript/の読み込む順番やDOMがどの段階で形成されていくのかが大変勉強になりました。tutbolinksやdocumentloadedについてはまだまだ勉強が足りていないですが、流れだけでも把握できたのはかなり大きいです。scriptにしたとたんすぐに解決したのも、書く順番を把握していたおかげでした。
参考にしたURL
"DOMContentLoaded周りの処理を詳しく調べてみました"
"【JS】addEventListenerが機能しない理由についてご教示ください"
"DOMContentLoadedイベントとloadイベントの違い[タイミング]"
- 投稿日:2020-09-12T15:39:37+09:00
Railsの本番環境でなぜかセッションが保存できない問題を解決した経緯
概要
railsで本番環境にプッシュしたアプリがある日セッションを使った機能の全てが動作しなくなるという絶望的な問題にぶち当たった。めちゃくちゃ苦戦したし記事も全然なかったのでその解決方法を記しておく。
最終的にこの記事から設定しなおして解決しました。
https://www.cotegg.com/blog/?p=1850環境
・EC2にデプロイ
・サーバー環境はWebサーバーにnginx、アプリケーションサーバーにPuma
・AWSのELBにACMのSSL証明書をアタッチしてSSL化↓クライアント→ELB→nginx→puma
セッションが保存されない原因
まず、セッションが保存されない原因
RailsではCSRF(クロスサイトリクエストフォージェリー)という脆弱性からサイトを守るための対策として
protect_from_forgeryというメソッドでサイトを保護している
application.html.erb
のheadタグ内にcsrf_meta_tag
という記述をしているが
ここで認証用のauthenticate_tokenを生成しておりこの値を使ってサイトを認証している。この辺りの説明は腐る程ググれば出てくるため割愛。
このprotect_from_forgeryメソッドのデフォルトの設定でこのauthenticate_tokenが正しくないと
セッションを空にする。(:null_session
オプション)が指定されている。
こいつのせいでセッションは空になっている。
※これが原因かどうか一発でわかる方法として、application_controller.rb
に
protect_from_forgery with: :exception
←CSRFトークンが正しくなければエラーを返す
を記述してサイトにアクセスする
ActionController::InvalidAuthenticityToken
というエラーが出たら原因はそれつまりなんらかの原因でCSRFの認証が通っていない
なぜこんなエラーがでるのか
冒頭に環境を載せておいたが
クライアント→ELB→nginx→puma
という経路でアプリケーションにアクセスするようになっている
ELBにはSSL証明書をアタッチしてあり、自分のこの構成だと
ELBまではhttpsアクセス
ELBとnginxはhttpアクセスになっている。実際にユーザーがアクセスするのはhttpsなのにrailsアプリ側にはhttpでアクセスしていると
情報が伝わってしまい、ここで矛盾が生じるためクロスサイト認定され
エラーが起きてしまっている模様。対策は
nginxなら
/etc/nginx/nginx.confにlocation / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #これ追加 proxy_set_header X-Forwarded-Proto https; #これ追加 proxy_pass http://www.xxxx.com; }としてやるとアクセスするURLと伝わるヘッダー情報が一致してエラーが出なくなる
つまり解決
補足
自分なりの理解で対処した問題の内容をまとめただけのため
間違いがあればご指摘いただけますと幸いです。
Twitterアカウント↓
https://twitter.com/TakeWeb1
- 投稿日:2020-09-12T14:44:17+09:00
【Rails】チェックされた項目だけDBに格納する
teratailで質問した後、自己解決しました。
サイトがすごく重くて(?)投稿できなかったので、先にこっちに書いておきます。やりたかったこと
映画にまつわるアプリを作っており、データベースに映画作品についての簡単な情報を格納する必要がありました。
ターミナルから入力するのはダルかったので、ver.1では公開せず後々ユーザー向けに改良するつもりで、アプリ内に投稿フォームを作成。newメソッドで下記の映画情報をデータベースに登録できるようにしました。
title
:作品タイトルdirected
:監督の名前story
:あらすじservice
:あらすじの引用元の配信サービス名time
:再生時間この中の配信サービス名を四つのチェックボックスの中から選択して、
チェックの入ったサービス名をデータベースに格納する
ということがやりたかったのですが、参考にした記事がチェックボックスの項目とチェック状況を配列に格納する
という内容だったため、実現するためには「チェックされた値だけを取り出す」必要がありました。チェックボックスは
check_box
タグを使って配置しています。<div class="box form4"> <%= check_box "service", "PrimeVideo" %>PrimeVideo <%= check_box "service", "Netflix" %>Netflix <%= check_box "service", "Hulu" %>Hulu <%= check_box "service", "U-NEXT" %>U-NEXT </div>コントローラー内で直接
service: params[:service]と書くと、生成されたハッシュがDBの
service
カラムに格納されます。
チェックが無ければ0
、されていれば1
が値になります。{"PrimeVideo"=>"0", "Netflix"=>"1", "Hulu"=>"0", "U-NEXT"=>"0"}ここから、チェックされた項目のサービス名(つまり値が
1
のキー)だけ抽出して保存したい!
基礎知識がそもそも足りていないためか、ずっと方法が分からず放置していました。解決方法
def req params[:service].each do | di1 , di2 | if di2 == "1" @movie = Movie.new( title: params[:title], directed: params[:directed], story: params[:story], service:di1, time: params[:time] ) @movie.save end end end元々参考にしていた記事のコードを流用させていただきました。
params[:service]
で値を受け取って、二つの変数にそれぞれサービス名
とチェック状態(0 or 1)
を格納する- チェック状態が
1
である場合、相方の変数に格納されているサービス名をserviceカラムに保存するという手順を踏みます。
サービス名のチェックがついていないとsaveメソッドがそもそも実行されないという雑な作りにしてしまいましたが……時間がないので今は前進できただけいいってことで…
落ち着いたらselectメソッド
やkeysメソッド
、あるいはSQLの構文など、もう少し丁寧なコードにできそうな部分を中心に基礎固めをしようと思います。きっかけをくださった回答者の皆さまに、心よりお礼申し上げます。
- 投稿日:2020-09-12T14:40:29+09:00
Rails:日本語化の仕方
手順
application.rb内に以下を記述
config.i18n.default_locale = :jaconfig/locales内に「ja.yml」ファイルを作成し
以下のRailsが推奨しているコードを記述ja: activerecord: errors: messages: record_invalid: 'バリデーションに失敗しました: %{errors}' restrict_dependent_destroy: has_one: "%{record}が存在しているので削除できません" has_many: "%{record}が存在しているので削除できません" attributes: モデル名: カラム名: '表示したいカラム名' カラム名: '表示したいカラム名' date: abbr_day_names: - 日 - 月 - 火 - 水 - 木 - 金 - 土 abbr_month_names: - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 day_names: - 日曜日 - 月曜日 - 火曜日 - 水曜日 - 木曜日 - 金曜日 - 土曜日 formats: default: "%Y/%m/%d" long: "%Y年%m月%d日(%a)" short: "%m/%d" month_names: - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 order: - :year - :month - :day datetime: distance_in_words: about_x_hours: one: 約1時間 other: 約%{count}時間 about_x_months: one: 約1ヶ月 other: 約%{count}ヶ月 about_x_years: one: 約1年 other: 約%{count}年 almost_x_years: one: 1年弱 other: "%{count}年弱" half_a_minute: 30秒前後 less_than_x_seconds: one: 1秒以内 other: "%{count}秒未満" less_than_x_minutes: one: 1分以内 other: "%{count}分未満" over_x_years: one: 1年以上 other: "%{count}年以上" x_seconds: one: 1秒 other: "%{count}秒" x_minutes: one: 1分 other: "%{count}分" x_days: one: 1日 other: "%{count}日" x_months: one: 1ヶ月 other: "%{count}ヶ月" x_years: one: 1年 other: "%{count}年" prompts: second: 秒 minute: 分 hour: 時 day: 日 month: 月 year: 年 errors: format: "%{attribute}%{message}" messages: accepted: を受諾してください blank: を入力してください confirmation: と%{attribute}の入力が一致しません empty: を入力してください equal_to: は%{count}にしてください even: は偶数にしてください exclusion: は予約されています greater_than: は%{count}より大きい値にしてください greater_than_or_equal_to: は%{count}以上の値にしてください inclusion: は一覧にありません invalid: は不正な値です less_than: は%{count}より小さい値にしてください less_than_or_equal_to: は%{count}以下の値にしてください model_invalid: 'バリデーションに失敗しました: %{errors}' not_a_number: は数値で入力してください not_an_integer: は整数で入力してください odd: は奇数にしてください other_than: は%{count}以外の値にしてください present: は入力しないでください required: を入力してください taken: はすでに存在します too_long: は%{count}文字以内で入力してください too_short: は%{count}文字以上で入力してください wrong_length: は%{count}文字で入力してください template: body: 次の項目を確認してください header: one: "%{model}にエラーが発生しました" other: "%{model}に%{count}個のエラーが発生しました" helpers: select: prompt: 選択してください submit: create: 登録する submit: 保存する update: 更新する number: currency: format: delimiter: "," format: "%n%u" precision: 0 separator: "." significant: false strip_insignificant_zeros: false unit: 円 format: delimiter: "," precision: 3 separator: "." significant: false strip_insignificant_zeros: false human: decimal_units: format: "%n %u" units: billion: 十億 million: 百万 quadrillion: 千兆 thousand: 千 trillion: 兆 unit: '' format: delimiter: '' precision: 3 significant: true strip_insignificant_zeros: true storage_units: format: "%n%u" units: byte: バイト eb: EB gb: GB kb: KB mb: MB pb: PB tb: TB percentage: format: delimiter: '' format: "%n%" precision: format: delimiter: '' support: array: last_word_connector: "、" two_words_connector: "、" words_connector: "、" time: am: 午前 formats: default: "%Y/%m/%d" long: "%Y/%m/%d %H:%M" short: "%m/%d %H:%M" pm: 午後この後、ファイルを読み込ませるためにrails serverを再起動で日本語化が完了
補足
attributes: モデル名: カラム名: '表示したいカラム名' カラム名: '表示したいカラム名'この部分だけはコピペしないように!
自分が使用したいテーブルで設計する
- 投稿日:2020-09-12T14:03:40+09:00
Rails:createアクションの仕組み
動機
newとcreateの役割に困惑したためのメモ作成
new
新しく作成する場合は
new.html.hamlとnewアクションがまず使用されるcreate
newの中のformで作成した内容を元にDBへ登録するアクションがcreate
以下のコードが一連の流れです。
def create @task = Task.new(task_params) if @task.save redirect_to task_path(@task), notice: 'Task is create' else render :new endデータの取得
@task = Task.new(task_params) private def task_params params.require(:task).permit(:title, :content) endnewで取得した内容を@taskに引数として渡している
引数にはprivateメソッドで取得した値をいれるrequireは必要という意味
permitは許可という意味
なので:taskモデルの:titleと:contentは許可するけど他の値は許可しないよというようなデータを取得している(ストロングパラメータ)save
if @task.save redirect_to task_path(@task), notice: 'Task is create' else render :new取得したデータが保存できたら
showページに遷移する
できなかったらnewページに遷移する重要
createアクション内もインスタント変数にしている理由は
createからnewページへ遷移する際newページで使用されるインスタンス変数はcreateアクション内にインスタンス数があればこれを利用できるすなわち
保存されなかったデータも残したままnewページを呼び出すことができる
- 投稿日:2020-09-12T13:48:02+09:00
【Rails】LINEBotでPush送信
概要
RailsからLineBotのPush送信します
以下のように、任意のタイミングで送信するやつです
LINEBotの作成
記事がたくさんあるので、そちらを参照してください
例えば、以下のような記事がありますLINE BOTの作り方を世界一わかりやすく解説(1)【アカウント準備編】
RailsからLINEBotでPush送信
- Gemfileでライブラリ
line-bot-api
をインストールしておいてくださいENV["LINE_CHANNEL_SECRET"]
、ENV["LINE_CHANNEL_TOKEN"]
は、herokuなど、サーバ側で、設定する必要があります。この後、説明します- 自分のuser_idは、チャンネル基本設定->あなたのユーザーID、から確認できます
- push_messageがlinebotでpush送信する部分です
linebot_controller.rbclass LinebotController < ApplicationController require 'line/bot' # gem 'line-bot-api' def client @client ||= Line::Bot::Client.new { |config| config.channel_secret = ENV["LINE_CHANNEL_SECRET"] config.channel_token = ENV["LINE_CHANNEL_TOKEN"] } end def push message={ type: 'text', text: "hello" } user_id = '[送信先のLINEアカウントのユーザID]' response = client.push_message(user_id, message) end endRailsアプリをherokuにデプロイ
Railsアプリのデプロイの記事がたくさんあるので、そちらを参照してください
例えば、以下のような記事がありますこの時、以下のように、チャネル基本設定->チャネルシークレット、Messaging API設定->チャネルアクセストークン、をherokuに必ず設定してください
$ heroku config:set LINE_CHANNEL_SECRET="[チャネルシークレット]" $ heroku config:set LINE_CHANNEL_TOKEN="[チャネルアクセストークン]"結果
herokuにデプロイした、Railsアプリのlinebot_controller.rbのアクションpushを実行します
以下のように「hello」がpush送信できました
- 投稿日:2020-09-12T09:28:35+09:00
rails tutorial 第10章
はじめに
独学でrails tutorialを進めていく過程を投稿していきます。
進めていく上でわからなかった単語、詰まったエラーなどに触れています。
個人の学習のアウトプットなので間違いなどあればご指摘ください。
初めての投稿なので読みにくいところも多々あるかと思いますがご容赦ください。
第10章 ユーザーの更新・表示・削除
10.1.1 編集フォーム
演習2
app/views/users/_form.html.erb#リスト 10.5: newとeditフォーム用のパーシャル <%= form_with(model: @user, local: true) do |f| %> <%= render 'shared/error_messages', object: @user %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit yield(:button_text), class: "btn btn-primary" %> <% end %>しれっと
object: @userが追記されています、、、
おそらくshared/error_messagesに渡すオブジェクトを指定しているとは思うのですが
shared/error_messagesは下記のようにapp/views/shared/_error_messages.html.erb<% if @user.errors.any? %> <div id="error_explanation"> <div class="alert alert-danger"> The form contains <%= pluralize(@user.errors.count, "error") %>. </div> <ul> <% @user.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %>と@userオブジェクトを指定しているから必要ない気がするのですが、、、
参考
https://teratail.com/questions/112178また、
<%= f.label :password_confirmation, "Confirmation" %>の第2引数が消えています、、、
第2引数はおそらくラベルのテキストだと予想は出来ましたが念のため調べました。
参考
https://teratail.com/questions/112172第2引数を指定しなくなった事により、ラベルのテキストが
ConfirmationからPassword confirmationと表示されています。意味はどちらもわかるけれどなぜサイレントで修正されたのか、、、
10.2.3 フレンドリーフォワーディング
Q.
requestオブジェクトって?
A.
request.original_url
現在のリクエストURLを返す
request.get?
HTTPメソッドがGETの時trueを返す10.3.2 サンプルのユーザー
問題発生!!
rails db:migrate:resetを実行するとエラーが発生するPermission denied @ apply2files - C:/environment/sample_app/db/development.sqlite3 Couldn't drop database 'db/development.sqlite3' rails aborted! Errno::EACCES: Permission denied @ apply2files - C:/environment/sample_app/db/development.sqlite3 bin/rails:4:in `require' bin/rails:4:in `<main>' Tasks: TOP => db:drop:_unsafe (See full trace by running task with --trace)こちらの記事を参考に解決しました。
https://qiita.com/Toshiki23/items/f366504844fd22ad87d9以前参考にしましたね。windowsでは自らがアクセスしているファイルを削除できないようです。
10.3.3 ページネーション
User.paginate(page: 1)paginateはキー:pageで値ページ番号を取る
User.paginateは、:pageパラメーターに基いて、データベースからひとかたまりのデータ(デフォルトでは30)を取り出す
上のコマンドをコンソールで実行するとページ番号1のオブジェクトを出力します(出力結果が11となりますが、Active Record自身のコンソール上限によるものです。lengthメソッドを呼べばこの制約を回避できます)@users = User.paginate(page: params[:page])params[:page])が1なら1~30のユーザー、params[:page])が2なら31~60のユーザーが取り出され@usersに代入されるようになります。
ちなみにページがnilの場合paginateは単に最初のページを返すとのことです。10.3.5 パーシャルのリファクタリング
app/views/users/index.html.erb<% provide(:title, 'All users') %> <h1>All users</h1> <%= will_paginate %> <ul class="users"> <% @users.each do |user| %> <%= render user %> <% end %> </ul> <%= will_paginate %>app/views/users/_user.html.erb<li> <%= gravatar_for user, size: 50 %> <%= link_to user.name, user %> </li>さらにリファクタリング。
app/views/users/index.html.erb<% provide(:title, 'All users') %> <h1>All users</h1> <%= will_paginate %> <ul class="users"> <%= render @users %> </ul> <%= will_paginate %>Railsは@users をUserオブジェクトのリストであると推測します。さらに、ユーザーのコレクションを与えて呼び出すと、Railsは自動的にユーザーのコレクションを列挙し、それぞれのユーザーを_user.html.erbパーシャルで出力します。(rails tutorial 10章より引用)
またまた初学者には難しい省略が、、、
コレクション、、、つまり複数のデータのようなものを渡すということでしょうか。省略出来ることは理解できましたがそうなると_user.html.erbの
app/views/users/_user.html.erb<li> <%= gravatar_for user, size: 50 %> <%= link_to user.name, user %> </li>eachメソッドで作った変数userはこのままでよいのでしょうか、よいとすれば何をもって変数userという名前に決まったのでしょう?
変数名は何でもよいのでしょうか?
試しに名前を変えてテストをしてみました。app/views/users/_user.html.erb<li> <%= gravatar_for aaa, size: 50 %> <%= link_to aaa.name, user %> </li>結果
エラー。undefined local variable or method `aaa'aaaという変数は定義されていないとのこと。
ここまでで推測出来ることは
①render で渡したオブジェクトを単数形にしたものを自動設定している?
②Userクラスのオブジェクトだから同じ名前としたものを自動設定している?試しに
app/controllers/users_controller.rbdef index @aaas = User.paginate(page: params[:page]) endapp/views/users/index.html.erb<% provide(:title, 'All users') %> <h1>All users</h1> <%= will_paginate %> <ul class="users"> <%= render @aaas %> </ul> <%= will_paginate %>結果
ActionView::Template::Error: The @users variable appears to be empty. Did you forget to pass the collection object for will_paginate?@users変数が空とのこと。will_paginateにコレクションオブジェクトを渡し忘れていないか?というメッセージも見られました。
ということはコレクションオブジェクトの変数名は@usersとするべきなのでしょうか?
そしてwill_pagineteにもそれ(@usersという名前の付いたコレクションオブジェクト)を渡さなければならないのでしょう、、、多分、、、
railsではUserオブジェクトのコレクションオブジェクトをusersという変数名にする(コレクションオブジェクトはクラスオブジェクトの複数形にする)ことで色々と良しなに解釈してくれる設定があるということでしょうか?
そうすることで変数名をuserとすると、そのコレクションを自動で列挙するといような便利な動作をするとか、、、なので
app/views/users/_user.html.erb<li> <%= gravatar_for user, size: 50 %> <%= link_to user.name, user %> </li>こうしなければならないのかな?推測ですけれど、、、
色々とよしなに解釈してくれるのは勿論そのためのルールに則った記述をしてこそですからね、、、
終わりに
今回はリファクタリングに悩まされた章でした。
少し曖昧な部分も残してしまったので、何度か振り返り、明確に理解できるよう学習を進めたいです。
- 投稿日:2020-09-12T08:29:42+09:00
Rails 6で認証認可入り掲示板APIを構築する #7 update, destroy実装
←Rails 6で認証認可入り掲示板APIを構築する #6 show, create実装
updateテストの実装
残り2アクションupdateとdestroyを実装します。
updateは更新処理です。
createができていたら似たようなものなので、大きく詰まることはないはずです。spec/requests/v1/posts_controller.rb... + describe "PUT /v1/posts#update" do + let(:update_param) do + post = create(:post) + update_param = attributes_for(:post, subject: "update_subjectテスト", body: "update_bodyテスト") + update_param[:id] = post.id + update_param + end + it "正常レスポンスコードが返ってくる" do + put v1_post_url({ id: update_param[:id] }), params: update_param + expect(response.status).to eq 200 + end + it "subject, bodyが正しく返ってくる" do + put v1_post_url({ id: update_param[:id] }), params: update_param + json = JSON.parse(response.body) + expect(json["post"]["subject"]).to eq("update_subjectテスト") + expect(json["post"]["body"]).to eq("update_bodyテスト") + end + it "不正パラメータの時にerrorsが返ってくる" do + put v1_post_url({ id: update_param[:id] }), params: { subject: "" } + json = JSON.parse(response.body) + expect(json.key?("errors")).to be true + end + it "存在しないidの時に404レスポンスが返ってくる" do + put v1_post_url({ id: update_param[:id] + 1 }), params: update_param + expect(response.status).to eq 404 + end + end ...例によってcontroller未実装なのでテストはコケます。
updateの実装
app/controllers/v1/posts_controller.rb... def update - # TODO + if @post.update(post_params) + render json: { post: @post } + else + render json: { errors: @post.errors } + end end ...ここももはや特に語ることなし。
これでテスト通過するはずです。destroyテストの実装
spec/requests/v1/posts_controller.rb... + describe "DELETE /v1/posts#destroy" do + let(:delete_post) do + create(:post) + end + it "正常レスポンスコードが返ってくる" do + delete v1_post_url({ id: delete_post.id }) + expect(response.status).to eq 200 + end + it "1件減って返ってくる" do + delete_post + expect do + delete v1_post_url({ id: delete_post.id }) + end.to change { Post.count }.by(-1) + end + it "存在しないidの時に404レスポンスが返ってくる" do + delete v1_post_url({ id: delete_post.id + 1 }) + expect(response.status).to eq 404 + end + end ...postの時と逆で、実行寺にレコード数が1減ることを確認します。
なおポイントとしては、
expectの前にdelete_postを呼んでいることです。以前記載した通りletは遅延評価をされるので、もしexpectの前にdelete_postが無かった場合、expect内の
delete_post.id
でレコードが生成され、delete v1_post_url
で1レコード消える。つまりexpectブロック内で+1-1=0となり、レコード数に変化が生まれません。そのためexpect前にレコードを生成し、expectの中でdeleteが実行されることにより-1レコードとなるわけです。
destroyの実装
app/controllers/v1/posts_controller.rb... def destroy + @post.destroy + render json: { post: @post } end ...こちらも非常にシンプル。
今回は短めですが、次にやるseedまで含むと長くなりすぎるのでここまで。続き
→
【連載目次へ】
- 投稿日:2020-09-12T08:08:02+09:00
rails Action Textの実装
はじめに
自分用メモとして残します。
環境
- Rails v6.0.3.2
- ruby v2.6.6p146
- node v12.18.3
- yarn v1.22.4
手順
1.アプリ作成
MySQLを使ったアプリを作成する。
$ rails new actionText -d mysql作成したアプリに移動
2.scaffold
scaffoldでアプリの雛形を作成する。
※articleはtitleとcontentを持つが、後で追加するためここではtitleのみでOK$ rails g scaffold article title:string3.DB作成
$ rails db:create4.migrate
$ rails db:migrate5.Action Textインストール
$ rails action_text:install6.migrate
$ rails db:migrate7.アソシエーション
Action Textのデータは専用のテーブルに格納されるためarticleモデルに関連づける必要がある。
app/models/article.rbclass Article < ApplicationRecord has_rich_text :content end8.ビューにcontent追加
app/views/articles/_form.html.erb... <div class="field"> <%= form.label :title %> <%= form.text_field :title %> <%= form.label :content %> <%= form.rich_text_area :content %> </div> ...app/views/articles/show.html.erb<p id="notice"><%= notice %></p> <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Content:</strong> <%= @article.content %> </p> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %>9.コントローラにcontent追加
contentをparamsパラメータを読み出すメソッドに追加する。
app/controllers/articles_controller.rb... def article_params params.require(:article).permit(:title, :content) end ...終了
- 投稿日:2020-09-12T00:26:58+09:00
rails tutorial 第9章
はじめに
独学でrails tutorialを進めていく過程を投稿していきます。
進めていく上でわからなかった単語、詰まったエラーなどに触れています。
個人の学習のアウトプットなので間違いなどあればご指摘ください。
初めての投稿なので読みにくいところも多々あるかと思いますがご容赦ください。
第9章 発展的なログイン機構
9.1.1 記憶トークンと暗号化
attr_accessorの復習
参考
https://qiita.com/Hassan/items/0e034a1d42b2335936e6
.remember_tokenが使えるようになります(remember_token属性が扱えるようになった)def remember self.remember_token = User.new_token update_attribute(:remember_digest, User.digest(remember_token)) end最後の(remember_token)はself.が省略されています。
9.1.2 ログイン状態の保持
BCrypt::Password.new(remember_digest) == remember_tokenこのコードをじっくり調べてみると、実に奇妙なつくりになっています。bcryptで暗号化されたパスワードを、トークンと直接比較しています。(rails tutorial 9章より引用)
ふむふむ、確かに、、、
==で比較する際にダイジェストを復号化しているのでしょうか。
しかし、bcryptのハッシュは復号化できないはずなので、復号化しているはずはありません。(rails tutorial 9章より引用)なるほど、、では何が起こっているのでしょう?
BCrypt::Password.new(remember_digest).is_password?(remember_token)どうやら上の2つのコードは同じ意味のようです。
また、is_password?メソッドでオブジェクトと渡された引数の検証をし、一致している場合trueを返しているようです。9.3.2 [Remember me]をテストする
また、リスト 9.24で定義したlog_in_asヘルパーメソッドでは、session[:user_id]と定義してしまっています。このままでは、current_userメソッドが抱えている複雑な分岐処理を統合テストでチェックすることが非常に困難です。(rails tutorial 9章より引用)
ん?なぜでしょう?
A.
統合テストではsessionメソッドが扱えないからtest/test_helper.rbdef log_in_as(user) session[:user_id] = user.id #使えない end
test/helpers/sessions_helper_test.rbtest "current_user returns right user when session is nil" do assert_equal @user, current_user assert is_logged_in? endこのcurrent_userとは
current_userメソッドが実行された上でモデルオブジェクトとなっているようです。多分、、、つまり
log_in user
なども行われており、sessionメソッドも実行されているのだと思います、、、app/helpers/sessions_helper.rbdef current_user if user_id = session[:user_id] #false @current_user ||= User.find_by(id: user_id) elsif user_id = cookies.signed[:user_id] #true user = User.find_by(id: user_id) if user && user.authenticated?(cookies[:remember_token]) log_in user #ログインされる @current_user = user end end endcurrent_userメソッドは戻り値にモデルのインスタンスを返すモデルオブジェクト。
test/helpers/sessions_helper_test.rbtest "current_user returns nil when remember digest is wrong" do @user.update_attribute(:remember_digest,User.digest(User.new_token)) assert_nil current_user end@user.update_attribute(:remember_digest, User.digest(User.new_token))remember_digestを新しいものに更新。
assert_nil current_userは
app/helpers/sessions_helper.rbdef 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]) #falseとなりcurrent_userはnilとなる log_in user @current_user = user end end endif user && user.authenticated?(cookies[:remember_token])こちらの検証に置いてcurrent_userがnilとなるか確かめています。
終わりに
こちらの章ではエラーで躓くことも少なく、内容もよく理解出来ました。