Hatena::ブログ(Diary)

=== SANDmark 19106 === beginning stress test このページをアンテナに追加 RSSフィード

2016年01月19日 [Windows][Ruby]natto gemで形態素解析をする!(Windows7 64bit編)

natto gemで形態素解析をする!(Windows7 64bit編)

あけましてから結構な日数が経ちましたが、去年どころか一昨年のご挨拶さえしたかどうか定かではない今日このごろ。皆さんお久しぶりです。いいえむしろ初めまして sandmark です。もともと定期購読してお得なブログを目指したわけでもなく、その場しのぎのナマモノ情報をお届けしているわけでして、本年もこんな自堕落な調子で過ごしていきたいと思います。

恋するプログラム

さて、先日こんな本を見つけました。
恋するプログラム―Rubyでつくる人工無脳

2005年出版かつWindowsベースの解説なので、ruby-1.8系のstableが出るか出ないかの時代ですね。RubyGemsさえないという。そんな中でよくGUIインターフェイスの実装まで解説したなぁと思って感心しているのですが、ライブラリ処理系の時代背景はともかく非常によくできた構成で、「これを機にRubyでWindowsアプリケーション開発しよう」と読者に奮起させる意図もあったのかな、と思います。当時はRubyもまだマイナーで、GUIアプリケーションといえばVisual C++デファクトスタンダードだったわけですし(今でも実質そうなんだけど)。それでも最近はQtGTKラッパーライブラリ経由で使ったり、.NETがいろいろ進化してたり、当時に比べればかなり敷居は低くなったんじゃないかなと思います。では本書のGUI解説がまったくの役立たずかというとそうでもなくて、コアライブラリと完全に分離されているため、GUI無しでもそのまま読み進められます。むしろ「ライブラリをどのように分離するか」の説明に重きを置いているので、プログラミング or Ruby初心者向けでもあります。テーマもなかなか魅力的……っていうかそれが目的で買ったんですが、人工無能の開発イディオムを1から解説してくれる書籍もあまり見つけられないので、個人的には☆5つ付けたいくらいの良書です。

libmecab.dllの64bit版がなかった

読み進めてきたところで、これ系のプログラムには必須とも言える形態素解析が必要になりました。本書では茶筅(ChaSen)FFI経由で利用する手法が採用されていましたが、私の開発環境はUbuntuなので、できればgemでなんとかしたい。というかプラットフォーム依存のコードにしたくなかったので「ruby 形態素解析」でぐぐった結果、どうもMeCab: Yet Another Part-of-Speech and Morphological Analyzerを使うのが王道な感じで、natto gemから叩くのが良さそう。

だったのですが、MeCabのWindowsバイナリは32bit版しか提供されておらず、他の言語用のバイナリをnattoから読もうとするとエラーが頻出したので、もう諦めて自前でビルドしました。他の方に倣って置いておきます。

64bit版libmecab.dll (See also: no title)
libmecab_X64.zip - Google ドライブ

%MeCabをインストールしたディレクトリ%/bin/にあるファイルをバックアップしておいて、上記バイナリを放り込んでください。これでrequire 'natto'戻り値trueなら大丈夫です。それでもダメなら環境変数MECAB_PATH=C:\Path\To\libmecab.dllを設定してみましょう。

文字コードのお化け

Windowsのコマンドプロンプトで実行すると、 natto は自動的にShift_JISでMeCabに渡すっぽいです。MeCab辞書をUTF-8でインストールしている場合、

require 'natto'

Encoding.default_external = 'UTF-8'

nm = Natto::MeCab.new
puts nm.parse(readline.chomp)

とかやるととっても文字化けるので、よくわからんちんな私のような人はMeCab辞書をあらかじめShift_JISでインストールしておくといいと思います。

require 'natto'

nm = Natto::MeCab.new
charset = nm.dicts.first.charset
encoding = (charset == 'SHIFT-JIS') ? 'Shift_JIS' : charset

puts nm.parse(readline.chomp.encode(encoding)).encode('UTF-8')

こんな感じでうまいことやってくれます。もっと良い方法があったら教えて下さい。

それではー。

