われプログラミングする、ゆえにバグあり

私だって価値を創造してみたいのです

またRubyGemを作った rspec_term

RSpecの実行中や、テスト結果によってiTermの背景画像を変更する RubyGem を作った。

brightgenerous/rspec_term · GitHub
rspec_term | RubyGems.org | your community gem host

使い方については GithubのREADME見てください。

使用例

4パターンの画像を使用できます

テスト実行中

f:id:bright-generous:20140518132501p:plain

テスト成功

f:id:bright-generous:20140518132513p:plain

テスト失敗

f:id:bright-generous:20140518132516p:plain

テストなし

f:id:bright-generous:20140518132519p:plain

まとめ

RSpecの実行オプションでon/off切り替えるようにしてもよかった。
(次version作るならやります)

RubyGemを作ってみた

前回の記事のやつ(要素が足りない場合に効率的に追加するEnumerable - われプログラミングする、ゆえにバグあり)を書きなおして、 ruby gem にしてみました。


公開しています。
お気に召したら、使ってみてください。

merge_enum | RubyGems.org | your community gem host

brightgenerous/merge_enum · GitHub


ActiveRecord と合わせて使うといいかも。

まとめ

Gem作って公開するのはすごく簡単だった。

要素が足りない場合に効率的に追加するEnumerable

Ruby on Railsでコード書いてて、以下の様なケースがありました。
 
Hogeからnameを取得し、limit件数に満たなければ、さらにFooからtitleを取得して追加する。

limit = 10
elems = Hoge.where(:status => 1)
                .first(limit)
                .pluck(:name)
if elems.count < limit
  elems += Foo.where(:flag => true)
                    .first(limit - elems.count)
                    .pluck(:title)
end

Hoge と Foo は ActiveRecord::Base を継承しています。
場合によってはSQLのUnionを使うようにして解決できますが、わりと面倒(ソート順のカラムを合わせたりなどなど)だったり。

 
いろいろとコードがめんどくさい感じになっていて不便なので、クラスを作りました。

作ったクラス
class MultiEnumerable
  include Enumerable

  def initialize(*args)
    if args.last.is_a? Hash
      @collections = args[0...-1]
      @options = args.last
    else
      @collections = args
      @options = {}
    end
  end

  def each &block
    cnt = 0
    fst = @options[:first]
    @collections.each do |c|
      f = fst ? fst - cnt : nil
      break if not f.nil? and f <= 0
      if c.is_a? Proc
        if c.arity == 0
          c = c.call
        else
          c = c.call(f)
        end
      end
      c = c.first(f) if f
      c.each do |i|
        block.call(i)
      end
      cnt += c.count
    end
  end

end


使い方はこんな感じです。

mul = MultiEnumerable.new(
  -> { [1,2,3] },
  -> (c) { c ? [4,5,6][0...c] : [1,2,3] }, # この場合、ここは実行されない
  {:first => 2}
)
mul.each do |item|↲
  # item => 1, 2 の2回、ここが実行されます
end

mul = MultiEnumerable.new(
  -> { [1,2,3] },
  -> (c) { c ? [4,5,6][0...c] : [1,2,3] }, # この場合、ここは c  => 1 で実行される
  {:first => 4}
)
mul.each do |item|↲
  # item => 1, 2, 3, 4 の4回、ここが実行されます
end

 

最初のコードを置き換えるとこうなります
MultiEnumerable.new(
  -> (c) { Hoge.where(:status => 1).first(c).pluck(:name) },
  -> (c) { Foo.where(:flag => true).first(c).pluck(:title) },
  {:first => 10}
)

簡潔になりましたね。

 

まとめ

車輪の再発明だと悲しい。