blog.waterlow.work

Ruby, Rails, js, etc...

【Ruby】配列の&と|の機能について

特殊なルールで配列を並び直す。たとえば
['いち', 'に', '', 'よん', 'なな', 'ご']
から
['に', 'よん','いち', 'ご', 'なな']
を作りたい(奇数が優先、2で割る等の数値演算はできない)ときはどうするか。
①まず以下のようにやることを思いついた。

['いち', '', '', 'よん', 'なな', '']
  .reject { |v| v.nil? || v == '' }
  .sort_by { |v| %w(に よん ろく はち いち さん ご なな).index(v) }

②ドキュメントを読んでいた時にArray#|のところで「preserving the order from the original array」という記述があり
 Array#&もやっぱりそうだった

%w(に よん ろく はち いち さん ご なな) &
['いち', '', '', 'よん', 'なな', '']

めでたしめでたし。これを期に、必要に迫られる前にドキュメントを読んでおくくせを付けておこうと思います! もっと良い方法がありましたらぜひ。

【ActiveRecord】exists?の引数にwhereクエリの内容を書いてみたらうまくいかない。

Person.exists?(1) # => true
# SELECT  1 AS one FROM `job_offers`  WHERE `job_offers`.`id` = 1 LIMIT 1
Person.exists?('id = 1') #=> false
# SELECT  1 AS one FROM `job_offers`  WHERE `job_offers`.`id` = 0 LIMIT 1

なぜか?
exists?にArrayもしくはHashを引数で渡すと、where(condition).limit(1)とほぼ同じ挙動をする。
それ以外の引数を渡すと主キーでの検索を行おうとする。
ただし、findではなくPerson.where(id: 'id = 1')が呼ばれ、(この後はソースを読み切れていないが)エラーにならずWHERE job_offers.id = 0なんてクエリが発行されてしまうらしい。

解決策
whereと同じような挙動を期待する場合はArrayもしくはHashを与えれば良い。 したがって今回の場合であれば

Person.exists?(['id = 1']) #=> true
# SELECT  1 AS one FROM `job_offers`  WHERE (id = 1) LIMIT 1

となる。(カッコが付いてしまうのは仕方なさそう。) ActiveRecord:: FinderMethods#exists?

railsエンジニアの勉強法(読む本とか)まとめ

初心者レベルを脱却するための記事とか読む本とか、いろいろと溜まってきたのでまとめておこうと思います。
勉強するのは常に一人なので、歩みは非常に鈍いのですが…

Rails で "とりあえず動くコード" を書けるようになった人が次に遭遇する問題とそれを解決してくれる本まとめ - 彼女からは、おいちゃんと呼ばれています

Rails Tutorial以前 - Web系エンジニア入門レベルを達成するには - - 国境の南

新卒ソフトウェアエンジニアのための技術書100冊 - クックパッド開発者ブログ


やること多々ですね!!
デザインパターンとかオブジェクト指向をちゃんと勉強したければJavaはある程度読めたほうが良さそう。「オブジェクト指向のこころ」という本を読んでいて思いました。

[追記 6/19]
Railsアプリケーション構築ガイド — Railsアプリケーション構築ガイド
Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
Rails Girls - Japanese

【ruby】モジュールをextendした時の動き

タイトルそのまんまです。 疑問だったのでいろいろ調べてみたのですが、メタプログラミングRubyの特異クラスのところを読んでいたらわかりました。 オブジェクトからモジュールをextendすると、そのメソッドはオブジェクトのクラスではなく特異クラスに入る。

class Object
  def eigenclass
    class << self; self; end
  end
end

class C; end

module M
  def my_singleton_method
    'obj#my_singleton_method()'
  end
end

obj = C.new
obj.extend M

obj.eigenclass.instance_methods.grep(/my_/) #=>[:my_singleton_method]
obj.class.instance_methods.grep(/my_/) #=>[]

だから、もしオブジェクトに対してextendする予定のモジュールに定数とか名前空間を更に定義している場合、外からは非常にアクセスしづらい。

class C
  C_VAL = :c
end

module M
  M_VAL = :m
end

obj = C.new
obj.extend M
obj.class::C_VAL #=> :c
obj.singleton_class::M_VAL #=> :m
obj.class::M_VAL #=> uninitialized constant C::M_VAL

もし2つモジュールをインクルードするとどうなるかというと…

class C; end
module M; end
module N; end

obj = C.new
obj.extend M
obj.extend N
obj.singleton_class.ancestors
# => [M, C, Object, Kernel, BasicObject]

特異クラスで継承が行われる。

【Google Analytics】簡単!メルマガの開封数を取得する方法