2013年12月30日

JRubyでUnicodeコードポイント文字列をデコードする

ハマりました。

Unicodeコードポイント文字列はあれですね。

\u3042\u3044\u3046\u3048\u304a

みたいな、一見文字化けしてるようなやつ。TwitterなんかのAPI戻り値で見たっていう人もいるんじゃないでしょうか。そういう場合はJSONに渡してやれば勝手にデコードしてUTF-8にしてくれる(らしい)のですが、今回はJSON形式ではなく生の文字列が対象だったので除外。

普段通りCRubyのつもりで書いていたところ、いつも動くコードが全然動かない。エラーを吐くわけでもなく、デコードメソッドを作って文字列を渡しても戻り値が一向に変わる気配がない。頑なにコードポイントでいようとするのでCRubyのirbで試したところ、普通に変換できちゃいました。ここで「あぁこれJRubyの問題だな」と確信した次第です。ちなみに使っていたコードはこんな感じ。Ruby1.9です。

str.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*') }

一応 jruby -E utf-8 とすれば動くっちゃ動くんですが、後のこと*1を考えるとちょっと気乗りがしませんでした。…ていうか、 -E 指定しないときは何だと思われていたのだろう。

ので、いつものようにGoogle先生にお頼み申し上げたところ、"jruby unicode" だの "jruby unicode gem" だの、検索候補が出るわ出るわ。ざっと眺めただけで結局どうしてこれがJRubyで動かないのかわからんので、なんかそれっぽい要因を勝手にリスト化して置いておきますね。

  • Java: 文字列の保持に内部ではUTF-16を使っている
  • Java: 出力するときはネイティブ文字コードに自動変換する
  • JRuby: 文字列の保持にUTF-8を使っている
  • JRuby: というよりJavaとCRubyとの互換性を保つためにUTF-8にせざるを得ない?
  • JRuby: コードにマルチバイト文字が含まれる場合、先頭におまじない*2が必要
  • JRuby: おまじないをしない場合 US-ASCII として扱われるのはCRubyと一緒
  • JRuby: そもそも # encoding: utf-8 と書けばいいのでは…?

解決しました。本当はJavaのIntegerクラスにアクセスしてparseIntメソッドを呼び出して無理やり変換していたんですが、記事を書きながら「そういえば -E オプションあったよね」と思い、試してみたら普通に動きました。過去を振り返るのも大切ですね。

というわけで良い例:

# encoding: utf-8
str.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*') }

悪い例:

# -*- coding: utf-8 -*-
str.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*') }  # 動かないよ><

普段Emacsさんが勝手にコメントを追加してくれるので、本来の意味を忘れてました。これファイルに含まれる文字コードをRubyに伝えるだけであって、扱うエンコード(-Eオプション)を指定するわけじゃないんですよね。はーい。

