「assertありゃええねん」

前回のdecoratorのテストのassertをminitest-power_assertに変えてみた。
# Gemfile:
group :test do
  gem 'minitest-power_assert'
end

わざと間違えてみる。

# test/decorators/user_decorator_test.rb:
require 'test_helper'

class UserDecoratorTest < ActiveSupport::TestCase
  def setup
    ActiveDecorator::ViewContext.current = controller.view_context
    @user = ActiveDecorator::Decorator.instance.decorate users(:jean)
  end

  test 'full_name' do
    assert { @user.full_name == 'Hugh Jackman' }
  end
end
$ rake test test/decorators/user_decorator_test.rb
Run options: --seed 52969

# Running:

F

Finished in 0.134274s, 7.4475 runs/s, 7.4475 assertions/s.

  1) Failure:
UserDecoratorTest#test_full_name [/Users/komagata/code/active_decorator-test_unit/test/decorators/user_decorator_test.rb:10]:

    assert { @user.full_name == 'Hugh Jackman' }
             |     |         |
             |     |         false
             |     "Jean Valjean"
             #<User id: 758109964, first_name: "Jean", last_name: "Valjean", website: "http://jeanvaljean.com", created_at: "2014-07-01 15:35:12", updated_at: "2014-07-01 15:35:12">

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

こりゃええ!

怖話はminitestに書き換えつつあるんですが、Bizerでは他の開発者に気を使ってrspecを使っています。