お仕事で使ったTipsです。 メールマガジン(以下メルマガ)がどのくらいの人に開封されているかを探る方法のメモです。
※以下の方法はHTMLメールの開封数しか取れません。簡単に言うとimgタグを使ってサーバにリクエストを送るものです。

前提として日本語の記事があまりない

リクエストを独自のサーバに送らせて計測するやり方は各自でやっているかと思うのですが
Google Analytics メール 開封」等のキーワードで調べても大抵以下の記事のようにクリック数の取得方法を解説した記事が出てきます。

記事の中では開封率はクリック数を見るのに比べて難しいというふうな記載もあります。まだクリック数の取得はやったことがないのですが、そちらのほうが簡単なのでしょうか?

やり方

では実際どうやるのかというと、以下のやり方を参考にします。

MailChimpというアプリを使っているようですが、今回は使いません。上記のURLを参考に、以下のようなimgタグをHTMLメールに埋め込みます。

<img src="http://www.google-analytics.com/collect?v=1&tid=UA-XXXXXXX-YY&cid=*|UNIQID|*&t=event&ec=email&ea=open&el=*|UNIQID|*" />

tidにはご自身のアナリティクスのトラッキングIDを入れましょう。
cidは、送信するメールをれぞれに異なる値を設定しておけば1回でも開封されたかどうかを見ることができるようになります。
elは計測上必要(日毎に図りたいorABテストしたい…)な場合は入れましょう。erbを使って"el=#{Date.today.strftime('%Y%m%d')}_#{test ? 'A' : 'B'}"的な感じで入れればアナリティクス側で出し分けられます。

以上、メルマガ開封数を測る方法でした。アナリティクスの使い方はいろいろありそうなので、便利な使い方があったらもっと知りたいですね!

参考

【ruby】【備忘録】ユーザをいくつかのグループに分ける・カウントする

備忘録
記事のタイトルと冒頭文のテストをする。 そのために、オブジェクト(ユーザ)を4つのグループに分けたい。 分け方に対する議論は一旦置いておいて、ひとまずidで順番にグループ分けをしてみる。

User = Struct.new(:id, :email, :test_pattern)
users = 57.times.map do |i|
  User.new(i, "user#{i}", { title: i % 2, statement: (i % 4) / 2})
end
# => [
#   <struct User id=0, email="user0", test_pattern={:title=>0, :statement=>0}>,
#   <struct User id=1, email="user1", test_pattern={:title=>1, :statement=>0}>,
#   <struct User id=2, email="user2", test_pattern={:title=>0, :statement=>1}>,
#   <struct User id=3, email="user3", test_pattern={:title=>1, :statement=>1}>,
#   ...
#   <struct User id=56, email="user56", test_pattern={:title=>0, :statement=>0}>
# ]

それぞれ何パターンずつ出力したのかカウントする

test_pattern_count = users.inject([]) do |array, user|
  array[user.test_pattern[:title]] ||= []
  array[user.test_pattern[:title]][user.test_pattern[:statement]] ||= 0
  array[user.test_pattern[:title]][user.test_pattern[:statement]] += 1
  array
end

info = "パターン別カウント\n"
test_pattern_count.each_with_index do |array, i|
  array.each_with_index do |count, j|
    info << "パターン#{i}_#{j}#{count}\n"
  end
end

puts info
# =>パターン別カウント
#   パターン0_0:15
#   パターン0_1:14
#   パターン1_0:14
#   パターン1_1:14

これでとりあえず出来たけど、テストする箇所が増えたりパターンが増えたりすると、 結構描き直さないと対応できない。
ただテストのパターンはころころ変わるしテストしない期間もあるためこんなもん?

【ruby】破壊的操作がなければdupは使って良さそう

最近rubydupというメソッドを使いました。dupと言うのはオブジェクトの”コピー”を作るのですが、一般的には以下の様な懸念点が有ります。

簡単に言うと

a = ["foo", "bar", "baz"]
b = a.dup
a[0].upcase!
p a #=> ["FOO", "bar", "baz"]
p b #=> ["FOO", "bar", "baz"]

オブジェクト自体は新しいものですが、インスタンス変数(配列の要素)の参照先が同じであるためこのようなことが起こっているようです。 オブジェクトを破壊的に変更しない場合にはそのようなことは起きない。

a = ["foo", "bar", "baz"]
b = a.dup
a[0] = a[0].upcase
p a #=> ["FOO", "bar", "baz"]
p b #=> ["foo", "bar", "baz"]

なので破壊的操作が存在しないFixnumやシンボル、真偽値等は特に気にせずdupして問題なさそう。

あと、この辺確かめるためにobject_idを使っていろいろしらべていたけど、以下のFixnumのオブジェクトIdの決め方の話が面白そう。