blog.waterlow.work

Ruby, Rails, js, etc...

Effective Rubyを読んで気になったこと(reduce・each_with_object・inject)

途中までですが、Effective Rubyを読んでみました。

Effective Ruby

Effective Ruby

「項目19 reduceを使ってコレクションを畳み込む方法を身に付けよう」を読んだとき、まず恥ずかしながらreduceというメソッドを知らなかったです。injectは知っていたのですが、そのエイリアスだそうです。

injectreduce)と聞くと、自分ならeach_with_objectを使うかなと思いました。細かいですがinjectだとアキュムレータを返す必要があるからです。 こんなかんじです。

[1, 2, 3, 4].inject([]) do |nums, num|
  nums << num * 2
  nums
end

each_with_objectだと副作用的に更新していってくれるのでその必要はありません。

[1, 2, 3, 4].each_with_object([]) do |num, nums|
  nums << num * 2
end

視点を変えてみる

そもそもinjectreduceには発想の違いがあるようです。(非常にわかりやすい図とともに説明されています。 Rubyist Magazine map と collect、reduce と inject ―― 名前の違いに見る発想の違い

reduceを使うということは、「このコレクションの畳み込みをします。」というのをメソッドで自己ドキュメント的に宣言できるということです。(最低現reduceの単語の意味は知っておく必要がありますが…。)それに対しeach_with_objectは「コレクションのループと同時にこのobjectを更新していきましょう」ということしか読み取れません。畳み込む場合は、多少記述が増えてもreduceを使うのがいい気がしました。

結論

畳み込みする(要素を足しあわせたり、条件に応じて捨てたりする)ときはreduce、そうでなく、例えばオブジェクトのArrayからHashに変換するみたいな場合はeach_with_objectを使うのがよさそう。injectはどうしよう><

【番外編】ニッポンのジレンマ

技術関係なしです。

ニッポンのジレンマという番組のスペシャルが年始1/1にやっていたので見ました。
どんな番組かというと、1970年生まれ以降の人が集まってあるテーマに沿って議論するというもの。真剣10代しゃべり場(死語?)のターゲット変えた版のようなものでしょうか。
放送は月一で、次は2月に放送されるようです。

議論を見ているのはすごく面白かったのですが、やはり全員がそれぞれの見地で発言をして他の人の発言を聞いても最終的に「へぇ〜」で終わっているような感じがしたのでちょっと残念でした。
また、結局今後どうすりゃいいの?という問いに対する答えが得られなかったので、やっぱり自分なりに意見を持たないと、と思いました。
収録後インタビューにそれぞれの人の意見がある程度まとまっています。

あとウォンテッドリの仲暁子さんが出ていて、転職しようと思い始めた頃とか、とりあえずRailsやるためにMac買ってみよとか思った頃を思い出しました。
Wantedly 航海日誌 — 私のようなの素人のためのHacker Way
Wantedlyの仲暁子さんのTEDでのスピーチが感動的 | No:711 |

今年も頑張ろう!

Railsのソースレビューで印象に残ったこと

link_toimage_tagを使おう。

erbやhamlでは(slimも?)link_toを使わずとも以下のようにコードを埋め込めます。

<a href="<%= url>">"><%= text %></a>
%a{ href: url}= text

しかしながらモデルのインスタンスを渡すだけでパスを作ってくれたり、config/routesのasの指定を取得したりと、いろいろ便利な機能があったり、また習慣的にもビューヘルパを使って各事が多いため基本はビューヘルパを使いましょう。

・dbへの問い合わせを減らそう

モデルが以下のように定義されているとします。

class Contact < ActiveRecord::Base
  has_many :phones
end

includesを使うと以下のように後から問い合わせるsqlを先に発行するらしい(合ってる?) 以下の例だとほとんど変わらないがContactが1万件とかあると大きく違う。

irb(main):001:0> Contact.all.each { |contact| contact.phones.each { |phone| puts phone }}
  Contact Load (0.1ms)  SELECT "contacts".* FROM "contacts"
  Phone Load (0.1ms)  SELECT "phones".* FROM "phones"  WHERE "phones"."contact_id" = ?  [["contact_id", 1]]
  Phone Load (0.1ms)  SELECT "phones".* FROM "phones"  WHERE "phones"."contact_id" = ?  [["contact_id", 2]]
  Phone Load (0.1ms)  SELECT "phones".* FROM "phones"  WHERE "phones"."contact_id" = ?  [["contact_id", 3]]
irb(main):002:0> 
irb(main):003:0> Contact.includes(:phones).each { |contact| contact.phones.each { |phone| puts phone.phone }}
  Contact Load (0.1ms)  SELECT "contacts".* FROM "contacts"
  Phone Load (0.2ms)  SELECT "phones".* FROM "phones"  WHERE "phones"."contact_id" IN (1, 2, 3)

・遅延評価を知っておこう

@users = User.allのようにモデルのインスタンスを代入すると@users.each{}のように@usersメソッドを実行するときに初めてsqlが発行される。Userが膨大にいる場合は(やらないと思うけど)先に条件を絞った上でeach等のメソッドを実行する。先にallで取ってからselectなんてことはしない。

少なくともこれらの周辺知識はしっかり勉強しておく必要がありそう。

私のよちよち.rb活用法

この記事はよちよち.rb Advent Calendar 2014 - Adventar11日目の記事です。
10日目はbonbon_0605さんによるSeleniumで明示的に要素検索の待ち時間を設定するでした。
Seleniumに対しては前々から使ってみたい気持ちと、怠け癖が均衡していたのですが、bonbon_0605さんの記事で使ってみたい気持ちが勝ち越しそうです!ありがたや。

書くこと

9月に転職して、同時に参加し始めた「よちよち.rb」について参加のいきさつと、参加して思ったこと、最近の自分のよちよち活用法(自分の中での位置づけ的な?)について書いていきます。

参加のいきさつ

よちよち.rbを知ったのは今年の4月頃だったと思います。Ruby on Rails チュートリアルをやっているということで興味があったのですが、募集のページに以下のような文言が…

大切なお願い

よちよち.rb のミートアップではグループワーク形式をとっていることから、原則として参加者全員が同じスタート地点に立った状態で当日の勉強を開始したいと考えています。
そのため、上記の項までは各自ですすめてあるという前提のもとでミートアップを実施します。ご協力をよろしくお願いします。

^^;
恐ろしくなってとりあえず見送りました笑
今思えばもっと早くに参加しておけばよかったと思います…。
そして9月に転職し、新天地で偉大なる先輩odailly_jpさんに出会い、おすすめのコミュニティを聞いた所、よちよちを紹介していただきました。
かくして恐れおののいていたよちよち.rbについに参加することになったのです…。

参加して思ったこと

まずよちよち.rbは恐ろしくない!個人的に理解しきれない所もみんなでペースを合わせて進めるし、環境構築等でこけていても相談に乗ってくれる人がたくさんいます。あとは疑問を上がった時に、みんなで考えようという空気がある。もっとごりごり進んで理解できないと置いて行かれるのかと思っていましたがそんなことはなかったです。

最近の私のよちよち活用法

このコミュニティの良さを最大限利用するため、超個人的なのですが最近は以下ののようなことを考えながら参加するようにしています。

背伸びをしていろいろ言ってみる

自信がないことでも「自分はこんなふうに理解しました・解釈しました」みたいなところはなるべく言うようにしています。そうすると他の人に突っ込まれたり、突っ込まれはしないまでも新しい理解の仕方が得られたりします。

悩むのOKの場所として

独学していると、ハマったり理解できなかったりするところが辛いので、本で理解できない所は努めてすっ飛ばすようにしています。ただ個人的には1ページずつしっかり悩みつくしつつ理解したいという気持ちもあるので、ペースがわりとゆっくりなこの勉強会で達成しています。

Railsチュートリアルを進める

当たり前ですが、ちょっとずつRailsチュートリアルを進める事ができます。一人でやっているとあまり理解せずにさくっと進めて満足してしまう。もしくはゆっくりやっていてあまりの長さに途中で挫折してしまう、といったことになりがちです。よちよちに参加している限りRailsチュートリアルが積本になることはありません。

最後に

他にも言語化出来ない恩恵がたくさんありそうですが、よちよち活用法についてまとめてみました。
書きながらですが、しっかり予習していくのも楽しいかなと思いましたので今度やってみようと思います。
みなさんがどんな気持ちで参加しているのかもぜひ知りたいですね!

明日は?

明日のよちよち.rb Advent Calendar 2014はta1kt0meさんの2014年反省会です。ta1kt0meさんが今年どんなことをしてきたのか興味がありますね!

【Ruby】【RSpec】The RSpec BookをRspec3系とTurnipで読み替え

yosemiteにアップグレードしたせいなのかわかりませんが、Ruby1.9.2以前のRubyがインストールできなくなってしまいました。RSpec Bookは1.9.2か1.8.7ベースで書かれているのですが、Rubyは2.1.3、RSpecは3.1.7、またCucumberではなくTurnipを用いてこの本を読んでみます。(挫折せず最後まで読めることを願って…。)
まだChap4あたりなのですが、気になった違いをまとめます。

RSpecバージョン差による違い

本の中でのRSpecは2.0.0を使っているので、全てshouldで書かれていますが、現在はexpectを使います。そのため、はじめのHello RSpecの例だと以下の様な読替えが必要です。

greeting.should == 'Hello RSpec!'

expect(greeting).to eq 'Hello RSpec!'

ただそこまで複雑ではないですし、以下のようなまとめもあるので乗り越えられそう。
Ruby - RSpecのshouldはもう古い!新しい記法expectを使おう! - Qiita

Cucumber→Turnipの読み替え

そもそもCucumberを使わない理由はるびまに書いてあったのと、ちょっと前まで現場でも使っていたのがかなり重たかったためです。ここが面倒かと思ったのですが、featureファイルはほとんど同じ記述で動作します。さすがCucumberの後継!
step定義に関しても似たような書き方で行けます。詳しくは以下を参照。
jnicklas/turnip · GitHub
Ruby - Turnip README 超意訳 - Qiita
Rubyist Magazine - エンドツーエンドテストの自動化は Cucumber から Turnip へ

Then /^I should see "([^"]*)"$/ do |arg1|
  pending
