KazumaLab.

流行りとリラックマと嵐が大好きです。

collection_check_boxesを使ったときにconfirmationのページをどう実装するか

かずまです。

今回collection_check_boxesを使って、formを作っていたときにハマったのでメモします。 ただ、この実装は本当に正しいかは微妙です。参考までにです。

ページの流れ

new -> confirmation -> complete

となっていて、ModelController内にそれぞれのメソッドを生やしています。

blog.kazumalab.com 前回の続きで、has_manyの関係のチェックボックスは簡単に扱えるというメモをのこしました。

collection_check_boxes

複数のチェックボックスを簡単に実装してくれるHelperです。

= f.collection_check_boxes :child_model_ids, ChildModel.all, :id, :name

こんな感じで実装したとします。 もちろん確認ページではチェックボックスは出さないのでhiddenにするのですが、 ただhiddenにするだけはダメでした。

フォームのパラメータとしては

child_model_ids["", "1", "2"]

この形で欲しいわけです。 それに合わせて、

= f.hidden_field :child_model_ids, multiple: true, value: nil
- @model.child_models.each do |child_model|
  = f.hidden_field :child_model_ids, multiple: true, value: child_model.id

こう書いてあげればできました。 multiple: trueを書かないと配列にしてくれないのでつけます。 これでcollection_check_boxesのhiddenを実装ができたと思います。

追記

value: nilを追加する必要がありました。

こんな感じ! なにか間違いがあればご指摘ください!

has_manyの関係でCheckboxを扱うのが超便利だった。(知らなかった)

かずまです。

今日色々1対多な関係で、Checkboxで複数選択させたい場合、 結構面倒だと思ってたのですが、意外となにもしなくてRailsすごい!ってなったのでメモしておきます。

使うのはcollection_check_boxesを使います。

なお、今回は

  • メインのモデルをModelとする
  • ModelはAssociationModelを複数持つ

を前提とします。

Viewでゴニョゴニョ定義します。(引数2つ目のインスタンスは適宜Controllerで作るなりしてください。) すると、checkboxが現れます。

# model/new.html.haml
= f.collection_check_boxes :association_models_ids, AssociationModel.all, :id, :name

あとはControllerのStrong Parameterで

params.require(:model).permit(
    ....
    association_model_ids: []
)

このようなにidの配列をとればOK! あとはいつも通りインスタンスを生成するときにparamsを渡すと中間テーブルを発行してくれる。 便利!

無駄なコードを書かなくても済むのはいい!

結構やりそうなこと

  • 複数のIDをeachなどでぶん回してbuildするとか?(まず今回これやってました)
  • accepts_nested_attributes_forを使う?

参考サイト

ちょっと使い方までは…というところ。 railsguides.jp

とりあえずドキュメントを見ればよかったと…。 だけど公式ではUpdateの例が上がってて、ほんとにこれでいいの?ってなったので実際にやってみました。 ActionView::Helpers::FormOptionsHelper

Lint/AmbiguousBlockAssociationで怒られたお話

かずまです。

Railsで開発していて、Haml-lintで怒られたので、その対処法のメモを残しておきます。

今回怒られたケース

  • formの中のSelect box
  • i18nを使いたいからmapを使う
= f.select :season, Model.seasons.map { |k, _| [t("activerecord.enum.model.season.#{k}"), k] }

こんな感じで書くと、怒られる。

Lint/AmbiguousBlockAssociation: Parenthesize the param
Model.seasons.map { |k, _| [t("activerecord.enum.model.season.#{k}"), k] }
the block will be associated with the Model.seasons.map method call.

ググっても出てこないので、解決してみる。

解決方法

selectとmapのメソッドが曖昧になってるからselectの後ろに()入れろよみたいな感じみたいなので、

= f.select(:season, Model.seasons.map { |k, _| [t("activerecord.enum.model.season.#{k}"), k] })

こうやってすればOK!
これでhaml-lintに怒られないで済みます。

ちゃんとエラーを読んでもなかなかわからない、ググっても出てこないのは結構つらいです。

集中力が切れたので対応してみた 等々力渓谷編

かずまです。

今日は朝モックとスタブについて記事を書いていたのですが、
集中力が切れたので16:00から一人で自然と戯れにいきました。

blog.kazumalab.com

ちなみにこのタイトルはこのブログを参考(パクった)にしています。
ぜひ箸休めにどうぞ!

codehex.hateblo.jp

15:00 ~ 下調べ

今日は朝から川越に行こうかな〜と思っていたのですが、残念ながらゴロゴロしていたら、
11:00ぐらいまで寝てしまっていました。残念。


ですので少しコードをカタカタ書いて、記事書いて〜ってやってたのですが、
集中力が切れてしまいました。


「どこに行こう...」

ということでGoogle検索

「東京 日帰り 一人旅」

検索しました。笑

そこで出てきたのが、「等々力渓谷」でした。

retrip.jp
このサイト。結構便利で色々使ってます。

世田谷のど真ん中にある自然の場所。
家からは乗り換え一回という楽さで行くことができました。

16:00~ 実際にいってみた

ゴルフ橋

まずは入ってすぐに大きな橋があります。
ゴルフ橋というそうです。昔は木製だったみたい。

f:id:kazumalab:20170820213207j:plain

等々力不動尊本堂

奥へ進むと等々力不動尊本堂へ続く道があります。
ここがまた雰囲気のあるライトで照らされていてきれいでした。
f:id:kazumalab:20170820213214j:plain

帰り道と川

帰りに撮影したのですが、ゴルフ橋を望みながら自然の音を堪能できました。
THE・夏という感じの場所でリフレッシュできました。

