hirakiucの日記

いろいろ

rails3 + rspec + helper method

まだ疑問に思っている程度の事。

rails3の複数のhelperに同じ名前のメソッドを作るとrspecできちんとテストできない様子。

以下のコードは実際に試したコードとは異なる名前なので、動作が異なるかもしれない。

ただ、このような状況が発生している事は残しておきたい。

例 ) こんな構成になっているとき。

# app/helper/top_helper.rb

module TopHelper
  def my_helper_method
    "top helper method"
  end
end

# app/helper/second_helper.rb
module SecondHelper
  def my_helper_method
    "second helper method"
  end
end
rspec
# spec/helpers/top_helper_spec.rb

require 'rspec_helper'
describe TopHelper do
  describe :my_helper_method do
    it { expect(helper.my_helper_method).to eq("top helper method") }
  end
end

これで動かしてみるとrspecがfailedな状態になる。

表示されるスタックトレースを見ると、どうやらSecondHelperの方が呼ばれている様子。


helper毎にmodule名が違うんだから、同じ名前のメソッド名つけてもきちんと使い分けてほしい。

まだ勉強不足でどこが原因なのか調べきれてないので、とりあえず状況をメモしてみた。

git pre-pushで ユーザの入力を受け付ける方法

https://raw.github.com/git/git/master/Documentation/RelNotes/1.8.2.txt

git 1.8.2からpre-push hookが追加されたので、早速使ってみた。

このとき、ユーザからの入力を受け付けるところで少し工夫が必要だったのでメモ。

ユーザからの入力が拾えない?

ユーザに yes/no ? と確認してから動作するようなスクリプトを書いたのだけど、単純に $stdin.gets とすると入力待ちしてくれない様子。

また、pre-pushは $stdinからはpushされる対象の情報が渡ってくるとのこと。

# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
解決方法

https://gist.github.com/ebihara/5948590#file-pre-push-py-L10

こちらの10行目が解決方法になってた。

まずスクリプトの先頭で $stdinから拾えるだけ入力を拾ってきておく。

その後は、$stdinを open("/dev/tty") で置き換えて利用。

これで解決できた。

今日の発見

Expecta

AFNetworkingのtestcaseをチラ見してて気がついたもの。

SenTestCaseで使ってるmatcherがさくっと書けてしまう優れもの。

petejkim/expecta - github.com

expect(@"foo").to.equal(@"foo"); // `to` is a syntatic sugar and can be safely omitted.
expect(foo).notTo.equal(1);
expect([bar isBar]).to.equal(YES);
expect(baz).to.equal(3.14159);

非同期テストもシンプルに書けるぽい。

expect(対象).will.to.equal(期待するもの);

とか。

実際の例はAFNetworkingのtestcaseから。

# https://github.com/AFNetworking/AFNetworking/blob/master/Tests/AFHTTPClientTests.m
- (void)testPostWithParameters {
    __block id blockResponseObject = nil;
    [self.client postPath:@"/post" parameters:@{ @"key": @"value" } success:^(AFHTTPRequestOperation *operation, id responseObject) {
        blockResponseObject = responseObject;
    } failure:nil];
    
    expect([self.client.operationQueue operationCount]).will.equal(0);
    expect(blockResponseObject).notTo.beNil();
    expect(blockResponseObject).to.beKindOf([NSData class]);
    
    NSError *error = nil;
    NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:blockResponseObject options:0 error:&error];
    expect(responseDictionary[@"form"]).to.equal(@{ @"key": @"value" });
}

httpbin

通信ライブラリとかのテストケースで使えそうなもの。

httpbin

いろいろとテストで使えそうな予感。

ローカルで動いてないと嫌な人はここからインストールするとかで使えそうな雰囲気。

kennethreitz/httpbin - github.com

bundle と mecab-ruby

mecab, mecab-ipadicはcheckinstallで入れたんだけど、mecab-rubyがいい感じに動いてくれなかった。

試行錯誤した結果、こんな風にGemfileを書いたらうまいこといった。

source "https://rubygems.org"

# gem "rails"
gem 'mecab-ruby', :github => 'hotchpotch/mecab-ruby-gem'

あとデフォルトでinstallするとMeCab.soがロードできなかったので、環境変数を設定

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib64

これで動くようにはできた。

rubyのJSON.parseでhashキーをsymbolにしてもらう方法

Ruby JSON parse changes Hash keys

  • ruby 1.9.3p393
    • json (1.8.0)
$ pry
[1] pry(main)> require 'json'
=> true

# hashをjsonへ変換
[2] pry(main)> json = {:key_a => "value2", :key_b => "value3"}.to_json
=> "{\"key_a\":\"value2\",\"key_b\":\"value3\"}"

# 普通にparseしてもらうと keyはString
[3] pry(main)> p JSON.parse(json)
{"key_a"=>"value2", "key_b"=>"value3"}
=> {"key_a"=>"value2", "key_b"=>"value3"}

# optionを指定すると、keyをSymbolにしてくれる
[4] pry(main)> p JSON.parse(json, {:symbolize_names => true})
{:key_a=>"value2", :key_b=>"value3"}
=> {:key_a=>"value2", :key_b=>"value3"}

rspecのfixturesに指定する名前でひっかかった話

rails3のrspecを書いて動かしていたときの話。

rspecのfixturesに指定する名前によっては、例外がログに出る事があるみたい。

fixtureに :xxx_meta という名前を指定した事が引き金だった。

rspecでfixturesを使ってた

