• 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
  • 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

インストール

shell>>
% gem install mongo_mapper
<<--

mongomapper (古いバージョンのgem)も存在するので注意

セットアップ

ruby>>
require 'mongo_mapper'
MongoMapper.database = "app1" # DB名
<<--

  • 各モデル内で明示されない場合に利用されるデフォルトのDB名

モデル

ruby>>
class Player
include MongoMapper::Document
key :name, String, :required => true
key :policy, Integer
key :renkei, String
key :note, String
timestamps!

validates_uniqueness_of :name
end
<<--

  • pkeyのidが勝手に定義される
  • 文字列は長さに制限がないので全てString

検索

ruby>>
Player.count
=> 822

Player.count(:renkei=>'萩原型')
=> 135

Player.first
=> #<Player label: "萩原 忠志" ... >

Player.all(:label=>/^河本/).map{|p| [p.pos, p.label]}
=> [["FW", "河本 鬼茂"], ["GK", "河本 龍将"]]
<<--

高度な設定

  • ARで言うテーブルをMongoDBではコレクションと呼ぶ
  • table を collection に変えるだけでAR風のAPIが使える

ruby>>
class Pref
include MongoMapper::Document

set_database_name "usei" # app1でなく usei DBを利用
set_collection_name "zip" # prefs でなく zip コレクションを利用
...
<<--

長所

  • MongoDB自体の性能がよい
  • インタフェースが AR と DM のよい点を取り込み (CRUDはAR, SearchはDM)
  • バリデーションや関連もサポート
  • 正規表現による検索

短所

  • サーバ(MongoDB)への再接続などのコネクション機能が弱い(mongo_mapper でなく mongo の問題かも)
posted by maiha maiha on Sun 21 Feb 2010 at 15:29 with 0 comments

答え

"!=", "!==" を使う

詳細

文字列のHTMLエスケープはRailsやRack::Utilによって
hヘルパメソッドとして提供されており、
ユーザはエスケープ処理を「hの有無」によって調整します。

|caption=例: hamlでのHTMLエスケープ
|
|コード,結果
|
|= link_to(...),<a href=...
|=h link_to(...),&lt;a href=...

自動エスケープモード

上記のようなユーザ駆動のエスケープ処理では100%の安全性を保証できないため、
hamlでは「デフォルトでエスケープする」というオプションが存在します。

ruby>>
Haml::Template.options[:escape_html] = true
<<--

これにより常時エスケープされるようになるのですが、
今度はh の逆とも言うべき「エスケープしない」方法が必要になります。
これがずっとわからなくて困っていたのですが、
「ドキュメントを見る」という手段を思い出して解決しました。
"!=" を使えばよいようです。

|caption=例: escape_html: true
|
|コード,結果
|
|!= link_to(...),<a href=...
|= link_to(...),&lt;a href=...
|=h link_to(...),&amp;lt;a href=...

posted by maiha maiha on Mon 8 Feb 2010 at 07:55 with 2 comments

link_to 等のヘルパメソッドは sinatra 本体には含まれていませんが、
sinatra_more という gem が用意されています。

install

shell>>
% gem install sinatra_more
<<--

standalone 利用の場合

直接 Sinatra::Base に register してしまいましょう。

ruby>>
require 'sinatra'
...
require 'sinatra_more/markup_plugin'
Sinatra::Base.register SinatraMore::MarkupPlugin
...
get '/' do
...
<<--

クラス利用の場合

該当クラスで register します。

ruby>>
require 'sinatra_more/markup_plugin'

class Application < Sinatra::Base
register SinatraMore::MarkupPlugin
end
<<--

MarkupPlugin

link_to だけでなく、全56種(ver 0.3.33 現在)ものヘルパメソッドが利用できるようになります。
以下に、よく使いそうなものを抜粋します。

ruby>>
link_to(*args, &block)
stylesheet_link_tag(*sources)
javascript_include_tag(*sources)
image_tag(url, options={})
image_path(src)
escape_html(text)
h!(text, blank_text = ' ')
content_tag(*args, &block)
tag(name, options={})
input_tag(type, options = {})
form_for(object, url, settings={}, &block)
text_field_tag(name, options={})
<<--

詳細は lib/sinatra_more/markup_plugin/*.rb を確認して下さい。

その他のPlugin

sinatra_more gem には、
MarkupPlugin 以外にも
RenderPlugin、WardenPlugin、MailerPlugin、RoutingPlugin
といったプラグインが用意されています。
利用方法は MarkupPlugin の場合と同じで、
利用したいプラグイン名を列挙していきます。

ruby>>
require 'sinatra_more/markup_plugin'
require 'sinatra_more/render_plugin'
Sinatra::Base.register SinatraMore::MarkupPlugin
Sinatra::Base.register SinatraMore::RenderPlugin
...
<<--

各プラグインの代表的なメソッドを記載します。
例えば、RenderPlugin にはみんな大好きな "partial" が入ってたりしますので、しっかりチェック&register しましょう。

RenderPlugin

描画系プラグイン

ruby>>
partial(template, options={})
erb_template(template_path, options={})
haml_template(template_path, options={})
render_template(template_path, options={})
<<--

WardenPlugin

認証系プラグイン

ruby>>
current_user
authenticate_user!
logged_in?
authenticated?(&block)
<<--

MailerPlugin

メール関係?(使わないので未調査)

RoutingPlugin

ルーティング系プラグイン (named route?)

ruby>>
map(*args, &block)

map(:accounts).to('/accounts/url')

map(:admin, :show).to('/admin/show/:id')

map(:admin) { |namespace| namespace.map(:show).to('/admin/show/:id') }

<<--

posted by maiha maiha on Thu 4 Feb 2010 at 00:22 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
NoSQLKVSTokyoCabinettokyotyranthamlMongoDBMongoMappermonkOhmredissinatra
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
Services from s21g
MagicBag
MagicBagは、Cloudflare R2やS3互換ストレージをMacのFinderから扱えるファイル同期クライアントです。
アンテナショップめぐり
東京にある全国のアンテナショップ、イベント、特産品を地図や条件から探せるサービスです。
YOMU Web小説リーダー
YOMUは、Web小説の読み上げに特化したiPhoneアプリです。
補助探
公開されている補助金・助成金情報を集約し、条件に合う制度を探しやすくするサービスです。
jotter.me
jotter.meは、考えやアイデア、情報を書き留めるためのノートサービスです。