f:id:kazumalab:20170820213218j:plain

川が流れていて、水の音もまた、涼しさを運んできます。
対抗の人が来ると川に落ちそうで少しドキドキします。笑

いつもは音楽を聞くのですが、ここにいると音楽は必要ありませんでした。

最後に

リフレッシュした、良い週末でした。
なお写真は結構いい感じにしてありますが天気が良ければもっと良かったです。

いつもPCとにらめっこしているのでたまには自然とこうやって戯れるのもいいな
と感じました。次回は富士山とか川越あたりをブラブラしたい!(集中切れたとき!)

mockとstubが難しい件 RSpec 3.6

かずまです。
いろいろ考えることや、やれることも増えてきて楽しい半面悩ましいです。

Rubyをテストする際にmini testやRspecを使って検証することが多いと思います。
その中でもメソッドをモックする、スタブにするということを覚えました。

追記

PM兼PM on Twitter: "@kazumalab つ https://t.co/Wfvu46XrAa
これ聴くと、もうちょっと視野が広がりそうな一方、混乱するかも笑"

https://ajito.fm/6/

Mockの知識が広がるのを教えて頂きました。

ここのT-wadaさんとの対談を聞いてmockの使い方の歴史を知り感動しました。

たしかにMockはクラスの設計時に使うというのは興味深いし、使えそうです。

僕らが使ってたのはtest doubleのサブクラスのmockだったんですね。勉強になりました。

モックとスタブの概念

結構難しいので説明はできませんのでこれの回答っぽいリンクを貼っておきます。

TDD > モック / スタブ - Qiita

どちらとも共通しているのが、「あるオブジェクトのメソッドが呼ばれたときに
実際にそのメソッドを実行せずに自分で返ってきてほしいものを指定する。」というところだと思っています。

今の考えはこれですね。

ただし、モック、スタブにする部分はそのオブジェクトの振る舞いがすでにテストされていて暗黙的に理想とするデータを返す状態になっていることを前提としていると思っています。つまり、テストをしているメソッドのテストが完璧だったとしてもモックしたメソッドが不十分な振る舞いであれば足を引っ張るのはそのメソッドになるということです。

なので、それぞれのメソッドに対してユニットテストは書くべきだと思っています。

どうやって使うのか?

すでに使っているのであればこれ以降読む必要は無いと思います。
わかりやすいレシピ集を載せておきます。(3.3ですが。)

dev.classmethod.jp


僕が使う例があっているかどうかという部分は確かではありませんが、
次の例を出してサンプルとします。

ex. Railsだとcurrent_userにログインしているユーザー情報が入る場合のお話

ApplicationController.current_userで返ってくるものがnilのときはログインしていない状態、
userが返って来ればログインしている状態です。

class ApplicationController
  def current_user
    # return login user
  end
end


次のようなテストを書いたとします。
この場合、userの詳細ページにアクセスできればテストはsuccessです。

しかしUserControllerのbefore_actionにはログインしているかチェックするメソッドをセットしているので、
このテストは失敗します。

そこでテストでもログインさせる処理が必要です。
もちろん、sign_in_pathにユーザーのログイン情報をpostしてログインするのもありだと思います。
ただ、ログインするテストをしていれば、データをわざわざpostしなくてもいいわけです。(?)

今回はApplicationControllerのインスタンスメソッドをモック、もしくはスタブ化するので、
次のように書いていきます。

relishapp.com

Mock化はexpect

Stub化はallow

2つの違いは難しいのですが、

jangajan.com

ココらへんいいかと思います。
メソッドが呼ばれることを検証したい場合はexpect、
しない場合はallowでいいようです。

もちろんexpectを使うとcurrent_userが呼ばれない場合は
このテストは失敗します。
ちなみに、expectを使うと何度呼ばれたか?というのも検証できるので便利です。

relishapp.com

onceとかtwiceを使うのですね。

他にも

  • test_double
  • double
  • spy

などといった、インスタンス自身をモック、スタブ化するものもあるようです。
relishapp.com

テスト時にインスタンスを生成し、
引数にオブジェクトを渡して、そのメソッド内で渡したインスタンスのメソッドを呼ぶ場合などに
test_doubleを使って、そのメソッドをモックすることができます。

ここをみるとdoubleはtesto_doubleをincludeしてます...。
何をやっているかは深く読んでないのでここでは何も言えません。
ちょっとあとで読んでみます。

https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/test_double.rb


さて、実際にスタブ化してみます。
今回はメソッドが呼ばれたことを検証しないのでallow_any_instance_ofを使います。

これでテストが成功しました。

最後に

今回は例としてログインの部分をスタブ化して、擬似的にログインしてる形を作りました。
いい例だとは思えませんが、こんな感じでできる!と言うのはつかめると思います。

Gemを作る場合やAPIを使う場合に外部との通信が入ってしまったりする部分にmockを使ったりします。
APIなどはとくにAPIサーバー側でアクセス回数制限を設けている場合がほとんどなのでテストで消費するのはもったいないですよね。

OauthなどはMockするためのものが用意されているのでそれを使うといいですね。
他にも

github.com

VCRという一度だけ外部と通信を行って、それをymlにダンプしてそれ以降はそれを使って
テストを実行していくという便利なgemもあります。
まだ使ったことはありませんが、mockデータを作るのは面倒なので、一度使ってみたいものです。

github.com
webmockというそもそもhttp accessをもモックしてしまうものもあるので
世の中便利ですね。

間違っている部分や疑問に思っていることがありましたら、
かずま (@kazumalab) | Twitter
か、こちらのコメントまでお寄せください。