kazumalab tech log

流行りとリラックマと嵐が大好きです。技術的ログ。

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を比較してnot nilだった場合その値を返す
  • @textがnilだった場合は"hello"を@textに代入して@textの値を返す

つまりは@textnilの状態で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は代入されないみたいです。(仕様?)

次に呼ばれたのが、authorsaveするときに呼ばれました。

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への問い合わせが一回で済みますよね。 そういったようにもう少し効果のわかりやすいのにして計測すればよかったですね。

明日のアドカレは!

@tastuoさんのAWS Lambda Layersを使ってみた!(Python)です!

【すいはんき工房】Youtuberをはじめました!

こんばんは!

かずまです。

 

最近更新できてなかった技術ブログですが、ここ最近ずっとYoutuberについて研究しておりました。笑

  つまりタイトルにも書いた通りYoutuberをとりあえず頑張ってみることにしました!🙌

 

チャンネル名は

【すいはんき工房】

www.youtube.com

珍しい開発系Youtuberとして活動します。エンジニアリングがわからない、プログラムがわからなくても楽しく見れるように作っています。もちろんわかる人にとってはより面白い動画になるよう頑張ります。

是非ともチャンネル登録してみてください!

最近の動画はこちら!

バンバンあげて行くのでよろしくお願いします!

InstagramTwitterもやってます!お見逃しなく! www.instagram.com

twitter.com

Webサービスを作る時にどれだけモチベを維持出来るかということ

かずまです。
今日は結構ふわっとしたお話です。
というより、最近「こういうサービスあったらいいな」と思って作ろうとするのですが、
モチベが続かず、ぼくのGithubにfirst commitと書いたcommitだけを残したリポジトリがあちらこちらに散らばっています。

サービスをいかに価値のあるものにするかというのは初歩のアイデアも大事ですが、
なにより作りきること、継続的に開発を進めていくことだと思っています。

これってゲームを作るのも同じ感覚でした。
なので、ゲームジャムとかで作ったものってシンプルだけど作りきるという意思で作っているから結構面白いものができるし、
そこに価値があると思うのです。

まぁゲームジャムはそこで終わっちゃうと残念ですが、
継続して続けられるならなおいいですよね。

最近作ろうと思ったものの例

  • 匿名性のSNS

これは完全匿名制で、情報をPOSTした際にそれに関連するタグを発行します。
そのタグをフォローしている人にその情報が届くシステムで、ユーザーに優位性を持たせて、自分が情報を投稿すればするほど
もらえるデータも増えるというサービスです。(Amyee)

続かなかった原因

  • セットアップ、設計に時間がかかってしまうこと。

WebサービスだとUser(ログイン)を必要とするサービスが多いです。最近だとOauthも標準的にログイン、会員登録として使われます。

個人で開発するときはRailsが多いのですが、
毎回ログイン周りをやろうとするとだるくてやめてしまう原因となります。

解決策

RailsではApplicationTemplateといった機能があります。
毎回これ入れるし、自動で作ってくれい!みたいな感じです。

それでさっとログインまでを作ってしまえば!と思ったりもします。

ginzarb.github.io

ここで@onkさんが話してたのを聞いて、いいなと思ったのですが、未だできてないのです...

  • 新しいことをやろうとする

これは今回のアプリケーションで使おうとしていた、PWAとか、技術的にはRailsAjaxだけでも全然できるんだけど、
ネイティブアプリっぽいデザインにしちゃったからPWAを使って通知とかしてみる?とか
Sinatoraで書くときにいつもは使わないのにWebpackerを使ってみたりとか
結構やらなくてもプロトタイプは作れるぞ!というのをいいたいのです(自分に)
ただ興味をもってしまうとたとえできなくても面白いのでやっちゃうのが現状です。
そして、導入できたら満足してしまう、もしくは上手く動かなくてサービス開発すら断念してしまう。

解決策

これは難しいと思います。と同時に引き合いだと思います。
今必ず必要な技術であれば、やる優先度を上げて、サービスがたとえできなくとも技術が学べただけでも大きいといったことであればどんどん挑戦していくべきだと思います。ただ、思いつきのサービスは実際作ってみると大したことないことがほぼほぼなので、簡単に作れる、一行でも書くコードが少なければ少ないほどいいですよね。

