開発


21
Aug 10

rvmふたたび

ruby 1.9.2が出たのでrvmを入れ直して整理した。

$ gem install --user-install rvm
$ PATH=$HOME/.gem/bin:$PATH
$ rvm-install
$ vi ~/.zshrc # ~/.gem/binをパスに追加、rvmの初期化コードを追加
$ exit

1.9.2と1.8.7をインストールする。 ただしMacPortsで入れたreadlineを使うために今回はオプションを付ける。

$ rvm install 1.8.7 -C "--enable-shared=true,--with-opt-dir=/opt/local"
$ rvm install 1.9.2 -C "--enable-shared=true,--with-opt-dir=/opt/local"

とりあえず1.8.7にRails2.3のgemsetを作り、1.9.2にRails3 RCのgemsetを作る。 そして前者をデフォルト(シェルを立ち上げたときに最初に読み込む)のgemsetとする。

$ rvm use 1.8.7@rails23 --create --default
$ gem install rails -v 2.3.8
$ rvm use 1.9.2@rails3rc --create
$ gem install rails --pre

rails2.3で作ったプロジェクトでは1.8.7@rails23を使いたいので、RAILS_ROOTで次のように.rvmrcを作る。rails3rcのほうも同様。

$ echo "rvm use 1.8.7@rails23" > .rvmrc

17
Aug 10

Rails 2.2.xでsession_idをもとにセッションにデータを突っ込む

例えば、外部にある認証系がこちらのAPIを叩く場合など、セッションが継続してないからsessionからデータを入れられないんだけど、session_idはわかってるからどうにかしたいな、というときに。

今更ながらRails 2.2.2で試してみた。2.3.x以降ではCGIモジュールは破棄されてRackに移行したので、全く変わってる気がする。

class SomeController < ActionController::Base
  session :off
  def index
    session_id = (get session_id ...)
    
    # CgiRequestとRackRequestに対応
    options = request.class.get_const("DEFAULT_SESSION_OPTIONS").merge(:session_id => session_id)
    my_session = CGI::Session.new(request.cgi, options.stringify_keys)
    my_session[:foo] = :bar
    my_session.close
  end
end

22
Jul 10

MeCabをrubyから使ってみる

インストール

まずMacPortsで本体と辞書を入れて読み込む辞書を設定する。

$ sudo port install mecab
$ sudo port install mecab-ipadic-utf8
$ sudo vi /opt/local/etc/mecabrc # 編集内容は下記を参照

mecabrcは次のように書き換える。

dicdir = /opt/local/lib/mecab/dic/ipadic-utf8

次にrubyバインディングのmecab-rubyをインストールする。 ソースを SourceForge から落としてきて展開し、そのディレクトリで以下を実行。

$ ruby extract.rb
$ vi Makefile # 編集内容は下記を参照
$ make
$ ruby test.rb # これでエラーが出なければ問題無し
$ sudo make install

Makefileは次のように/opt/local/libを読むように修正を加える。

LIBS =  -lstdc++ -ldl -lobjc -L/usr/local/lib -L/opt/local/lib -lmecab

参考: MeCab + Ruby で形態素解析(わかち書き)

rubyから使う

irbで次をやってみる。

require 'MeCab'
wakati = MeCab::Tagger.new('-O wakati')
puts wakati.parse('最近の夜は寒い')

mecab = MeCab::Tagger.new()
node = mecab.parseToNode('最近の夜は寒い')
while node do
  puts "#{node.surface}\t#{node.feature}"
  node = node.next
end

参考: MeCab + Ruby で形態素解析(わかち書き)

地味にはまったのがnewするときの引数。特にオプションを指定しないなら上記のようになにも与えないか、もしくは空の文字列を与える。明示的にnilを与えるとエラーになるので要注意。


7
Jul 10

モジュールでnamed_scopeを追加

モデルの機能ごとにモジュールにまとめてあとからまとめてincludeしたいというときに、どうせならその機能のために用意したちょっと複雑なnamed_scopeも同じように外に出してしまいたい。

そんな場合にはincludedとclass_evalを使う。