end

step "I should see :arg1" do |arg1|
  pending
end

greatですね!!

ちょっと悩んでるとこ。

ただひとつ読み進める上で注意というか疑問点というか。
chapter4の4.3でstep定義ファイルに直接classを以下のように追加している箇所がある。

class Output
  def mssages
    # ...
  end
  # ...
end

Cucumberの場合はstep定義以外の記載もすべて読み込まれるのでこれでOKなのだけど、Turnipの場合は勝手には読んでくれないので、以下のように明示的にRSpecに取り込む必要がある。

module Codebreaker
  class Output
  end
end
RSpec.configure { |c| c.include CodeBreaker }

この本の中ではOutputは標準出力のスタブのような役割をしているのだけど、そもそもstepファイルの中にこんな感じに加えてしまうのはオッケーなのかな??

【CoffeeScript】書き方いろいろメモ

最近はRubyよりもCoffeeScript使う時間のほうが長いwaterlowです。
触りつついろんなソースを読みつつしてるうちに書き方がなんとなくわかってきたので書き留めておきます。

極力かっこは省略したい!

理由:インデントで文が解析されるのをフル活用したい。
   ハッシュが非常にすっきりする。
   jQueryセレクタ使ってvalue変更…なんてときにたくさん括弧を書きたくない。

