• 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

Ubuntu 10.04 では、JDKのデフォルトが SunJDK から OpenJDK に変更され、SunJDK はパッケージにも含まれなくなりました。
恐らく、今後の Sun によるサポートを不安視する声が多く、
将来的に安定したパッケージリリースを行うために独立する流れになったのだと思われます。

しかしながら、OpenJDKは若干機能的にSunJDKに遅れている事もあり、
SunJDKが使えないと困る局面があるのも事実です。
その場合、以下のpartnerパッケージを追加することで、
10.04でもSunJDKが利用できるようです。

shell>>
deb http://archive.canonical.com/ lucid partner
<<--

shell>>

aptitude update

aptitude search sun-java6

p ia32-sun-java6-bin ...
p sun-java6-bin ...

aptitude install sun-java6-jdk

<<--

また、OpenJDKと併用している場合は、
下記でパッケージのデフォルト設定を SunJDK にすることが可能です。

shell>>

update-alternatives --config java

<<--

posted by maiha maiha on Thu 27 May 2010 at 14:16 with 0 comments

ext3の1ディレクトリに大量のファイルを配置すると、
ファイルアクセスが遅くなります。
一般的な回避策は、何らかのハッシュ値でディレクトリを階層的にする方法ですが、実際にやるとなると数行でできるとは言えちょっと面倒です。
そこで、これを手軽に実現できるようにライブラリに追加しました。

Pathnameと同じAPIですので、
最初はPathnameで使っておいて、
数が増えたらHashnameに変えるという使い方ができるので便利だと思います。
(マイグレーションも両者で一緒にloopするだけでOK)

ruby>>
import com.s21g.rubyist.{Pathname,Hashname}

val path = Pathname("data/users/910.xml")
val hash = Hashname("data/users/910.xml")

path.path // => "data/users/910.xml"
hash.path // => "data/users/e/2/0/e205ee2a5de471a70c1fd1b46033a75f/910.xml"

hash.write("Love Scala!")
hash.read // => "Love Scala!"
<<--

また、com.s21g.rubyistには先日の rescue の実装も入っていますので、
よかったら使ってみてください。
jar ファイルは Downloads から入手可能です。

参考

posted by maiha maiha on Wed 19 May 2010 at 03:23 with 0 comments

gem push によって、
gem の公開が大変簡単になりました。
しかし、敷居の低さは油断に繋がるもので、
近い将来、誰もが一度は秘匿性のあるファイルを誤って公開してしまい、
パニックになることでしょう。

今回、皆さんを出し抜く形になってしまい甚だ恐縮ではございますが、
一足先に体験させて頂きましたので、
その対処法を報告致します。

手順

  1. gem yank
  2. support staff への報告

1. gem yank

gem の新機能である yank コマンドを利用すると、
指定したバージョンの gem の公開を停止することができます。
例えば、foo-0.0.1 gem の場合、以下を実行します。

shell>>
% gem yank foo -v0.0.1
<<--

この機能は rubygems1.3.6 にはまだ含まれて居ませんので、
"Unknown command yank" と言われた場合には、
以下のコマンドで gem を最新のものにアップグレードする必要があります。(1.3.7以降で、gemcutterも0.5.1以降であれば不要なはずです)

shell>>
gem update --system
gem install gemcutter
<<--

2. support staff への報告

これで、gemcutter の index からは削除され、
gem search および install の対象からは除外されたのですが、
ホームページ上からは依然としてダウンロードが可能な状態になっています。
完全に削除しない理由は、

  • その gem に依存関係を持つ gem が存在し得る
  • 重要な gem (例えば rails) が突然なくなると困る

というポリシーからのようです。
でも、
「パスワードみたいに本当に致命的な場合は、
個別に判断するのでとりあえず連絡下さい」
という方針のようなので、
以下の問い合わせフォームから削除を嘆願します。

http://help.rubygems.org/discussion/new

これは基本的に公開BTSなので、下のほうにある

[x] This is a private discussion, don't let the public see

を忘れずにチェックして、private なメッセージにして投稿しましょう。
これで数日中には完全に削除されるはずですので、
support staff に感謝の気持ちを伝えて作業完了です。

[おまけ] unyank

yank を取り消す(undo)機能も存在しています。

shell>>
% gem yank foo -v0.0.1 --undo
<<--