すっきり解決しましたが、戒めとしてJavaのメソッドを呼び出すRuby離れしまくった暗号のようなコードも載せておきます。いい勉強になったね(白目

require 'java'
str.gsub(/\\u([\da-fA-F]{4})/) {
  java.lang.Integer.parseInt($1, 16).chr("UTF-8")
}

*1:出来るかどうか知らないけど .jar にしたりとか

*2:# -*- coding: utf-8 -*-とか

2012年11月22日

今更だけどrvmからrbenvに乗り換えるときの個人用メモ

ruby-2.0.0-devを使いたいのにrvmが対応してない。
というだけの理由で、rbenvに乗り換えることにしました。
rvmも便利だったのですが、cdコマンドを書き換えたりと
結構ブラックなこともやっていたので、いい機会かなと。
なんだか長くなっちゃったので省略されました。

rvmとrbenvの違い

  • rvmはビルドからgemsetまで面倒を見てくれる
  • でも結構多方面に影響するので怖い部分もある
  • rbenv「ビルドツールなんてありません」
  • rbenv「でも ruby-build っていうのがあるからそれ使ってもいいよ」
  • rbenv「gemはbundleで各自管理してください」

rbenvさんは結構放任主義みたいです。
※とっても語弊があるので詳細についてはGoogle先生にお尋ねください。

まずはrvmをアンインストール

する前に注意事項。
端末はひとつだけ開いた状態で作業すること。
そして言うまでもなくgemはすべて消し飛びます。
※私はこの大前提を忘れて混乱しました。
シェル:

$ rvm seppuku

切腹って。
いざ消そうとするときに限ってそういう……。
まぁいいや、とにかくこれで ~/.rvm が存在していないことを確認し、
それから .zshrc なりに書き加えた各種rvm用の関数などを手作業で削除。
これでおしまいです。
アンインストールが終わったら端末を再起動すること。
※私はこの大前提を(ry

rbenvのインストール

続いてこちらがrbenvになります。
が、Ubuntu12.10にはパッケージがあったのでそちらを使用。
シェル:

$ sudo apt-get install rbenv

今のところこれで問題なしです。

それから(私はzshを使っているので) ~/.zshenv に下記を追加。
~/.zshenv:

eval "$(rbenv init - zsh)"

bashを使っている方は zsh の部分を削り取れば行けるはずです。多分。

ruby-buildのインストール

ビルドツールとして ruby-build を使おうと思います。

こちらもUbuntuのパッケージにあったのですが、
rbenvのプラグインとして使ったほうが何かと都合が良さそうでした。*1

というわけでシェル:

$ mkdir -p ~/.rbenv/plugins/
$ git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

これでいつでもパッケージリストを更新できるのでお手軽です。
さらに、環境変数で MAKEOPTS もしくは MAKE_OPTS を設定しておくと、
値を make コマンドの引数に渡してくれます。

例えば ~/.zshenv なんかに:

export MAKEOPTS="-j5"

とか書いておくと、CPUコアがたくさんある人は幸せになれます。

Ruby1.9.3-p327とRuby-2.0.0-devのインストール

前置きが長くなりましたがいよいよインストール。
シェル:

$ rbenv install -l                   # インストールできる処理系・バージョン一覧(ruby-buildに依存)
$ rbenv install 1.9.3-p327 --verbose # rvm と同じ感覚でインストールできる
$ rbenv install 2.0.0-dev  --verbose
$ rbenv global 1.9.3-p327            # 全体で使うruby処理系を指定
$ ruby -v
ruby 1.9.3p327 (2012-11-10 revision 37606) [i686-linux]

"ruby -v" の出力が違うよーっていう人は端末を再起動したらいいと思います。

gemの設定

rvmと違ってrbenvの場合、新しいgemをインストールするたびに

$ rbenv rehash

しなければならないという、自由への代償的なものがあります。
さすがに面倒くさいので何とかしたい。

というわけでこんな便利ツールが。
gemをインストール/アンインストールするたびに rbenv rehash を実行してくれる
痒いところに手が届く感じのgemです。
ついでに bundle もインストールしましょう。

シェル:

$ gem i rbenv-rehash
$ gem i bundle
$ bundle

これで bundle が実行できれば大丈夫です。多分。

その他

$ rbenv shell VERSION

とかやった時は手動で

$ rbenv rehash

しなきゃいけない……っぽい?

Rails3.2.9でオートテスト+初期コマンド諸々まとめ

上記エントリの情報がとても古くて、
Rails4がリリースされた今となっては改めて更新する必要も無いかな、
と思っていたのですが。

$ gem install rails

でインストールされるバージョンが 3.2.9 だったので、
とりあえずRails4は見送る方向にしました。

しました、はいいのですが、いろいろ事情が変わりすぎてて動かん。
ので、さっくりまとめてしまおうと思います。

まずはテスト環境(改)の構築

おもむろにGemfile:

# mode: ruby
source 'https://rubygems.org'

gem 'rails', '3.2.9'
gem 'bootstrap-sass'
gem 'bootswatch-rails'
gem 'jquery-rails'
gem 'thin'
gem 'haml-rails'

group :development, :test do
  gem 'spork'
  gem 'rspec-rails'
  gem 'guard-spork'
  gem 'guard-rspec'
  gem 'capybara'
  gem 'capybara-webkit'
  gem 'factory_girl_rails'
  gem 'headless'
  gem 'database_cleaner'
  gem 'sqlite3'
  gem 'rails3-generators'
  gem 'therubyracer'
  gem 'less-rails'
  gem 'rb-inotify'
end

group :development do
  gem 'twitter-bootstrap-rails'
end

group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'
  gem 'uglifier', '>= 1.0.3'
end

いろいろ入っちゃってますがお好みで削除なりしてくだしあ。
例によって bundle install して、次にやることがテスト環境の構築。
といっても、

こっちとほとんど変わりません。

---- 2012-11-28: 追記 ----
CapybaraのDSL設定を忘れていたので適当に追加。
spec/spec_helper.rb:

Spork.prefork do
  # ...
  require 'rspec/rails'
  require 'rspec/autorun'
  require "factory_girl_rails"      # 追記
  require "capybara/rspec"          # 追記
  include Capybara::DSL             # 追記
  # ...

---- 追記終わり ----

Guardが大幅にバージョンアップしていたので、そこだけ修正です。*2
まずはGuardfileの以下の部分をちょいと修正します:

guard 'rspec', :version => 2, :cli => '--drb' do # 古い方。
guard "rspec", :cli => "--drb" do                # 新しい方。

「もうRSpec2にしか対応しねーから!!」という方針を固めたようで、
実際は「:version引数はdeprecatedだよ」という警告が出るだけなんですが、なんとなく。

次に、こんな感じのエラーを吐かれた人のために。

Starting Spork for Test::Unit & RSpec
Couldn't find a supported test framework that begins with 'testunit'

Supported test frameworks:
( ) Cucumber
(*) RSpec

Legend: ( ) - not detected in project (*) - detected
Using RSpec
Preloading Rails environment
Loading Spork.prefork block...
Spork is ready and listening on 8989!
--> ERROR: Could not start Spork server for Test::Unit & RSpec. Make sure you can use it manually first.
Guard::RSpec is running, with RSpec 2!
Running all specs

これはどうやら ./test/ ディレクトリが存在すると起こるエラーのようで、
単純に

$ rm -r ./test/

としてやると正常に動いてくれました。

次に叩くコマンド(完全に自分用)メモ

あとはTwitterBootstrap用の2行だけだけど。

$ rails g bootstrap:install
$ rails g bootstrap:layout application [fluid]

$ rake RAILS_ENV=test db:migrate

参考

*1:パッケージリストがruby-buildで管理されているので。

*2:Emacsにまで通知するようになってたんだけど、どうやってんのあれ……server?

2012年04月03日

Capybara-webkitによる高速インテグレーションテストまとめ

このテのエントリも何度目になるかわかりませんが、
実際に使ってきて「これだ!」という環境がある程度確立できた気がするので
一旦ここにまとめておきます。Ruby1.9.3、Rails3.2.2で動作確認。

使うものリスト:

ライブラリちょっとした解説
Sporkテストの高速化をしてくれるDRbサーバ
RSpec2言わずと知れた「動く仕様書」
Guardファイルを監視し、変更があればテストを自動実行*1
Capybara独自のDSLでインテグレーションテストを可能にする
CapybaraWebkitCapybaraでJavaScriptの動作確認をするためのエンジン
FactoryGirl「fixtureの代わり」とだけ表現するにはあまりにも惜しい多機能なモデルテンプレートエンジン
Headlessブラウザを画面上に表示せずにテストするためのラッパーライブラリ
DatabaseCleanerCapybaraへデータを渡すとき、渡したあとのデータ管理
Rails3GeneratorsFactoryGirl用の定義を自動生成してくれるジェネレータ

autotestからGuardへ乗り換えたのは、Guardが常にプロンプトを表示しており、
rと叩くだけでSporkの再起動から各種specファイルの実行まで行なってくれる他、
Macならgrowl、Linux系ならlibnotifyなど、通知までサポートしてくれる小粋な奴だからです。*2 *3

早速Gemfile:

group :development, :test do
  gem 'spork'
  gem 'rspec-rails'
  gem 'guard-spork'
  gem 'guard-rspec'
  gem 'capybara'
  gem 'capybara-webkit'
  gem 'factory_girl_rails'
  gem 'headless'
  gem 'database_cleaner'
  gem 'rails3-generators'
end

シェル:

$ sudo apt-get install xvfb         # headlessのための仮想ディスプレイライブラリ
$ sudo apt-get install libqt4-dev   # capybara-webkitのためのqt4ライブラリ
$ bundle install
$ bundle update
$ rails g rspec:install
$ spork --bootstrap

ここでSporkの設定が spec/spec_helper.rb の先頭に追加されているので、
Spork.prefork ブロックに元々の spec_helper.rb の内容を全て流し込みます。

そして、各gemの設定もついでにしてしまいましょう。

  • 2012-04-07追記: Sporkはテスト実行時にモデルをリロードしないようなので、
  • no title
  • こちらを参考に Spork.each_run ブロックに追記しました。

spec/spec_helper.rb(一部コメントなどを消去しています):

Spork.prefork do
  # headless のための設定。
  # これをしないとテストのたびにブラウザが表示される(かもしれない)
  require "headless"
  headless = Headless.new(display:99)
  headless.start
  at_exit{ headless.destroy }

  # ここから下は元々の spec_helper.rb の内容
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
  require 'rspec/autorun'

  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

  RSpec.configure do |config|
    config.use_transactional_fixtures = true
    config.infer_base_class_for_anonymous_controllers = false

    # :focus => true のテストのみ実行する
    config.treat_symbols_as_metadata_keys_with_true_values = true
    config.filter_run :focus => true
    config.run_all_when_everything_filtered = true

    # database_cleanerの設定
    config.before :suite do
      DatabaseCleaner.strategy = :truncation
      DatabaseCleaner.clean_with :truncation
    end

    config.before :each do
      if example.metadata[:js]
        self.use_transactional_fixtures = false
        DatabaseCleaner.start
      end
    end

    config.after :each do
      if example.metadata[:js]
        DatabaseCleaner.clean
        self.use_transactional_fixtures = true
      end
    end
  end

  Capybara.javascript_driver = :webkit  # ドライバをcapybara-webkitに(デフォルトは :selenium)
end

Spork.each_run do
  FactoryGirl.reload   # ファクトリの定義が実行ごとにリロードされる
  silence_warnings do                                            # 2012-04-07追記
    Dir[Rails.root.join('app/**/*.rb')].each{ |file| load file } # 
  end                                                            #
end

続いてGuardの設定:

$ bundle exec guard init spork
$ bundle exec guard init rspec

このコマンドで Guardfile というものが作成されているはずです。
一見すると長くて記号だらけで読む気も起きませんが、
単に監視するファイルを正規表現で指定し、
対応するspecファイルを実行するようにしているだけです。

今回はFactoryGirlを使う他、
SporkとはDRbを使って通信してもらわなければならないので、
そこんところをちょっとだけ修正します。

# ↓コメントアウト
# guard 'rspec', :version => 2 do
guard 'rspec', :version => 2, :cli => '--drb' do  # rspecの引数に --drb を渡すことでSporkと通信してくれます
  watch(%r{^spec/factories/(.+)\.rb$})                { "spec/models" }  # 追加
  # ...
end

モデルを作成するとき、rails3-generatorsが spec/factories/ に
ファクトリファイルのひな形を置いてくれるようになっているので、
そのフォルダも監視してあげましょうという感じ。

そしてラスト、config/application.rb:

# Generators
module YourAppName
  class Application < Rails::Application
    # ...略...
    config.generators do |g|
      g.test_framework :rspec, fixture:true, views:false
      g.fixture_replacement :factory_girl
    end
  end
end

これでようやくスタートラインに立てました。
さっそくGuardを実行します。

$ bundle exec guard
Guard uses NotifySend to send notifications.
Guard is now watching at '/home/sandmark/sites/YourAppName'
Starting Spork for RSpec
Using RSpec
Preloading Rails environment
Loading Spork.prefork block...
Spork is ready and listening on 8989!
Spork server for RSpec successfully started
Guard::RSpec is running, with RSpec 2!
Running all specs
Running tests with args ["--drb", "-f", "progress", "-r", "/home/sandmark/.rvm/gems/ruby-1.9.3-p125/gems/guard-rspec-0.7.0/lib/guard/rspec/formatters/notification_rspec.rb", "-f", "Guard::RSpec::Formatter::NotificationRSpec", "--out", "/dev/null", "--failure-exit-code", "2", "spec"]...
Run options: include {:focus=>true}

すでにモデルやコントローラを作成してあった場合は
大量の pending が出迎えてくれることでしょう。
ともあれ、何も問題がなければここでGuardのプロンプトが表示されているはずです。
(詳細については help と入力してEnterしといてください。)

さて、実際のspecファイルはこんな感じになります。
spec/requests/users_spec.rb:

describe "Users" do
  describe "GET /users" do
    before do
      @user = FactoryGirl.create(:user)
      visit "/users"
    end

    context "when 'about username' link was clicked," do
      it "should show user's name", :focus => true do
        click_link "about #{@user.name}"
        page.should have_content(@user.name)
      end

      it "should pop-up hidden message for JavaScript", :js => true do
        click_link "about #{@user.name}"
        page.should have_content("Woo Hoo!")
      end
    end  # context
  end  # GET /users
end  # Users

ここで it ディレクティブに渡しているのは :js と :focus ですが、
今回のエントリに直接関係があるのは :js オプションのほうです。

spec_helper.rb にも書きましたが、
:js => true を設定していると capybara-webkit がJavaScriptエンジンとして動き、
Capybaraがブラウザ(今回のケースではwebkit-server)を起動、接続して動作をチェックするわけです。

しかし headless を導入しているのでブラウザのウィンドウは画面に表示されず、
メモリ上の仮想ディスプレイでうまいことテストしてくれる、というからくり。*4

ただ :webkit が呼び出されたときは webkit-server にデータを渡さなければならないので
RSpec2のタイミングでデータをリセットされると困ります。
そのため、 spec_helper.rb の DatabaseCleaner の設定部分で
一時的に RSpec2 でのデータ管理を止め、 DatabaseCleaner に任せています。

そして :focus => true のときは、Guardが変更を感知し、テストを開始しても、
:focus => true が設定されているテストのみを実行してくれるものです。
ひとつの機能を集中的に実装したい場合は特に役立つでしょう。

というわけで以上、もっと小さくまとめるつもりでしたが冗長な説明になってしまいました。
VMwareからネイティブUbuntuに変えたときに異常にテストが遅かった(50秒近くかかった)ので、
あるあ…ねーよwww と思って調査した次第です。

少しでもお役に立てれば。
それでは Happy Testing!

*1:SporkとRSpec2との仲介もしてくれる上に、結果を自動で通知してくれます。

*2Windowsは知らん。

*3:Mac環境は持っていないのでわからないけど、growlって有料になったんだっけ…?

*4:capybara-webkitはそもそもX無しで動作するので headless 導入の意味があるかどうかは僕にはわかりません。

2012年04月02日

rakeで単純なファイル変換するためのRakefileテンプレート

使いたい時:

  • wav -> mp3
  • bmp -> jpg
  • coffee -> js

要は大量のファイルを一撃で変換するためのタスクです。
大きなプロジェクトでは*1最終的にひとつのファイルを生成することになるわけですが、
単純なルールで各ファイルを変換するだけなら、複雑な依存関係を記述する必要はないわけで。

Rakefile(CoffeeScriptをJavaScriptにコンパイルする場合):

require 'rake/clean'

SRC = FileList["**/*.coffee"]
DST = SRC.ext("js")

CC = "coffee"

CLEAN.include DST

task default: DST

rule ".js" => ".coffee" do |t|
  sh "#{CC} -c #{t.source}"
end

いっつも忘れそうになるのでメモしておきます。
ちなみに rule ディレクティブで引数として渡されている t は
t.name で変換後の名前を取得することができます。
gccなんかを使うときに gcc -c #{t.source} -o #{t.name} なんかはよく見かける断片ですね。

参考: http://www2s.biglobe.ne.jp/~idesaku/sss/tech/rake/

*1:例えば ./configure && make && sudo make install が必要なプロジェクト。