@sample = (id) ->
  $(document).on 'click', id, ->
    $.ajax
      url: @dataset.data
      data:
        prefecture_id: @value
      dataType: 'json'
      success: (data, status, xhr) ->
        $($(elememt).attr "data-data").html("add")

内容は適当ですが、ページで何かがクリックされた時にタグに持たせておいたurlに対してajaxリクエストを送るように、onメソッドで処理を登録しています。
まず、はじめのonメソッドの括弧省略はonメソッドの引数が(イベントハンドラ, セレクタ, 処理)とほぼ固まっているため括弧は省くべきかなと(railsのlink_toで括弧使わないのと同じイメージ?)
次に、ajaxメソッドにはハッシュを引数に渡すのですがメソッド呼び出しの括弧とハッシュの括弧の両方を省略しています。つまりこうも書ける。

$.ajax({
  url: @dataset.data
  data: { prefecture_id: @value }
  # ...
})

でも閉じ括弧のインデント位置を考えないといけなかったり、ハッシュにハッシュを渡すならインデントしたほうが見やすいかなと考えた末括弧を取っ払いました!

括弧の考え方はスタイルガイドによると「"readability" can be subjective」ということなので、一般解がないのが難しいですが…。

とりあえずthisが何かわかっておく

理由:分からないと要素にアクセスするのに周りくどい方法をとってしまうため
先ほどの例で行くと

url: @dataset.data

CoffeeScriptは@でメソッドの呼び出し元(jsでいうthis)にアクセスできるので、コードがスッキリかつthisへのアクセスが目印っぽくなってよいです。success以下の処理はthisが変わってしまうため同じ呼び出し方は出来なかった(気がする)。

data-*属性にはjQueryならdata()でアクセスできる

js全般ですが、先ほどのサンプルの最後の例は以下のようにも書けます。

$($(elememt).data "data").html("add")

短く書けるのでこちらを使ったほうがいい気がしますがHTML5 カスタムデータ属性「data-*」にJavaScript、jQueryからアクセスする方法 | Dress Cordingによると、dataを使うとデータの型変換が行われるとのこと、001などのidを使っいる場合は注意が必要ですね。