とあるrails3なアプリのrspecでfixtureを使ってみたところ、実行する度にログにstack traceが出てくるようになってた。

# spec/models/blog_meta_spec.rb
require 'spec_helper'

describe BlogMeta do
  fixtures :blog_meta
# ... spec...
end

spec/fixtures/blog_meta.yml にはちゃんとfixtureなデータファイルは置いてて、期待する感じで動いてくれる。

動いてくれるのだけど、なぜか実行の度にstack traceがログに出てくる。

気持ち悪い!

rspecの実行の度にstack traceがログに出る

Unable to load blog_metum, underlying cause No such file to load -- blog_metum

(snip)/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/dependencies.rb:317:in `rescue in depend_on'
(snip)/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/dependencies.rb:312:in `depend_on'
(snip)/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/dependencies.rb:225:in `require_dependency'
(snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:767:in `try_to_load_dependency'
(snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:782:in `block in require_fixture_classes'
(snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:779:in `each'(snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:779:in `require_fixture_classes'
(snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:762:in `fixtures'
(snip)/spec/models/blog_meta_spec.rb:4:in `block in <top (required)>'
(snip)/vendor/bundle/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:242:in `module_eval'
(snip)/vendor/bundle/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:242:in `subclass'(snip)/vendor/bundle/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:228:in `describe'
(snip)/vendor/bundle/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/dsl.rb:18:in `describe'
(snip)/spec/models/blog_meta_spec.rb:3:in `<top (required)>'

(...snip...)

(一部省略)

原因

結論からいうと、

ActiveSupport(ここでは3.2.13) - String.singularize

文字列の単数形を作ってくれる便利なメソッドの挙動が原因。

$ bundle exec ./script/rails console                                                                                     
Loading development environment (Rails 3.2.13)
1.9.1 :001 > "blog_meta".singularize
 => "blog_metum" 
1.9.1 :002 >

meta の 単数形 が metum になってる。

参考:metaはmetumの複数形じゃないよね

もう少し細かい話

stack traceを追いかけてみると、少しずつ見えてきた。

activerecordでは、fixturesをロードする前に、指定された名前から必要となりそうなファイル名を推測して先にロードしようとがんばる。

そのときに指定した名前を単数形にして、そのファイルをロードしようとする様子。

まさにこのfixtures.rb:781

# vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb
778       def require_fixture_classes(fixture_names = nil)
779         (fixture_names || fixture_table_names).each do |fixture_name|
780           file_name = fixture_name.to_s
781           file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
782           try_to_load_dependency(file_name)

rspecの "fixtures :blog_meta"という記述の引数にあたる部分が fixture_namesになってわたっていく。

そして、 "blog_meta".singularize => "blog_metum" となり、その"blog_metum"をloadしようとして、そんなファイルないわーと例外を投げる結果になる。

ちなみにここではActiveRecord::Base.pluralize_table_names はdefaultのまま trueでした。

対策

一つの解決方法として、設定で回避する事に。

Railsで単数形をsingularizeすると不可解な変化をする場合の対応

rails3でも同様な対処ができる様子。

# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections do |inflect|
  inflect.singular 'blog_meta', 'blog_meta'
end

こんな風に、「"blog_meta"の複数形は "blog_meta"にしてください」とお願いすると回避できた。

$ bundle exec ./script/rails console                                                                                     
Loading development environment (Rails 3.2.13)
1.9.1 :001 > "blog_meta".singularize
 => "blog_meta"
1.9.1 :002 >

すっきりした。

今日の発見

後でこんなのあったなーと自分が振り返れるようにするために、

細かい説明とかは省略して、発見した情報をメモするだけの試み。

aws sdk for iOS

awsのiOS用SDKがある様子。便利そう。

まだ使ってないので雰囲気はAPI referenceをサラッと見た感じだけ...

あとで使ってみる予定。

$ pod search AWSiOS

-> AWSiOSSDK (1.5.0)
   Amazon Web Services SDK for iOS.
   - Homepage: http://aws.amazon.com/sdkforios
   - Source:   https://github.com/aws/aws-sdk-ios.git
   - Versions: 1.5.0, 1.4.6, 1.4.5, 1.4.4, 1.4.3, 1.4.2, 1.4.1, 1.4.0, 1.3.3, 1.2.2 [master repo]
   - Sub specs:
     - AWSiOSSDK/Runtime (1.5.0)
     - AWSiOSSDK/AutoScaling (1.5.0)
     - AWSiOSSDK/CloudWatch (1.5.0)
     - AWSiOSSDK/DynamoDB (1.5.0)
     - AWSiOSSDK/EC2 (1.5.0)
     - AWSiOSSDK/ElasticLoadBalancing (1.5.0)
     - AWSiOSSDK/S3 (1.5.0)
     - AWSiOSSDK/SES (1.5.0)
     - AWSiOSSDK/SNS (1.5.0)
     - AWSiOSSDK/SQS (1.5.0)
     - AWSiOSSDK/STS (1.5.0)
     - AWSiOSSDK/SimpleDB (1.5.0)

image_optim for ruby

mac appのImageOptimはとてもお世話になっているのだけど、rubyから使えるgemもあるぽい。

toy / image_optim

中身をチラっと見た感じ、最適化用の各種コマンドを呼び分けてくれるものの様子。

たった2行で画像の最適化ができた。(ちょっぴりサイズが減った)

コマンドも付いてて、linuxで手軽に使えるようになってる様子なのはありがたい。