class Message < ActiveRecord::Base
  include Extensions::Search
end

module Extensions
  module Search
    def self.included(mod)
      mod.class_eval do
        named_scope :name_like, lambda { |arg| {
          :conditions => ["name LIKE ?", "%%arg%%"]
        }}
      end
    end
  end
end
> Message.name_like("ob").first.name #=> "Bob"

named_scopeも結局はクラスメソッド。この方法はnamed_scope以外にも利用できる。


19
Jun 10

テスト用oauth_callback

oauth gem(0.4.0)のoauth_callbackでちょっとはまったのでメモ。

/rubyを参考に、Twitterに登録しているコールバックURLとは別にテスト用のURL(が使いたかったので、いろいろ試してみた結果、次で動いた。

test_callback  = "http://example.org/login"
request_token = consumer.get_request_token(:oauth_callback => test_callback)
aurhorize_url = request_token.authorize_url(:oauth_callback => test_callback)

なんで両方で指定しなければならないのかは謎。


19
Jun 10

iPhoneアプリ申請のチュートリアルまとめ

iPhoneアプリを開発する際にiPhone Dev Centerでやる作業がこまごまとして面倒くさいので、そのときに参考にしたサイトをまとめておく。

テスト端末の登録

アプリを実機で動かす を参照。

このときProvisioning Portalで作成したAppIDは申請フローの際に使い回す。

審査の申請フロー

全体の流れは 【iPhone】App Store申請用アプリをビルドする を参照するとよい。

審査をするアプリを作る際のDistributionというConfigurationの作り方は 目指せ!iPhoneアプリ開発エキスパート 第10回 App Storeでアプリを開 を参照。

申請の際、スクリーンショットの追加方法がちょっと分かりづらい。 Choose Fileボタンでファイルを追加していき、Upload Filesボタンで一気にアップロードする。 このとき優先度の低いものから追加していく。つまり一番最後に追加した画像がPrimary Screenshotとして扱われる。

iAd、Game Centerまわりの影響で、ここ1ヶ月ぐらいで申請フローがちょっと変更されたらしい。iAdは使用しないならEnable欄は操作せずにContinueして良し、Game Centerも利用しないならNoでContinueすれば良し。


10
Jun 10

コマンドラインからWEBrickを起動

一時的に任意のディレクトリをHTTPサーバに公開したくなったので、WEBrickを起動するスクリプトを組んでみた。既存のものがある気がしなくはないけど、OptionParserの勉強という意味も含めて。

#!/usr/bin/env ruby

require 'optparse'
require 'webrick'
include WEBrick

# WEBrickを起動する際のオプションのデフォルト値
WebrickOptions = {
  :Port => 8000,
  :DocumentRoot => Dir::pwd,
}

OptionParser.new do |opt|
  # パース方法を設定する
  # オプションが指定されていた場合にブロックが評価される
  opt.on('-p', '--Port N', Integer) { |v| WebrickOptions[:Port] = v }
  opt.on('-d', '--DocumentRoot PATH') { |v| WebrickOptions[:DocumentRoot] = v }
  
  # 実際にパースを行う
  opt.parse!(ARGV)
end

# WEBrickを起動する
s = HTTPServer.new(WebrickOptions)
trap("INT") { s.shutdown }
s.start

オプションを指定しない場合は8000番ポートにカレントディレクトリをドキュメントルートとして起動する。

$ ruby webrick

-pオプションもしくは–Portオプションでポート番号を指定し、-dオプションもしくは–DocumentRootでドキュメントルートを指定する。

$ ruby webrick -p 8080 -d ~/Sites

これらの値を指定しない場合にはOptionParser::MissingArgumentが投げられる。

$ ruby webrick -p
#=> OptionParser::MissingArgument

なおここでは使っていないが、オプションの引数を必須としない場合には次のように[を含める。

  opt.on('-p', '--Port [N]', Integer) { |v| WebrickOptions[:Port] = v }

引数が与えられたときにはopt.onのブロックにその値が渡され、引数が与えられなかった場合には「オプションが指定された」という意味でtrueが渡される。

参考文献


2
Jun 10

(挫折)NSPreferredMailCharset on Snow Leopard

NSPreferredMailCharset on LeopardをSnow Leopardに対応させようとしたけど、ちょっと挫折した。

やったことをとりあえずメモっておく:

  • method_impをmethod_(set|get)Implementationに書き換える
  • Info.plistのSupportedPluginCompatibilityUUIDsに、Mail.appとMessage.frameworkのInfo.plistに書かれているPluginCompatibilityUUIDsを列挙する
  • ビルド対象のアーキテクチャを32bit/64bit対応のUniversalに設定する

Console.appを見ながら作業したのでここまでは合ってるはずなんだけど、ここで”Domain=NSCocoaErrorDomain Code=3588″が出た。どっかでエラーが出てるんだけど、今すぐにはちょっとわからない。


20
May 10

名前空間付き定数対応のObject.cost_set

Object.const_getをハックした話 を参考に、Object.const_setもモジュールによる名前空間付き定数に対応させてみた。車輪の再発明をした気がしなくはないけど、そこは気にしない。


class Object
  def self.nested_const_get(name)
    stack = (name.is_a?(Array))  ? name : name.split("::")
    klass = Object
    while const = stack.shift
      klass = klass.const_get(const)
    end
    return klass
  end

  def self.nested_const_set(args = {})
    args.each do |name, value|
      stack = (name.is_a?(Array)) ? name : name.to_s.split("::")
      last_const = stack.pop

      klass = nested_const_get(stack)
      klass.const_set(last_const, value)
    end
  end
end

使い方は、

Object.nested_const_set("Foo::Bar" => "foooo")

RSpec: Stubbing RAILS_ENV and other constantsのように、一時的に定数を換えたいという場合に使えると思う。

ただしまだテストはしていない :)


28
Mar 10

非同期処理にはWorkling

Ajaxで非同期に処理をキックして、一定間隔でその進捗を確認して…というのを書く必要があったのでBackgrounDRbを試してみたら、どうも使いにくかった。理由としては、

  • MetaWorkerのサブクラスでワーカを実装したのに、MiddleMan.workerで呼び出したときにはRailsWorkerProxyにすり替わっている
  • キューイングはしないのに、キューイングのためのテーブルを作らないとエラーが出る
  • 名前が好きじゃない
  • 引数のハッシュに:workerが妙に出て来て冗長
  • RSpecでテストしづらい(できなくはない)
  • APIがよくわからない

後半はなんだか個人的な理由だけど、とにかく調べるのに疲れたわりに、あまり実り多い感じではなかった。

で、その代わりに見つけたのがWorkling。これは非同期に処理を呼び出すためのプラグイン。サンプルは、githubのREADMEの通りなので特に書かない。

キューイングサーバが必要ならばStarlingと組み合わせる。これはTwitterでの利用実績もあるらしい

ただ注意する必要があるのは、進捗を保存しておくサンプルでWorkling::Return::Store::MemoryReturnStoreが利用されてるけど、これはそのプロセス内でのみ有効という点(ただのハッシュに値を保存してるだけ)。なので、バックグランドで進行中のところをAjaxで見に行く場合などにはその値にはアクセスできない。別途memcacheなどに値を保存しておく必要がある。

もしかしたらWorkling::Return::Store::StarlingReturnStoreを使えばいいのかもしれないけど、Starlingはキューイングのサーバだしなぁと詳しく把握しきれていなかったので、とりあえずmemcacheに直でアクセスするものを自分で追加した

追記1

調べてみたところ、StarlingReturnStoreはキューと関係なくただ単にmemcacheをラップしてるだけ(より正確には、StarlingReturnStoreはWorkling::Clients::MemcacheQueueClientをラップしていて、こいつはMemCacheを使ってconfig/workling.ymlで設定されたmemcacheサーバと接続してデータのやりとりをしてる)。

なのでStarlingReturnStoreを使うのが手軽で、接続先memcacheサーバの設定をworkling.ymlに外出しできるので絶対的に便利。