Railsにおけるメモ化についてちょっと覗き見してみる
こんにちは! みんなのウェディングのエンジニア@kazumalabです。 この記事はくふうカンパニーアドベントカレンダーの3日目になります。
Rubyにおけるメモ化とは
||=
を利用して、オブジェクトのキャッシュを行うことができます。
例えば、
def say "hello" end
この場合say
メソッドを呼ぶ場合、振る舞いは同じなのに毎回違うStringクラスのインスタンスが生成されてしまいます。
確認してみます。
irb(main):010:0> say.__id__ => 70107590117540 irb(main):011:0> say.__id__ => 70107599432980
どうでしょうか。それではメモ化してみます。
def say @text ||= "hello" end
このメモ化の流れの振る舞い以下のようになっています。
つまりは@text
がnilの状態でsayメソッドが呼ばれると@text
に"hello"が代入されて、それ以降その@textに入っているオブジェクトを呼び続けるということになります。
irb(main):015:0> say.__id__ => 70107599401820 irb(main):016:0> say.__id__ => 70107599401820 irb(main):017:0> say.__id__ => 70107599401820
何度呼び出しても同じオブジェクトのIDが返ってくることがわかります。
Railsでメモ化を使うケース
まずはメモ化をどういったところで使うのかを考えてみます。
- Formオブジェクトのgetter
- Decoratorパターンでメール送信をするときなど
- APIを利用するとき
いろんなユースケースがあると思います。 さて、Railsでメモ化するとどれぐらいいい感じになるんでしょうか。
Attributesの呼ばれる回数
今回のケースとして、Formオブジェクトで利用する場合を例題にあげてみます。
Book
モデルとAuthor
モデルがあり、それを同時に生成するFormオブジェクトを以下のように記述してみます。
class BookForm include ActiveModel::Model attr_writer :name attr_accessor :title validates :title, :name, presence: true def save return unless valid? ActiveRecord::Base.transaction do book.save! author.save! end end def name @name ||= "NoName" end private def book @book ||= Book.new(title: title) end def author @author ||= book.authors.build(name: name) end end
作者名を入力しない場合はNoNameを入れる仕様にしています。
流れ
/books/new
にアクセス/books
にデータをPOST/books/:id
にリダイレクト
/books/new
にアクセス
さて、この作者の名前を返すnameメソッドにbinding.pry
を仕組み、動きをみてみます。
まず止まったところは/books/new
にアクセスしたときです。
/books/new
ではform_with
を利用した以下のようなフォームを設置しています。
= form_with model: @book_form, url: books_path, method: :post, local: true do |f| - if f.object.errors.present? - f.object.errors.full_messages.each do |message| %p= message = f.label :title = f.text_field :title = f.label :name = f.text_field :name, value: @book.name = f.submit
text_fieldメソッドの中でAttributesが展開されているので、ここでまずは一回呼ばれました。この時点で@name
の中身はnilなのでメモ化されることになります。
exit
でpryを抜けると/books/new
が表示されました。
しかし、ここで一旦このオブジェクトの寿命は以上になります。
/books
にデータをPOSTする
さて、データを入力して、POSTをします。
Controllerでパラメータを受け取り、BookForm
のインスタンスを生成します。
その後保存するためのsave
メソッドを呼びますが、その中でvalidationが走ります。
def save return unless valid? # here ActiveRecord::Base.transaction do book.save! author.save! end end
まずはそこで呼ばれました。まぁそうですね。nil
だった場合はここでNoName
が代入されることになりますが、今回の検証で初めてわかったのですが、空文字だった場合はNoName
は代入されないみたいです。(仕様?)
次に呼ばれたのが、author
をsave
するときに呼ばれました。
def author @author ||= book.authors.build(name: name) end
空文字だった場合はメモ化が使えないのは以外でした。
このオブジェクトはバリデーションに失敗した時にはrender :new
を呼ぶため、/books/new
にアクセスしたときと同じ回数がプラスで呼ばれることになりそうです。今回の場合、validationに失敗した場合も、しなかった場合も3回呼ばれました。
最後に
(時間が間に合わなかったー) 今回の検証方法ではあまりメモ化の良さみたいなところがわかりにくかったと思います。 例えば
def book @book ||= Book.find_by(title: title) end
みたいな感じでメモ化するとViewで何度も呼ばれてもDBへの問い合わせが一回で済みますよね。 そういったようにもう少し効果のわかりやすいのにして計測すればよかったですね。
明日のアドカレは!
【すいはんき工房】Youtuberをはじめました!
こんばんは!
かずまです。
最近更新できてなかった技術ブログですが、ここ最近ずっとYoutuberについて研究しておりました。笑
つまりタイトルにも書いた通りYoutuberをとりあえず頑張ってみることにしました!🙌
チャンネル名は
【すいはんき工房】
珍しい開発系Youtuberとして活動します。エンジニアリングがわからない、プログラムがわからなくても楽しく見れるように作っています。もちろんわかる人にとってはより面白い動画になるよう頑張ります。
是非ともチャンネル登録してみてください!
最近の動画はこちら!
バンバンあげて行くのでよろしくお願いします!
Instagram、Twitterもやってます!お見逃しなく! www.instagram.com
Webサービスを作る時にどれだけモチベを維持出来るかということ
かずまです。
今日は結構ふわっとしたお話です。
というより、最近「こういうサービスあったらいいな」と思って作ろうとするのですが、
モチベが続かず、ぼくのGithubにfirst commitと書いたcommitだけを残したリポジトリがあちらこちらに散らばっています。
サービスをいかに価値のあるものにするかというのは初歩のアイデアも大事ですが、
なにより作りきること、継続的に開発を進めていくことだと思っています。
これってゲームを作るのも同じ感覚でした。
なので、ゲームジャムとかで作ったものってシンプルだけど作りきるという意思で作っているから結構面白いものができるし、
そこに価値があると思うのです。
まぁゲームジャムはそこで終わっちゃうと残念ですが、
継続して続けられるならなおいいですよね。
最近作ろうと思ったものの例
- 匿名性のSNS
これは完全匿名制で、情報をPOSTした際にそれに関連するタグを発行します。
そのタグをフォローしている人にその情報が届くシステムで、ユーザーに優位性を持たせて、自分が情報を投稿すればするほど
もらえるデータも増えるというサービスです。(Amyee)
続かなかった原因
- セットアップ、設計に時間がかかってしまうこと。
WebサービスだとUser(ログイン)を必要とするサービスが多いです。最近だとOauthも標準的にログイン、会員登録として使われます。
個人で開発するときはRailsが多いのですが、
毎回ログイン周りをやろうとするとだるくてやめてしまう原因となります。
解決策
RailsではApplicationTemplateといった機能があります。
毎回これ入れるし、自動で作ってくれい!みたいな感じです。
それでさっとログインまでを作ってしまえば!と思ったりもします。
ここで@onkさんが話してたのを聞いて、いいなと思ったのですが、未だできてないのです...
- 新しいことをやろうとする
これは今回のアプリケーションで使おうとしていた、PWAとか、技術的にはRailsとAjaxだけでも全然できるんだけど、
ネイティブアプリっぽいデザインにしちゃったからPWAを使って通知とかしてみる?とか
Sinatoraで書くときにいつもは使わないのにWebpackerを使ってみたりとか
結構やらなくてもプロトタイプは作れるぞ!というのをいいたいのです(自分に)
ただ興味をもってしまうとたとえできなくても面白いのでやっちゃうのが現状です。
そして、導入できたら満足してしまう、もしくは上手く動かなくてサービス開発すら断念してしまう。
解決策
これは難しいと思います。と同時に引き合いだと思います。
今必ず必要な技術であれば、やる優先度を上げて、サービスがたとえできなくとも技術が学べただけでも大きいといったことであればどんどん挑戦していくべきだと思います。ただ、思いつきのサービスは実際作ってみると大したことないことがほぼほぼなので、簡単に作れる、一行でも書くコードが少なければ少ないほどいいですよね。
どっちがいいか、エンジニアとして幅を広げていくなら前者、サービスをひたすら作りたいのであれば後者みたいな感じかなぁ。
いい案があればコメントください。
最後に
最近こういう一人で作業しようとすると眠気くんが誘惑してきます。
布団の世界にこないか?こっちの世界は最高だぞと。
要はサービスを作る上で最も大事なのはある程度の技術は必要ですが、
モチベーションと健康は大切ですね...。
朝早く起きて、集中できる状態で、、、と思ったりして実行してみましたが、
9:00出社だ、あと2時間しかダメじゃん!みたいなことを考えるとかえって開発が進まなかったりしました。
自分にとって最高に集中できるのってなんだろうってお休みの間に考えたりするのもいいですね。
リモートでもローカルでもペアプロが行えるサービス"Pepro(β版)"をリリースしました
こんにちは。 かずまです。
saku氏とともにコツコツ作っていた、ペアプログラミングが行えるサービスをリリースしました。 それがPeproです。
まだまだスタートして間もないサービスですが、
ぜひとも使ってみてください。
* なお、若干つながりが悪いかもしれません。そういう場合はプログラムをCommand + S
で保存して、お互い再読込してみてください。
ペアプロとは
ペアプログラミングの略で、基本的に2人体制でプログラミングを行う開発手法です。 実際にやったことありますが、結構いいです。
- めんどくさいところも絶対に進む
- 詰まっているところが明確になる
- その場でレビューもしてもらえる
- お互いの開発のいい癖を盗める
デメリットとしては
- 片方の作業が止まる
- 1人じゃできない
が挙げられると思いますが、これら以外ではあまりデメリットはないイメージです。
Pepro
Peproの主な機能としてはペアプログラミングで大事な
- 声に出すこと
- 指差しすること
- 一つのコードを一緒にみること
これらの機能を備えています。
声に出すこと
同じルームに入ると基本的に音声通話が始まります そこで話をしながら進められます。
指差しすること
今、ここのコードのことを言ってるんだよ!という確認や指示のときに使います。 基本的には画面をクリックしたところにポインターが当たるようになっています。
一つのコードを一緒に見る
Pepro を開くとエディタが一つだけあります。 片方がプログラムを書き始めると相手側の画面にもプログラムが同期されます。 (本来は同じ画面ですが...)
これでペアプログラミングを行うことができます。
おまけ
ペアプログラミングは喋って、頭を使って、プログラムを書きます。 その為、疲れてしまうのではないでしょうか?
Peproのルームを作る際にウェイティングページを準備しておりますので、その間にコーヒー等を一杯お持ちくださいませ。 準備ができ次第、ルームに入りましょう!
運営
サービスURL
私にペアプロして!という感じの意味合い...!!
RailsでCSRFを無効にせずにAjaxからRESTなAPIを叩く
かずまです。
ここ最近Railsでサービスを作っている時にJavascriptをよく使います。 そのときにうーんこれはどうすれば良いのだろうと思ったので自分用にメモします。 初歩的なお話です。今回CSRFについては長くなりそうなので省略します。
始まり
- 同じリソース内に定義したRESTのAPIをJavascriptから叩きたい
- RailsはCSRF対策がされているので、AjaxでPOSTなどは400(InvalidAuthenticityToken)で弾かれる
- RailsのController側でCSRFを無効に出来るが、あまり使いたくない
今回はこんな感じの流れだったと思います。
対策方法
今日たまたまAjaxを使った部分があって、 気になっていたので調べてみました。
あら。すぐにありました。 Willnetさんのブログ。いつもお世話になっている...!! しかも、9年前だった...。
= hidden_field_tag "authenticity_token", form_authenticity_token
これであとはJS側でgetElementByIdとかjQueryとかでとってきてdataの中に組み込んであげれば良いのでめっちゃ便利。
Rails5になって...
Formごとにトークンを発行できる仕様にできるみたい。 これで攻撃被害をすこしでもすくなくできるっぽいですね。