この yank / undo の一連の操作を覚えておくと、
「もしかしてセキュリティーホールがある?」
「新バージョンでエンバグしてる?」
なんて時には速攻で yank して、
「やっぱ気のせい」
という場合は undo で戻せるので、とても便利です。

参考

Removing a published RubyGem

posted by maiha maiha on Fri 16 Apr 2010 at 17:53 with 1 comment

thor のメリット

thor は rake に比べて、

  • タスク定義が Ruby メソッド
  • コマンドライン引数が簡単に扱える

というメリットがあり、お気に入りです。
前者はもしかすると
「いやいや task の方が DSL 風で...」
と人によるかもしれませんが、
後者が強力なのは異論がないでしょう。
例えば、Rails の db:create を例にすると、
環境を指定するのに rake では仕方なく RAILS_ENV を使っていますが、
もし thor であれば

ruby>>
class Db < Thor
desc "create ENV", "Create database for specified env"
def create(env = 'development')
...
<<--

のように、引数の処理もデフォルト値の指定も、
ruby コードそのままで非常に直感的です。

complete usage

でも、しばらく使っていると、
上記の desc 部分(usage, descprtion)のうち、
前者の usage がメソッド定義と被っていて少しイライラしてきます。
そこで、get_args を利用して usage を補完するように改良してみました。

http://github.com/maiha/thor/tree/complete-usage

これにより、以下のように記述できます。

ruby>>
class Db < Thor
desc "Create database for specified env"
def create(env = 'development')
...
<<--

ruby>>
% thor list
thor db:create ENV = "development" # Create database for specified env
<<--

もちろん、従来の記法も利用可能です。
ユーザを増やして本家に取り込んでもらうと思うので、
よかったら使ってみてください。
バグ報告も大歓迎です。

インストール

で、こういう野良 github アプリはどうするんですかね?
とりあえず適当に clone して、complete-usage ブランチの lib で、
gems/thor-0.13.4/lib とかを直接差し替えるのが一番早い、
かも?です。
あと、もちろん get_args のインストールも必要になります。

ruby>>
% gem install get_args
<<--

posted by maiha maiha on Sun 28 Mar 2010 at 15:37 with 0 comments