Bizerをrails4.1に上げたので、「ヒャッハー、デフォルトでtravel_toが使えるからtimecop削除できるぜー!」と思ったのですがrspecじゃそのままじゃ使えない。(クソがぁ

Before:

it 'coupon is expired.' do
  # FIXME: travel_to in rails 4.1
  Timecop.travel(2014, 5, 10, 0, 0, 0) do
    expect(expired_coupon_user.free_by_coupon?).to be_false
  end
end

After:

# spec/spec_helper.rb:
RSpec.configure do |config|
  config.include ActiveSupport::Testing::TimeHelpers
end
it 'coupon is expired.' do
  travel_to Time.new(2014, 5, 10, 0, 0, 0) do
    expect(expired_coupon_user.free_by_coupon?).to be_false
  end
end

今までTime.nowDate.todayを内部で使うメソッドはテストしやすいように

def metrix(now = Time.now); ... end

みたいに書いてたんですが、デフォルトで気軽にtravel_toが使えるならやらなくていいかな!

C-t: カーソル部分のタグにジャンプ

C-o: 前いた場所に戻る(tagに限らずいつも使いまくるヤツ)

g C-t: タグ候補が複数ある場合に候補一覧を表示

この3つだけでクッソはかどる

Xcodeとかの機能がうらやましい

IDEでは当然の機能ですが、クラスやメソッドの定義に飛べるのって便利ですよね。vimやemacsでは設定すればできるんだろうけどやってる人あんまり見ない。

開いてるbuffer内のものしか飛べないとか、明示的にtag生成コマンド実行とか論外。

そんな折、Gemfileのgemを含めて保存時自動tag生成してくれるvim-tagsというpluginがとても良かったのでご紹介。

szw/vim-tags

vim-tagsの設定

$ brew install ctags
# ~/.vimrc:
Bundle 'szw/vim-tags'
let g:vim_tags_project_tags_command = "/usr/local/Cellar/ctags/5.8/bin/ctags -f tags -R . 2>/dev/null"
let g:vim_tags_gems_tags_command = "/usr/local/Cellar/ctags/5.8/bin/ctags -R -f Gemfile.lock.tags `bundle show --paths` 2>/dev/null"
set tags+=tags,Gemfile.lock.tags

macデフォのctagsはbsdで色々違うのでbrewで入れてfullpath指定。

ファイル保存時にtagsファイルを生成。ちょっと遅くなるので非同期実行するvim-dispatch pluginとの連携も試してみたいと思います。

tpope/vim-dispatch

とにかく機能が細かくgemに分割される昨今。これははかどる!

EngineYardCloudのEnvironmentをのぞいてみたらruby2.1.2に対応してたので怖話をアップデートしました。

stackのversionが上がる時ほど警戒は必要無いとは思いますが、clone environmentしてそちらをアップデートしてみて動作確認してから本アップデートをかけるのが正義。

ruby2.0.0-p247からのアップデートだったんですが、何の問題も無く完了しました。最新は気持ちいい。

Early AccessにはWebサーバーのpumaも来てるのでrails4.1系にしてpumaが正式版になったらこっちも変えたらかなり速くなるんじゃないかなと期待しております!

railsでいつも下記のようにしてたのでgemにしました。

# lib/seed_helper.rb:
require 'active_record/fixtures'
                               
module SeedHelper              
  def import_fixture(name)
    puts "Import #{name}..."   
    ActiveRecord::FixtureSet.create_fixtures \
      "#{Rails.root}/db/fixtures", name
  end                          
end
# db/seeds.rb:
require 'seed_helper'
include SeedHelper # この2行がウザい

import_fixture :users
import_fixture :posts

seed系のgemはたくさんあるけど俺にはどれもオーバースペックだったので。

simple_seed

komagata/simple_seed

使い方

$ mkdir db/fixtures
$ vi db/fixtures/users.yml
user_1:
  name: Jean Valjean
user_2:
  name: Bishop Myriel
user_3:
  name: Cosette
$ vi db/seeds.rb
import_fixture :users
$ rake db:seed
Import users...

rubyのIDEではRubyMineが有名だけど有料なのでEclipse+RDT(Ruby Development Toolkit)を試してみました。(Aptana Studio 3 Pluginは使わない)

Eclipseのインストール

eclipseのサイトからMac OS X 64 Bit版をダウンロード。この時のバージョンは4.3.2でした。

RDTのインストール

-All Available Sites--にしてrubyで絞り込むとRDTが出てくるのでインストール。

PreferencesでRubyのInterpretersを追加。上記のようにrbenvを使ってる場合はshims/rubyを指定すればOKでした。

Enginesで何でdebugするのかというのを選びます。他のはgemが必要だったりバージョン毎に動いたり動かなかったりするのでRuby Build-In Debuggerを選択。

普通のrubyスクリプトをdebug

これは普通にできる。トップレベルのlocal変数が見れない気がするけど。

sinatraアプリをdebug

これも普通に実行するとhttp://localhost:4567/とかで立ち上がるのでブラウザからアクセスするとちゃんとブレイクポイントで止まってくれる。

railsアプリをdebug

怖話で試してみる。

これも要はscript/rails serverを実行してhttp://localhost:3000/にアクセスすればいいだけだからDebug Configurationsで下記のようなものを用意する。

実行するスクリプトはscript/rails

引数はserver

ただでさえ遅い怖話のデバッグ起動はクッソ重いので覚悟すること。

ちゃんとブレイクポイントの上の行の@nowがとれてます。

感想

ややこしいプロジェクトの厄介なバグを本腰入れて追い回す時には良いかも。ただほんとに重いので軽快にdebugしたいならpry-byebugの方が良いかな。

気になった点

ruby1.9からのnew hash syntaxがエラーと認識されている。RDTのどこかで設定できるのかな?

$ brew update
$ brew upgrade rbenv ruby-build
$ CONFIGURE_OPTS="--with-openssl-dir=`brew --prefix openssl` --with-readline-dir=`brew --prefix readline`" rbenv install 2.1.2
$ rbenv global 2.1.2
$ gem install bundler rbenv-rehash

sinatraset :foo, 'bar'みたいなDSLを使って値を設定できる。でもこの値ってどこにどうやって保存されているんだろう?

普通、最終的にはインスタンス変数かクラス変数に格納されてるって思うよね。

(class << self; self; end).class_eval do
  undef_method(name) if method_defined? name
  String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
end

sinatra/lib/sinatra/base.rb at v1.4.5 · sinatra/sinatra

ところがset :foo, 'bar'するとbarを返すfooというメソッドが定義されるという形で保存されている。

setするたびにundef_methodして定義し直すので変数みたいに使えるようだ。静的な頭で考えてたのでクラクラするような格納方法だ。

$LOADED_FEATURESっていうのがある。

% pry
[1] pry(main)> $LOADED_FEATURES
=> ["enumerator.so",
 "/usr/local/opt/rbenv/versions/2.1.0/lib/ruby/2.1.0/x86_64-darwin13.0/enc/encdb.bundle",
 "/usr/local/opt/rbenv/versions/2.1.0/lib/ruby/2.1.0/x86_64-darwin13.0/enc/trans/transdb.bundle",
 "/usr/local/opt/rbenv/versions/2.1.0/lib/ruby/2.1.0/x86_64-darwin13.0/rbconfig.rb",
 "/usr/local/opt/rbenv/versions/2.1.0/lib/ruby/2.1.0/rubygems/compatibility.rb",
 "/usr/local/opt/rbenv/versions/2.1.0/lib/ruby/2.1.0/rubygems/defaults.rb",
 "/usr/local/opt/rbenv/versions/2.1.0/lib/ruby/2.1.0/rubygems/deprecate.rb",
 "/usr/local/opt/rbenv/versions/2.1.0/lib/ruby/2.1.0/rubygems/errors.rb",
 "/usr/local/opt/rbenv/versions/2.1.0/lib/ruby/2.1.0/rubygems/version.rb",
(snip)

ほうほう。

rubygems - How do I get a list of files that have been `required` in Ruby? - Stack Overflow

ActionMailerでマルチバイトのメールを送る時下記のように三行書く。

mail = NoticeMailer.charging(user, charge)
mail.transport_encoding = '8bit'
mail.deliver

プロジェクトの中にはNoticeMailer.posted_questionとかいくつかあるし、他のmethodでも他のMailerクラスでも一行で書きたいんだけどどのファイルにどう書くのがキレイかなあ?

追記:

tkawaさんのコメントを受けて実装された。

NoticeMailer.charging(user, charge).deliver