どっちがいいか、エンジニアとして幅を広げていくなら前者、サービスをひたすら作りたいのであれば後者みたいな感じかなぁ。
いい案があればコメントください。

最後に

最近こういう一人で作業しようとすると眠気くんが誘惑してきます。
布団の世界にこないか?こっちの世界は最高だぞと。

要はサービスを作る上で最も大事なのはある程度の技術は必要ですが、
モチベーションと健康は大切ですね...。

朝早く起きて、集中できる状態で、、、と思ったりして実行してみましたが、
9:00出社だ、あと2時間しかダメじゃん!みたいなことを考えるとかえって開発が進まなかったりしました。
自分にとって最高に集中できるのってなんだろうってお休みの間に考えたりするのもいいですね。

リモートでもローカルでもペアプロが行えるサービス"Pepro(β版)"をリリースしました

こんにちは。 かずまです。

saku氏とともにコツコツ作っていた、ペアプログラミングが行えるサービスをリリースしました。 それがPeproです。

www.pepro.me

まだまだスタートして間もないサービスですが、 ぜひとも使ってみてください。 * なお、若干つながりが悪いかもしれません。そういう場合はプログラムをCommand + Sで保存して、お互い再読込してみてください。

ペアプロとは

ペアプログラミングの略で、基本的に2人体制でプログラミングを行う開発手法です。 実際にやったことありますが、結構いいです。

  • めんどくさいところも絶対に進む
  • 詰まっているところが明確になる
  • その場でレビューもしてもらえる
  • お互いの開発のいい癖を盗める

デメリットとしては

  • 片方の作業が止まる
  • 1人じゃできない

が挙げられると思いますが、これら以外ではあまりデメリットはないイメージです。

Pepro

Peproの主な機能としてはペアプログラミングで大事な

  • 声に出すこと
  • 指差しすること
  • 一つのコードを一緒にみること

これらの機能を備えています。

声に出すこと

同じルームに入ると基本的に音声通話が始まります そこで話をしながら進められます。

指差しすること

今、ここのコードのことを言ってるんだよ!という確認や指示のときに使います。 基本的には画面をクリックしたところにポインターが当たるようになっています。

一つのコードを一緒に見る

Pepro を開くとエディタが一つだけあります。 片方がプログラムを書き始めると相手側の画面にもプログラムが同期されます。 (本来は同じ画面ですが...)

これでペアプログラミングを行うことができます。

おまけ

ペアプログラミングは喋って、頭を使って、プログラムを書きます。 その為、疲れてしまうのではないでしょうか?

Peproのルームを作る際にウェイティングページを準備しておりますので、その間にコーヒー等を一杯お持ちくださいませ。 準備ができ次第、ルームに入りましょう!

運営

Pepro開発ブログ

サービスURL

https://www.pepro.me

私にペアプロして!という感じの意味合い...!!

RailsでCSRFを無効にせずにAjaxからRESTなAPIを叩く

かずまです。

ここ最近Railsでサービスを作っている時にJavascriptをよく使います。 そのときにうーんこれはどうすれば良いのだろうと思ったので自分用にメモします。 初歩的なお話です。今回CSRFについては長くなりそうなので省略します。

始まり

  • 同じリソース内に定義したRESTのAPIJavascriptから叩きたい
  • RailsCSRF対策がされているので、AjaxでPOSTなどは400(InvalidAuthenticityToken)で弾かれる
  • RailsのController側でCSRFを無効に出来るが、あまり使いたくない

今回はこんな感じの流れだったと思います。

対策方法

今日たまたまAjaxを使った部分があって、 気になっていたので調べてみました。

blog.willnet.in

あら。すぐにありました。 Willnetさんのブログ。いつもお世話になっている...!! しかも、9年前だった...。

= hidden_field_tag "authenticity_token", form_authenticity_token

これであとはJS側でgetElementByIdとかjQueryとかでとってきてdataの中に組み込んであげれば良いのでめっちゃ便利。

Rails5になって...

techracho.bpsinc.jp

Formごとにトークンを発行できる仕様にできるみたい。 これで攻撃被害をすこしでもすくなくできるっぽいですね。