Ohm を使っていると色々と不満な点が見えてきます

  • @attributes を得る手段がない (#attributes が column_names を返す)
  • create!, save! がない
  • first, all がない
  • ...

これは AR 脳というよりも、ARの洗練されたAPIと比べてしまうと、
Ohm の未成熟さが浮き彫りになってしまうためだと思います。

ohm-arfreaks

そこで、ARのメソッドをOhm上で使えるようにしたラッパーを作成しました。

http://github.com/maiha/ohm-arfreaks

インストール

shell>>
% gem install ohm-arfreaks
<<--

ruby>>
require 'ohm'
require 'ohm-arfreaks' # これを追加

class Video < Ohm::Model
attribute :url
set :tags
end

Video.create!(:url=>"a")
Video.first.attributes
=> {:url=>"a", :tags=>[]}
<<--

以下のメソッドが利用可能です (0.1.0 現在)

  • self.primary_key
  • self.columns
  • self.column_names
  • self.content_columns
  • self.columns_hash
  • self.create!
  • save!
  • self.first
  • self.last
  • self.count
  • self.delete_all
  • new_record?
  • attributes

ARと比べると使えるメソッドは少ないですが、
これだけでも随分Ohmが快適になると思います。

backup

例えば、redis はどこに DB を作っているかわかり辛いので
時々バックアップが欲しくなるのですが、
昔懐かしのar_fixtures plugin 相当のことは以下のコードでできるようになります。

ruby>>
File.open("videos.yml", "w+") do |f|
hash = Video.all.inject({}){|h,v| h[v.id]=v.attributes;h}
f.print hash.to_yaml
end
<<--

posted by maiha maiha on Tue 16 Mar 2010 at 18:58 with 0 comments

現状

shell>>
% gem --version
1.3.6

% ./script/console
Loading development environment (Rails 2.2.2)
/usr/lib/ruby/gems/1.8/gems/rails-2.2.2/lib/rails/gem_dependency.rb:224:in specification':NoMethodError: undefined method version_requirements=' for #Gem::Dependency:0xb7769604
<<--

  • rails 側での対応はなし
  • rubygems 側は trunk で修正済
  • rubygems の新バージョンはなかなか出ず

対策

  • rubygems の trunk から最新版をインストールする (git じゃないのがダサイ)

インストール

(/tmp/ あたりの適当な場所で実行)

shell>>
% svn export http://rubygems.rubyforge.org/svn/trunk/ rubygems
A rubygems
A rubygems/History.txt
...
リビジョン 2460 をエクスポートしました。

% cd rubygems

ruby setup.rb

<<--

(setup.rb の実行でインストールされます。必要に応じてrootで)

確認

ruby>>
% ./script/console
Loading development environment (Rails 2.2.2)
/usr/lib/ruby/gems/1.8/gems/rails-2.2.2/lib/rails/gem_dependency.rb:224:Warning:...
/usr/lib/ruby/gems/1.8/gems/rails-2.2.2/lib/rails/gem_dependency.rb:224:Warning:...
...

User.count
=> 25707
<<--

warning が一杯出るが気にしない。
rubygems 1.3.7 が出るまで、とりあえずこれでOK。

posted by maiha maiha on Sun 7 Mar 2010 at 13:20 with 0 comments
  • ActiveRecord の TC/TT アダプタ
  • AR 上で動作するので全てのAPIが利用可能

インストール

shell>>
% gem install activetokyocabinet
<<--

サーバの起動

shell>>
% ttserver -port 11114 db.tct &
<<--

  • ポート(11114)は何でもよい
  • ファイル名も何でもよい (拡張子はtct)

セットアップ

ruby>>
require 'active_tokyocabinet/tdb'

ActiveRecord::Base.establish_connection(
:adapter => 'tokyotyrant',
:database => {
:englishes => {:host => 'localhost', :port => 11114},
}
);
<<--

  • adapter 名は 'tokyotyrant'
  • database で利用するテーブル名と参照するTTの設定を指定する
  • 以下で Englishモデルを使うので "englishes" を定義している
  • 複数のモデルを使うときはやっぱりTTが複数必要?

使い方

ruby>>
class English < ActiveRecord::Base
include ActiveTokyoCabinet::TDB

string :word
int :length

validates_presence_of :word, :length

def validate
self[:length] ||= word.to_s.size
end
end
<<--

  • ARなので validate でも何でも思い通り
  • カラム定義は string, int, float (date/datetimeはない?)

ruby>>

/usr/share/dict/words を流し込み

buf = File.read("/usr/share/dict/words")
buf.scan(/^([a-z]+)$/) {
English.create!(:word=>$1)
}

English.count
=> 64024

English.all(:conditions=>["word regexp ? and length > ?", '^mai', 12])
[#<English id: 33247, word: "mainstreaming", length: "13">,
#<English id: 33250, word: "maintainability", length: "15">]
<<--

  • 正規表現の検索も可能

ruby>>
e = English.first
e.delete
ActiveRecord::StatementInvalid: NoMethodError: undefined method values_at' for 1:Fixnum: DELETE FROM englishes WHERE (id IN (1)) from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/abstract_adapter.rb:219:in log'
<<--

あれ!?あれ?

まとめ

  • ARで稼動する上に正規表現による検索も可能
  • 削除(delete, destroy)ができないのはご愛嬌

で、一番驚いたのは、実装方法。
普通なら、ARの各メソッドをTT化していきそうなものだが、

  1. ARにSQLを作成させて
  2. そのSQLをパーズして
  3. TTに翻訳する

その発想はなかった。目から鱗です。
というか、SQLのparserは実用性が高いのでまず、
それをgem化希望!激しく希望!!

参考

posted by maiha maiha on Fri 26 Feb 2010 at 01:28 with 2 comments

TokyoTyrant を ActiveRecord 風のAPIで利用するライブラリ

インストール

shell>>
% gem install miyazakiresistance
<<--

セットアップ

適当なポートで TT を起動。(テーブルデータベースを利用するので *.tct)

shell>>
% ttserver -port 11114 services.tct &
<<--

使用例

ruby>>
require 'rubygems'
require 'miyazakiresistance'

class Service < MiyazakiResistance::Base
set_server "localhost", 11114, :write
set_timeout 1

set_column :name , :string
set_column :port , :integer
set_column :proto, :string
end

buf = File.read("/etc/services")
buf.scan(%r{^(\w+)\s+(\d+)/(udp|tcp)}) {
Service.create(:name=>$1, :port=>$2, :proto=>$3)
}

Service.count
=> 373

Service.first
=> #<Service:... @id=1, @port=1, @proto="tcp", @name="tcpmux">

Service.find_all_by_port(80)
=> [#<Service:... @id=40, @port=80, @proto="tcp", @name="www">,
#<Service:... @id=41, @port=80, @proto="udp", @name="www">]
<<--

  • データ型は :string, :integer, :date, :datetime
  • master/slave, dual master をサポート
  • ARのdynamic finderもサポート
  • (created|updated)_(on|at) は magic column
  • TTへの保存キーは id の値 (数字の連番が自動付与)

欠点

  • スキーマを途中で変更するとエラー (DB内は同じスキーマのデータが必要)
  • 1モデル毎にTTサーバが1つ必要になる?
  • :date, :datetime を空にできない (Time.at(0) になる)
  • "set_" prefix が冗長

という実装を見る限り、想定されたユースケースは、

  • 任意のドキュメントを格納

ではなく、

  • 固定されたスキーマ定義によってTTをRDB的に利用

のようだ。
ARもASも必要としないので、「TTを手軽に便利にCRUDしたい」
という用途にはピッタリだろう。

参考

posted by maiha maiha on Thu 25 Feb 2010 at 08:24 with 0 comments

TokyoCabinetとは

  • 高速なKVS
  • mixiの平林さんが作成
  • mixiの高負荷で運用されている性能と実績
  • 永続化機能あり (memcachedに対する利点)
  • 効率的、並列可、単純なAPI
  • 単純なKVS(hash)だけでなく、B+木、テーブル(hashを値に取る)も利用可能
  • 仕様書: Tokyo Cabinet第1版基本仕様書

また親類が多く、用途に応じて使い分けることができる

  • Tokyo Cabinet : KVSライブラリ
  • Tokyo Tyrant : TCのネットワーク対応版
  • Kyoto Cabinet : KVSライブラリ(TCとは別方向の実装)

開発順序も同じで、TCというKVSを作って、TTはそれをネットワークに対応させたもの。

TCとKCの違い (余談)

じゃあ、KCって何?何でまたKVSの作成に戻るの?後継なの?TCより強いの?
て気がするが、一言にすると、TCはシングルスレッドでの最速を追求した実装。
(汎用的だが若干速度面で甘さのある)既存のライブラリには一切頼らず、
TCのために最適化された部品を自作し、速度という神の一手を追求した「攻撃的な実装」。
言わば、一瞬の隙を見逃さない久保棋王の将棋。
でもそれは一人でやるには開発以上に保守が大変になってくる。
それに対して、KCは個々の部品レベルでの最善の一手の追求は少し緩めても、
マルチプロセスで性能が出るように再設計し、
既存のライブラリを使ってでも保守性を高めて、
結果的にトータルでの最速を目指した「負けない実装」。
言わば、渡辺竜王の将棋。
したがって、(まだ発展途上なせいもあって)シングルスレッドではTCの方が速いが、
将来を期待させてくれるツールになっている。
ということで、KCは暖かく見守りつつ、今はTC(TT)を使うことになる。
(以上、全て推測)

インストール

shell>>
% gem install rufus-tokyo
<<--

使用例 (TC)

ruby>>
require 'rubygems'
require 'rufus/tokyo'

t = Rufus::Tokyo::Table.new("foo.tct")

t['gem1'] = {:name=>'sinatra', :minor=>9}
t['gem2'] = {:name=>'monk', :minor=>0}

gems = t.query { |q|
q.add_condition 'minor', :numge, '1'
}

=> [{"name"=>"sinatra", :pk=>"gem1", "minor"=>"9"}]

t.close
<<--

直接ハッシュを扱うため、ORMというよりHVM(Hash-Value Mapping)。
というかそもそも tokyocabinet ライブラリを直接使うのと殆ど違いが見えない。
恐らく利点は

  • 全体的に記述が Ruby ぽい (エラー処理とか)
  • transaction サポート (ブロックで記述できる)

あたりだろうか?

ruby>>
t.transaction do
begin
t['gem1'] = {:name=>'sinatra', :minor=>9, :author=>'user1'}
t['user1'] = {:name=>'bmizerany'}
rescue
t.abort
end
end
<<--

うーん、なんか微妙かも。
やっぱりObjectに対してCRUDしたいよね。
とか思ってたら、作者(jmettraux)から
「oklahoma_mixerの方がいいよ」
とアドバイスを頂いた。ダメじゃん。
というか、tokyocabinet が撒いた種とは言え、
関連ライブラリの名前の弾け方が凄い>oklahoma_mixer, miyazakiresistance。

posted by maiha maiha on Wed 24 Feb 2010 at 16:54 with 0 comments

redis とは

インストール

shell>>
% gem install ohm
<<--

セットアップ

ruby>>
require 'ohm'
Ohm.connect(:port=>6379)
<<--

モデル

ruby>>
class Video < Ohm::Model
attribute :url
attribute :created_at
set :tags

index :url # 検索対象には全てindexを作成する
index :tags # 検索対象には全てindexを作成する
end
<<--

  • pkeyのidが勝手に定義される
  • redisで利用可能なvalueの型は、文字列と集合
  • Ohmでは文字列型を attribute で定義する
  • Ohmでは集合型を set で定義する
  • Ohmではさらに独自の list(順序付き集合), counter(増減のみ操作可能な数値) 型が利用可能

使用例

ruby>>
video = Video.new(:url=>"http://www.you...", :tags=>"愛理")
video.save
video.tags << "cute"

Video.find(:tags=>"cute")
=> #<Index: ["1", "2"]>

Video.find(:tags=>"cute").last.tags
=> #<Set: ["cute", "愛理"]>
<<--

低レベルAPI

Object-Hash ではなく、純粋に redis の KVS 用 API として利用する場合。

ruby>>
Ohm.redis.get "Foo" # => nil
Ohm.redis.set "Foo", "xxx" # => "OK"
Ohm.redis.get "Foo" # => "xxx"
<<--

長所

  • redisのKVS性能が高いので単純レコードへの参照・記録は超高速
  • 集合型を持つのでタグの扱いなどが凄く便利
  • スキーマレスでとっつきやすい
  • 各操作がアトミックなのでロックがない

短所(redis)

  • 文字列型のみ(数値や日付を入れても範囲検索できないので実用的ではない)
  • 検索機能が貧弱 (文字列の完全一致のみ)
  • データ保存が async
  • もちろんトランザクション処理もなし

保存時間の間隔を設定できるとは言え、基本 async なのでクラッシュによるデータ損失の危険性が常にある。
その意味では、RDBのようなしっかりとしたデータストレージでなく、
前回の状態を(運が良ければ)そのまま再開してくれる memcached
という認識(利用)がよさそう。

短所(Ohm)

  • 予約語チェックがなく、id 属性を指定すると謎の挙動でハマる
  • new record 時に集合型を参照するとエラー

など使い辛い

posted by maiha maiha on Mon 22 Feb 2010 at 07:04 with 0 comments
Contents rssrss
Ubuntu 10.04 (lucid) でSunJDKを使う方法
Hashname: 大量のファイルをScalaで楽に扱う方法
公開した gem を削除する方法
thor で usage の自動生成
[NoSQL] ohm-arfreaks (redis用のAR風ORM) の使い方
rubygems-1.3.6 を入れてしまうと rails-2.2.2 が全滅する件
[NoSQL] activetokyocabinet (TokyoTyrantのORM) の使い方
[NoSQL] MiyazakiResistance (TokyoTyrantのORM) の使い方
[NoSQL] rufus-tokyo (TokyoCabinetのORM) の使い方
[NoSQL] Ohm (redis用のORM) の使い方
Tags
NoSQLKVSTokyoCabinetOhmredisrubygemstokyotyrantActiveRecordjavarailsRubyistScalaSunJDKthorubuntu
Comments rssrss
maiha maiha: 追記)対応は約1営業日でした。チームの忙しさに応じて変動するとは思いますが、大体の目安情報ということ... 04/19 07:39
maiha maiha: なるほど。rails のログから実行したクエリを抜き出したいと思うことがよくありまして。 あ、削除... 03/08 04:15
winebarrel winebarrel: レポートありがとうございます。 削除できない件は修正しました。 SQLのparserはTC/TTに... 02/27 04:40
maiha maiha: なるほど。ありがとうございます! 02/08 18:19
ursm ursm: Haml 2.2 以降はどこでも式展開が使えるようになったので、== は意味がなくなりました。! だ... 02/08 10:23
walf443 walf443: それgit remote -vでできるよ 01/18 02:10
maiha maiha: reset だと範囲が tree 全体になるよね?例えば 「lib/* 以下は正しく修正できていて ... 08/29 21:32
Leonard Chin (レオ) Leonard Chin (レオ): git reset --hard でいいんじゃない? 08/28 12:32