2014年 導入してよかったもの等

先日、"2014年買ってよかったもの" などというエントリ書ける人間たいしたもんだ、日々記録やアウトプットをしていてえらい、みたいな話をして、自分は思い出せないからまあいいかと思ったんだけど、それで終わらすと何も始まらず終わるので頑張って思い出しました。一応、WEB 系エンジニアのような仕事するにあたって導入してよかったもの等です。

空気のような存在になったもの

導入してあたりまえのように使われたものたち (ゆえに忘れていた)。

アプリケーションをつくる英語 (電子書籍版)

アプリケーションに関する英語について書かれているもので、英語っぽいものを書くのに役立ちました。読むじゃなく書く。クラスやメソッドなどの名前をつけるときや、英語でやりとりされているリポジトリにコミットするとか、そういうときに利用されています。これの PDF 版と辞書 (Dictionary.app) とを常に開いてあります。ローカルにデータあるとやっぱり速いし、外でちょっと調べたいときとかにオフラインで利用できると便利です。それからアプケーションで利用される英語、というコンテキストでまず探せるので、英語力ない者としてはありがたいです。

direnv

意外と2014年に入ってから導入、Rails (Spring) でオススメされてたので使い始めたような記憶。特定のプロジェクトのディレクトリに入ったときに、そこにある bin というディレクトリにパス通す程度でしか利用できてないから、プロジェクトはじめるときに存在を思い出します。

普通に使うようになったもの

IntelliJ IDEA

型がある言語で IDE つかうとはかどりそうという目論見から導入されました。不慣れなライブラリを使いはじめるときに、どういうインタフェース持ってるのかとか、どういう定義されてるのか見にいったりとかそういうときに便利です。クラス名とか package 変えるみたいな変更のときにリファクタリングと称して一括名前変更できるのが一番便利です (このためだけに起動する価値あり)。慣れてきたら普段使いのエディタで書いてしまうけど、天才ではないので本来ならばそのままツールの支援を得て書くべきだと思います。REPL がある環境だと、数行のコードの単位で試してフィードバックが得られるため、オッこれは Rails みたいにやってけるぞ、みたいになってエディタとコンソールで書いてくような習慣が抜けないです。
ちなみに Rails っぽく型のある言語導入してくのには Skinny Framework が良い気がします。オフィシャルに Skinny framework's concept is Scala on Rails ですゆえ。モデルまわりのつかいごこちは驚きが少ない感じがあってよいです。静的型付言語、外からくる値に対して型をつけてくところがダルい印象なので、Skinny ORM (scalikejdbc) の TypeBinder とかにおもてなしを感じます。

意外と使わなかったもの

最近出てきたチャットツール各種

Slack とか。きちんとプロジェクトで利用しはじめたらまたちょっと違ったかもしれないけど、仲間内で使う分にはやっぱ IRC だね、みたいな感じで移行に至らない感じでした。チャンネルにいる人間の倍くらい IRC bot がいたり、ログが保存されて一応検索できる仕組みとか WEB インタフェースやら API があるなど IRC まわりの環境がそこそこ発達しているという背景もあったので、周辺のツールたちに嫌気がささない限りは IRC 使い続けそうな気がします。

peco

最初は嬉々として使ってたけど一度使う習慣なくなったら存在を忘れたうえに、もともと使ってた zaw も使わなくなってた。ちゃんと使ったら便利だと思います。シェルのヒストリ絞り込みで生きていく、みたいになっています。



感想

保守的。

エディタ から dayone

やりたいこと

  • エディタで書いたテキストを Day One に追加したい

前提

  • Mac
  • ビム
  • あんまりむずかしいことはわからない

今回の対応

エティタのプラグインとか、ラッパースクリプトさがしたりしたけど、エディタのバッファを dayone コマンドにわたすのでいいか~ となりました。

:w !dayone new

(わ びっくりだよね ニュー と内的発話です。)

準備
  1. http://dayoneapp.com/tools/ から dayone-cli.pkg をダウンロードして dayone コマンドを使えるようにする (dayone コマンドは位置情報などを付加しない点について留意する必要があります)
手順
  1. エディタを起動する
  2. テキストを書く(必要に応じ :setl ft=markdown などとすると、色がつくかと思います)
  3. バッファを外部コマンド dayone new にわたす(かきこむ)

毎日のメモを一箇所にまとめたいのでオススメされていた Day One をつかいます

背景

毎日のメモをひとところにまとめたい。

要件

  • 複数の端末でメモを書けること
    • ケータイ端末から入力できること(とりあえず iPhone)
    • キーボードのついた端末から入力できること(とりあえず Mac)
  • 入力内容を複数の端末で参照できること
    • 非同期でかまわない
    • 再編集の可否を問わない

希望

  • コンソールからの入力が可能であること
  • オフラインで編集可能であること
  • 人間が読める形式で保存されること
    • エクスポート処理など特別な処理をしないでデータにアクセスできること
    • 特殊なファイル形式でないこと(テキストファイルを希望)

その他

  • お金を払ってもよい

結果

今回採用したもの
  • Day One
今回見送られた候補

Day One

オススメされていた。完璧な感じではないけど、いい感じな気はする。

気に入った:
気に入らない(または問題点):
  • データが XML (plist)

その他:

  • 有料(とりあえずアプリケーションの分だけ)
  • 同期は iCloudDropbox を選べる
  • 1エントリ1ファイル というのが、良いかどうか判断しかねる(アプリをとおしてみれば、日毎にまとめてくれる)

howm

テキストエディタプラグインする自分用 wiki っぽいメモ機能。

気に入った:
  • エディタと相性がよい
  • けっこう機能が充実している
  • テキストフォーマット
気に入らない(または問題点):
  • モバイル端末からの入力が困難
    • エディタのプラグインで実現していることが裏目にでている気がする
    • タッチデバイスにエディタ導入して howm つかうとかはナシ (可能かどうかの話ではない)
その他:

データの同期については Dropbox などのサービスが存在しているため、テキストデータを扱うならさほど困難でないと考える(データを扱う単位によってはマージの難しさがある)。

howm と似たような位置にあって、もっとシンプルな方式の ChangeLog メモという選択肢もみてみたが、これもモバイル端末との相性があまりよくない(MHODiA というアプリがある: https://itunes.apple.com/jp/app/id431691790)

Evernote とそのクライアントたち

今 PostEver という iPhone アプリをつかっている(https://itunes.apple.com/jp/app/id422023962)。1日の入力を1つのノートにどんどん追記していく(日毎に1つのノートになる)。
PostEver のノートに Mac からの入力があいのりできるならそれでよかった(現状簡単にきれいにできない)。

気に入った:
  • PostEver は入力に徹していてよい(テキストを入力するためのフィールドしかないため、過去の入力は他のクライアントで参照することになる)
  • Evernote の機能を利用できる
  • Evernote は関連のアプリケーションが充実している
気に入らない(または問題点):
  • PostEver に相当する Mac クライアントがない
  • データへのアクセスは常に Evernote を経由する必要がある
その他:
  • EvernoteREST API を持ってたら話がかわってくる可能性あるが thrift
  • 今みたら PostEver2 という iOS7 対応版の別アプリがあることに気付いてウッとなった(Tweetbot, Tweetbot2, 3... 問題)


以下は思ったことなど。

雑感

観点としては以下について重点を置いていたように思われる:

  • データをどこにどうやって置くか
    • 複数の端末で同期可能な箇所にあること(プライベートでさえあれば手段は問わない)
  • どのように見えるか
    • 日毎にまとまっているのが好ましい

Evernote の1アカウントをもって一箇所とみなす、という雑な運用でもよかったかもしれないという迷いがある。見せかたの問題だったのかもしれない。

Dropbox について

サードパーティのクライアントを認証すると $HOME/Dropbox/アプリ みたいなディレクトリを作られてつらい気持ちになるが、 落ち着いて $HOME/Dropbox/Apps にリネームすればよさそう。Day One が Dropbox で同期されているディレクトリがわからなくなってしまうので、一旦終了させてから起動して、ディレクトリを教えてあげれば認識する。
iPhone アプリのほうは、どういう仕組みになっているかわからないが、とりあえず上記手順ののちインストールして同期設定を行った。

複数端末で同期するということについて

データの扱いについて、同時更新の問題があるため以下のような対応になるのかなと思う:

  • データの単位について、
    • 1端末から1ファイル(データ)単位で更新する
    • 1更新で1ファイル(データ)を追加する
  • ロックはあまり現実的でない

Day One では、1更新で1ファイルを追加し、編集の際は衝突した事実はのこるものの基本後勝ちというルールっぽい。同時更新すると以下のようにコンフリクトしたファイルがつくられる。

ls -1 ~/Dropbox/Apps/Day\ One/Journal.dayone/entries
FF04BE2FE3304849B025ABAF87150748 (Watanabe Mayu's conflicted copy).doentry
FF04BE2FE3304849B025ABAF87150748.doentry

もしコンフリクトした場合は https://dayone.zendesk.com/hc/communities/public/questions/200028729--Conflicted-copy-on-Dropbox-sm- の、ファイル消して作りなおす、というのがオフィシャルな回答っぽい。
ファイル名が UUID (version 4) のハイフン抜きにしたもので、中身の XML(plist) が適切であればクライアントが何であってもよさそうなので、別ファイルにしてあげてもよいかもしれない。

テキストのメタデータについて

テキストデータでメモ行為をしようとすると、メモ本文とは別に管理のためのメタデータをどこに持つか問題が常につきまとう気がしていて、今自分の認識では、本文にちかいところに決まったフォーマットで入っているほうがあつかいやすそうという感じです。ChangeLog 形式とか XMLメタデータ持つのがわかりやすくて処理しやすくてよさそう。
メタデータファイルとかインデックスファイルを別に持つというのはキャッシュ用途でなければ端末間の同期とかの都合でめんどくさそうという印象。

総括

そういうわけで、メモ行為について、しばらく Day One つかって生活してみます。

DbCharmer commit:b02576644d982f895365e796ba75b825f7104210 時点の README.rdoc のざっくり読み下し

"DbCharmer" は、シンプルでパワフルな ActiveRecordプラグインです。ActiveRecord が複数のデータベースや複数のデータベースサーバで動作できるよう拡張します。

このライブラリが ActiveRecord に追加する主な機能は以下のとおり:

  1. AR model のコネクションをシンプルに管理 (switch_connection_to メソッド)
  2. AR model をデータベースを分割したコネクションに
  3. クエリをどこにむかわせるか簡単に選べる機能 (Model.on_* メソッド群)
  4. 自動でマスタ・スレーブにクエリをむかわせる (参照はスレーブに、更新はマスタで)
  5. 複数のデータベースのマイグレーションをフレキシブルに
  6. 複数のシャーディング方法でシンプルなシャーディング機能を利用できる (value, range, mapping table)

詳細は、 http://dbcharmer.net を見てください。

導入

DbCharmer の導入には2つのアプローチがあります:

  • gem をつかう (オススメ。Rails 3.2以降ではこの方法でしか使えません)
  • Rails plugin として入れる (Rails 2.x でのみ使えます)

gem として導入するなら、Gemfile に以下を追加してください:

gem 'db-charmer', :require => 'db_charmer'

Railsプラグインとして導入するなら以下のコマンドで:

./script/plugin install git://github.com/kovyrin/db-charmer.git

注意: DbCharmer を Rails じゃないプロジェクトで使うなら、コネクションを管理するメソッドを使う前に、DbCharmer.env に適切な値を設定する必要があります。適切な値は、database.yml のトップレベルのセクション名です。

簡単な ActiveRecord のコネクションの管理

このプラグインで追加される機能として switch_connection_to というメソッドがありますが、これはいろんな種類の DB コネクションの指定を受け付け、モデルでその指定を使います。以下をサポートしています:

  1. 文字列とシンボル: database.yml にある接続設定のブロックを指定
  2. ActiveRecord のモデル (モデルに対してコネクションを設定する)
  3. デーテベースのコネクション (Model.connection)
  4. nil: デフォルトのコネクションにリセットする

サンプルコード:

class Foo < ActiveRecord::Model; end

Foo.switch_connection_to(:blah)
Foo.switch_connection_to('foo')
Foo.switch_connection_to(Bar)
Foo.switch_connection_to(Baz.connection)
Foo.switch_connection_to(nil)

サンプルの database.yml の設定:

production:
  blah:
    adapter: mysql
    username: blah
    host: blah.local
    database: blah

  foo:
    adapter: mysql
    username: foo
    host: foo.local
    database: foo

switch_connection_to メソッドは第二引数にオプション引数として should_exist = true な引数をとります。このオプションは文字列またはシンボルを引数にメソッドが呼ばれたとき、そのコネクションの設定が database.yml にあるかどうか見ます。この should_exist が true ならば、例外をあげるし、false ならばエラーは無視され、コネクションの変更は起きません。

これは、development モードやテストのときに、複数のデータベースをローカルに作りたくない、ひとつのデータベースに全部テーブルを作りたい、とかいうときにつかえます。

警告: 全てのコネクションのスイッチの呼び出しは、呼んだクラスだけでコネクションをスイッチします。switch_connection_to の呼び出しと、コネクションのスイッチを継承元のクラスでやってはいけません (たとえば ActiveRecord::Base クラスのコネクションをスイッチすると、全てのモデルが新しいコネクションになってしまいます。そういうことがしたいときには、普通に establish_connection を使いましょう)。

複数データベースのマイグレーション

複数のデータベースを使うアプリケーションでも、便利なスキーママイグレーションの仕組みが必要でしょう。
Rails ユーザのみなさんは既に Rails の migration という仕組みを持っています。DbCharmer でも、Rails の migration で可能なかぎりシームレスに複数データベースを扱えるようにしています。

複数のデータベースを操作するのに、以下の2つの方法が使えます:

  1. マイグレーションファイル全体でコネクションを変更する方法: マイグレーション全体を指定のデータベースにスイッチする
  2. ブロックでコネクションを変更する方法: マイグレーションの一部を指定のデータベースにスイッチする

マイグレーションファイルの例(全体でコネクションを変更する場合):

class MultiDbTest < ActiveRecord::Migration
  db_magic :connection => :second_db

  def self.up
    create_table :test_table, :force => true do |t|
      t.string :test_string
      t.timestamps
    end
  end

  def self.down
    drop_table :test_table
  end
end

マイグレーションファイルの例(ブロックでコネクションを変更する場合):

class MultiDbTest < ActiveRecord::Migration
  def self.up
    on_db :second_db do
      create_table :test_table, :force => true do |t|
        t.string :test_string
        t.timestamps
      end
    end
  end

  def self.down
    on_db :second_db { drop_table :test_table }
  end
end


マイグレーションファイルの例(全体でコネクションを変更し、同じテーブルの操作を複数コネクションに):
(注: :connection と :connections は、コネクションの配列を指定することができる)

class MultiDbTest < ActiveRecord::Migration
  db_magic :connections => [:second_db, :default]

  def self.up
    create_table :test_table, :force => true do |t|
      t.string :test_string
      t.timestamps
    end
  end

  def self.down
    drop_table :test_table
  end
end
デフォルトのマイグレーションのコネクションについて

DbCharmer 1.6.10 以降では、ActiveRecord::Migration.db_magic の呼び出しと、マイグレーションでのデフォルトのコネクションの設定(特定のコネクションの変更しない限りこれが使われる)ができるようになりました。マイグレーションでデフォルトの ActiveRecord のコネクションが使いたい場合は db_magic :connection => :default を使ってください。

不正なコネクション名の扱いについて

どの環境でもデフォルトでは、on_dbdb_magic の宣言は指定したコネクションが database.yml に存在しない場合失敗します。production 環境以外でひとつのデータベースを使うなどしているときに、こうなるのを無視させることができます(テストコード用のデータベースとかでとくに便利です)。

この振る舞いは、Rails の initializers で DbCharmer.connections_should_exist の設定をすることでで制御できます。

警告: もしテスト環境でデータベースのコネクションを分けてマスタ・スレーブのサポートをしたいなら、transactional fixtures のサポートをオフにする必要があります。そうしないと、データを使ったテストでいろいろ問題に遭遇することになるでしょう。

モデルをマスタ・スレーブ環境で使う

マスタ・スレーブレプリケーションは、今日の中規模から大規模なデータベースアプリケーションでの、もっともポピュラーなスケールアウト手法です。いくつかの Rails プラグインが、モデルでスレーブサーバを使えるようにしていますが、それらは大きなアプリケーションで使った感じではあまりフレキシブルではありません。

ActsAsReadonlyable プラグインをしばらく使ってきましたが、たくさんの変更を加えながら使っていました。しかし、このプラグインが作者に放置されたので、我々はマスタ・スレーブのためのコードをプラグインにまとめリリースすることに決めました(Rails 2.2以降向け)。DbCharmer は、以下の機能を Rails のモデルに追加します:

全ての参照をスレーブ(たち)に自動スイッチ

モデルを作ったら、db_magic :slave => :blahdb_magic :slaves => [ :foo, :bar ] のコマンドをモデルで使うことができます。そうすることで、find, count, exist などの参照系の操作をスレーブ(または複数のスレーブなら、それらをラウンドロビン)に切り替えるモードになります。例は以下のとおり:

class Foo < ActiveRecord::Base
  db_magic :slave => :slave01
end

class Bar < ActiveRecord::Base
  db_magic :slaves => [ :slave01, :slave02 ]
end
デフォルトコネクション切り替え

もし、一組のマスタ・スレーブ(あるいは単純にひとつより多いデータベース)がある環境ならば、いくつかのモデルでデフォルトのコネクションを変更したいかもしれません。db_magic :connection => :foo をモデルに書くことでそうすることができます。例:

class Foo < ActiveRecord::Base
  db_magic :connection => :foo
end

マスタ・スレーブ構成 (つまりメインのコネクション + スレーブのコネクション) で分割されているモデルの例:

class Bar < ActiveRecord::Base
  db_magic :connection => :bar, :slave => :bar_slave
end
クエリごとのコネクション管理

マスタで select のクエリを走らせたいというケースがあると思います。たとえば、ちょうどデータを追加して、それを読み出す必要があって、でもそれがスレーブに反映されたかどうかわからない、というようなときです。こういうようなときのために、設定する方法を ActiveRecord モデルに追加しています:

1) on_master - これは block スタイルと proxy スタイルの2種類の書き方があります。
block スタイルでは、コードブロックのコネクションを強制的にスイッチさせることができます:

User.on_master do
  user = User.find_by_login('foo')
  user.update_attributes!(:activated => true)
end

proxy スタイルは、あるクエリをマスタに向けることができます:

Comment.on_master.last(:limit => 5)
User.on_master.find_by_activation_code(code)
User.on_master.exists?(:login => login, :password => password)

2) on_slave - これはマスタを強制的に使うようにしたあとなどに、強制的にスレーブに使うようにさせるメソッドです。スレーブが複数あれば、ランダムでひとつが選ばれます。このメソッドも block と proxy のスタイルがあります。

3) on_db(connection) - これは前出の2つのメソッドを可能にするものです。これはモデルのコネクションを、コードのブロックや1つの文で、ある DB に切り替えるのに使います(2つの形式があります)。これは switch_connection_to と同じ類の値を受け付けます。例:

Comment.on_db(:olap).count
Post.on_db(:foo).find(:first)

development と test 環境のデフォルトでは、存在しないコネクションを on_db の呼び出しを使うと、クエリはひとつのデータベースに全てのクエリを送ります。production の on_db では、存在しない名前は受け付けません。

この振る舞いは、 DbCharmer.connections_should_existRails の初期化で設定することによって制御できます。

強制的なスレーブ参照

いくつかの場面で、デフォルトで、全ての参照がスレーブにむかうモードが使われるには重大すぎるモデルがありますが、それらをスレーブにむかわせるモードに切り替えたいと思うことがたまにあります。
たとえば、User モデルがあるとします。ユーザは古いアカウント情報を見たくないので、スレーブ参照での遅延は避けたいです。しかしあるケースでは(たとえば、ログアウト状態でのプロフィールページの表示、などなど)、全ての参照をスレーブに向けるよう切り替えるというのは合理的です。

このユースケースのために、DbCharmer の 1.7.0 から、強制的にスレーブを参照する機能が追加されました。これは、いくつかの小さな別々の機能から成りますが、組み合わせによりパワフルなものになっています:

1) ActiveRecorddb_magic メソッドの :force_slave_reads => false オプション。
このオプションはモデルで自動でスレーブ参照するのを無効にします。なので、on_slave あるいは、その他のスレーブ参照できるようにするメソッドを必要に応じて呼ぶ必要があります。例:

class User < ActiveRecord::Base
  db_magic :slave => slave01, :force_slave_reads => false
end

2) ActionControllerforce_slave_reads クラスメソッド。
このメソッドは、コントローラごと(引数なしで呼ばれたとき)、あるいは、アクションごと (:only:except パラメータを渡して呼んだとき) 、強制的にスレーブを参照します。
これは、スレーブ参照によるラグがいくらか許容できるアクションで、スレーブが設定されているモデルの参照先をスレーブに向わせる、というようなときに使えます。例:

class ProfilesController < Application
  force_slave_reads :except => [ :login, :logout ]
  ...
end

3) ActionControllerforce_slave_reads! というインスタンスメソッド、このメソッドで、アクション内、あるいは、コントローラのフィルタで、一時的に参照先を強制的にスレーブに向かわせられます。
このメソッドは同じアクションをログインしているユーザとログインしていないユーザに呼ばれるときに使えます。before_filter でユーザ認証して、認証していないユーザ向けには force_slave_reads! メソッドを呼びます。

class ProfilesController < Application
  before_filter do
    force_slave_reads! unless current_user
  end
  ...
end

注意: このメソッドを使う前に、DbCharmer の ActionController サポートを有効にする必要があります。
DbCharmer.enable_controller_magic! を、プロジェクトの初期化コードで呼ぶ必要があります。

4) DbCharmer.force_slave_reads メソッドは、ブロック内のコードで強制的にスレーブ参照をするのに使われます。これは、とても細かい粒度でのスレーブ参照を強制する機能です。例:

DbCharmer.force_slave_reads do
  ...
  total_users = User.count
  ...
end

注意: 今のところ、この機能はベータで、使う場合は注意が必要です。テストしているものの、現実世界のアプリケーションでは想定外の問題が起こる可能性があります。

関連のコネクションの管理

ActiveRecord モデルはそれぞれ互いに関連を持っていて、それぞれがデータベースのコネクションを持っているため、User.posts.count のようなメソッドチェーンなんかで、コネクションの管理をするのが少し難しいです。 posts の count を別のデータベースでやりたいならば、あるひとつのクラスだけコネクションを変更して、以下の様にメソッドを呼びます:

Post.on_db(:olap) { User.posts.count }

これはあまり良い書き方ではないようなので、関連をうまく扱えるように on_* メソッド群を準備しました。こんなかんじで使えます:

@user.posts.on_db(:olap).count
@user.posts.on_slave.find(:title => 'Hello, world!')

注意: ActiveRecord の関連は、結果のオブジェクトあるいはコレクションのプロキシとして実装されているため、チェインしたメソッド以外もコネクションの切り替えを使えます:

@post.user.on_slave   # post の author を返す
@photo.owner.on_slave # photo の owner を返す

DbCharmer の 1.4 以降では、has_many と HABTM 関連のコネクション切り替えで prefix 記法を使えます:

@user.on_db(:foo).posts
@user.on_slave.posts
named scope のサポート

DbCharmer ユーザの named scope でのコネクションの切り替えを簡単にするため、スコープでも on_* メソッドをサポートするようにしました。
以下のスコープチェーンは全部やってることは同じです(クエリは :foo データベースコネクションで実行されます):

Post.on_db(:foo).published.with_comments.spam_marked.count
Post.published.on_db(:foo).with_comments.spam_marked.count
Post.published.with_comments.on_db(:foo).spam_marked.count
Post.published.with_comments.spam_marked.on_db(:foo).count

関連のサポートもしているので、以下のようにも使えます:

@user.on_db(:archive).posts.published.all
@user.posts.on_db(:olap).published.count
@user.posts.published.on_db(:foo).first
一括したコネクションの管理

たくさんのテーブルを使ってコードを書きたい、そして、それらを別のデータベースを使うようにしたいというときがあります。
こんなふうにできます:

DbCharmer.with_remapped_databases(:logs => :big_logs_slave) { ... }

デフォルトが :logs のどのモデル (たとえば db_charmer :connection => :logs などと書いているモデル)も、このブロック内ではコネクションを :big_logs_slave に変えます。
これは、他のどの DbCharmer のメソッドより優先度が低いです。なので、Model.on_db(:foo).find(...) などは、指定されたデータベースを使いつづけ、そうでないものはリマップされます。

一度に、いくつでもリマップの指定をできます。また、DbCharmer でコネクションを指定していないどのモデルにマッチするデータベース名として :master を使えます。

ノート: DbCharmer は モデルで alias_method_chain を使って動いています。注意して、必要な箇所にだけパッチしています。
しかし、with_remapped_databases とデフォルトのデータベース(:master) のリマップを使いたいなら ActiveRecord::Base の全てのサブクラスにパッチするしかありません。
これはひどい問題も、大きなパフォーマンスの影響もおこさないはずです(よくできてる)。

シンプルなシャーディングのサポート

DbCharmer の 1.6.0 から、ActiveRecord の拡張としてシンプルなデータベースシャーディングをサポートしています。
この機能は本番環境でもテストされているとはいえ、この機能の基本をちゃんと理解せずにアプリケーションで使うのはオススメしません。

現時点では4つのシャーディング方法をサポートしています:

1) "range" - とてもシンプルなシャーディング方法で、事前に定義したプライマリキーの範囲でテーブル分割を行い、その小さく分割したテーブルを別のデータベースやデータベースサーバに配置することができます。
こんなとき便利: 非常に大きいテーブルがだんだん大きくなっていくようなとき。テーブルをいくつかのサーバにのせることで複雑なシャーディングの仕組みなしでシンプルさを保ちたいとき。

2) "hash_map" - けっこうシンプルなシャーディング方法で、事前に定義しておいたいくつかのキーでテーブル分割を行います(プライマリキーでなくてよい)。
たとえば、どの州をどのデータベース(あるいはデータベースサーバ)に保存するか定義しておいてアメリカの住所のリストを州ごとにシャーディングするとかできます。

3) "db_block_map" - これはとても複雑なシャーディング方法で、テーブルを小さなブロックのセットに分けて、さらに、シャーディングしたデータベースなりデータベースサーバなりにそれを割り当てます。
ブロックを追加したいときはいつでも、ブロックは自動かつシャーディング間でバランスをとってデータベースに割り当てられます。
この方法は、1000万から10億レコードの巨大なテーブルをスケールするのに使え、比較的簡単にシャーディングしなおせる一番よい方法です。

4) "db_block_group_map" - これは "db_block_map" とよく似た方法で、一点だけが異なります。それは、この方法は
データベースのセット(テーブルのグループ)を各サーバに持てて、各グループがシャーディングとして扱われるところです。
このアプローチはアプリケーションをスケールするまえに事前にシャーディングしておくのに使えます。
ひとつのサーバから簡単にはじめられます。10, 20, 50 に分割したデータベースを持っておき、ひとつのマシンで対応できなくなったら、それらのデータベースを別のサーバに移します。

どうやってシャーディングしたらよいか?

シャーディングの拡張を有効にするには、すこし準備が必要です:

1) シャーディングのコネクションを定義した Rails のイニシャライザを作る (スクリプトやアプリケーションの初期化のときのコード)。それぞれのコネクションに名前と、シャーディング方法と、初期化のためのそのオプションを指定します。

2) シャーディングを使いたいモデルにシャーディングのコネクションを明示する

3) シャーディングしたいモデルを操作する前にシャーディングを明示する

より詳細は、以下のドキュメントをご覧ください。

シャーディング(分割)コネクション

シャーディングコネクションはシンプルな抽象化で、クラスタのためのシャーディングパラメータを一箇所で指定し、このひとところで設定したものをモデルで使えるようにすることができます。
いくつかのシャーディングのコネクションの初期化の例は以下のとおりです:

1) range ベースのシャーディングコネクションの例:

TEXTS_SHARDING_RANGES = {
  0...100   => :shard1,
  100..200  => :shard2,
  :default  => :shard3
}

DbCharmer::Sharding.register_connection(
  :name => :texts,
  :method => :range,
  :ranges => TEXTS_SHARDING_RANGES
)

2) ハッシュマップのシャーディングコネクションの例:

SHARDING_MAP = {
  'US'  => :us_users,
  'CA'  => :ca_users,
  :default  => :other_users
}

DbCharmer::Sharding.register_connection(
  :name => :users,
  :method => :hash_map,
  :map => SHARDING_MAP
)

3) データベースブロックマップのシャーディングコネクションの例:

DbCharmer::Sharding.register_connection(
  :name => :social,
  :method => :db_block_map,
  :block_size => 10000,                   # ブロックごとのキーの数
  :map_table => :event_shards_map,        # シャーディングブロックをマップするテーブル
  :shards_table => :event_shards_info,    # シャーディングコネクション情報のテーブル
  :connection => :social_shard_info       # マップでどのコネクションを使うか
)

シャーディングコネクションの定義をしたら、モデルでそれを使えるようになります:

class Text < ActiveRecord::Base
  db_magic :sharded => {
    :key => :id,
    :sharded_connection => :texts
  }
end

class Event < ActiveRecord::Base
  set_table_name :timeline_events

  db_magic :sharded => {
    :key => :to_uid,
    :sharded_connection => :social
  }
end
シャーディングしたモデルでのコネクションの切り替えについて

シャーディングしたモデルを操作するとき、必要があればいつでも、どの分割先を使うか指定することができます。
シャーディングしていない環境での DbCharmer のつかいかたと似た方法で、クエリごとに接続を管理できるメソッドを用意しています:

Event.shard_for(10).find(:conditions => { :to_uid => 123 }, :limit => 5)
Text.shard_for(123).find_by_id(123)

その他に、range と hash_map のシャーディングの方法で、デフォルトの分割先に切り替えることができるメソッドがあります:

Text.on_default_shard.create(:body => 'hello', :user_id => 123)

そして最後に、システム内の各シャーディングで、コードを実行するメソッドがあります(現在のところ、db_block_map と db_block_group_map でのみサポートされています):

Event.on_each_shard { |event| event.delete_all }
自分のシャーディング方法の定義

DbCharmer は、ユーザ定義のシャーディング方法が可能です。自分のシャーディング案を実装するには、いくつかやることがあります:

1) DbCharmer::Sharding::Method::YourOwnName という名前をつけてクラスをつくる

2) 最低でも、コンストラクinitialize(config) と、判別するためのインスタンスメソッド shard_for_key(key)、これは database.yml ファイルか、rails connection アダプタのコネクションパラメータのハッシュからコネクション名を返すようなメソッドになるでしょう。

3) 以下のようにして、あなたのシャーディングコネクションを登録します:

DbCharmer::Sharding.register_connection(
  :name => :some_name,
  :method => :your_own_name,    # あなたの定義したシャーディング方法を小文字にしたシンボルで渡します
  ... ほかにパラメータが必要ならそれも ...
)

4) 他の標準のシャーディングコネクションと同様に、あなたの定義したシャーディングコネクションを使います

自分で定義したシャーディングで、デフォルトの分割先をサポートする方法

もし、on_default_shard メソッドを自分の定義したシャーディングモデルで使いたいなら、2つやることがあります:

1) support_default_shard? というインスタンスメソッドをシャーディングクラスに実装します。これは、デフォルト分割先の指定をサポートするなら true を、そうでないなら false を返します。

2) shard_for_key メソッドで、:default のシンボルをキーとしてサポートするよう実装します。

自分で定義したシャーディング方法で、シャーディング列挙をサポートするには

シャーディング列挙を自分で定義したシャーディングのモデルでサポートするには、shard_connections というインスタンスメソッドを追加する必要があります。このメソッドは、シャーディングコネクション名か、コネクションの接続で使われる設定の配列を返す必要があります。

ドキュメントと質問について

このライブラリについてのよりくわしい情報については、われわれのサイト http://dbcharmer.net を参照してください。DbCharmer の内部の詳細について知りたいときは、ソースコードをチェックアウトしてください。プラグインのコードは、だいたい100%のカバレッジがあります。test-project ディレクトリに全てのユニットテストと、実際に使うときのテストが入っています。

このプロジェクトについて何か質問があるときは、作者が使っている DbCharmer ユーザーズグループのメーリングリストにコンタクトすることができます:

動作する RubyRails について

この gem ライブラリは、いくつかの Ruby のバージョンと、Rails 2.3, 3.0, 3.1, 3.2 で CI しています。

CI は TravisCI.org (https://travis-ci.org/kovyrin/db-charmer) でまわしています。
ビルドステータスは https://travis-ci.org/kovyrin/db-charmer を見てください(原文では現在のステータスについてのバッジみたいな画像が表示されています)

今、以下の組み合わせでビルドしています:

  • Rails のバージョン:
    • 2.x
    • 3.0
    • 3.1
    • 3.2
  • Ruby のバージョン:
    • 1.8.7
    • Ruby Enterprise Edition (1.8.7)
    • 1.9.3 (Rails 3 のみ。それ以前はサポートしていないため)
  • データベース:

くわえて、この gem は Scribd.com の本番でも使われています (世界で最も大規模な Rails で作られたサイトのひとつです)。 REE + Rails 2.2, 2.3 あるいは Sinatra と Rack アプリケーションで使われています。

1.8.0 から、3.2.8 に対応しましたが、3.2.4 はオフィシャルにサポートしてないので注意してください。たぶん動くと思いますが、対応していないバージョンについてバグレポートはうけつけません。

作者について

このプラグインScribd.com 社内で使うために作られ、さらにそれ以外のみなさんに使ってもらうために公開されました。コードのほとんどは Oleksiy Kovyrin によって書かれ、MIT ライセンスで公開されています。詳細については LICENSE ファイルを参照してください。

その他のコントリビュータは以下のとおりです(アルファベット順):

  • Allen Madsen
  • Andrew Geweke
  • Ashley Martens
  • Cauê Guerra
  • David Dai
  • Dmytro Shteflyuk
  • Eric Lindvall
  • Eugene Pimenov
  • Jonathan Viney
  • Gregory Man
  • Michael Birk
  • Tyler McMullen




付記

Rails のパンくずリストのオススメを教えてください

背景

パンくずリストというのを用意してみたところ、センスがなく、これは自前でやるものでなくてライブラリでやったほうがいいと思いました。

要件

つかいやすく、見た目があまりダサくないく、かつ、つくりがあまりダサくない、というものがよいです。

自分が見付けられたもの

ruby-toolbox.com でさらっと見てみたところ、以下の5つがよさそうかな、と思いました。

  • Breadcrumbs On Rails
  • Crummy
  • Breadcrumble
  • Loaf
  • Gretel
Breadcrumbs On Rails

定番のようで、一番人気っぽい。コントローラでリンク名とパスを配列に詰めてゆき、ビューでそれを表示するタイプ

  • ActionController::Base#add_breadcrumb にリンク名とパスを渡しリストを作る。同名のクラスメソッドがあり、before_filter を使いマクロ的に定義する機能を提供する
  • 表示のためのヘルパを定義しており、パンくずリストのセパレータ(» がデフォルト)や、span やら li などのタグ指定ができたりする
  • 幾種類かのリンク名やパスの指定が可能であり、シンボルや Proc オブジェクトをわたせばコントローラのインスタンスのコンテキストで評価される
Crummy

Breadcrumbs On Rails とアプローチは同じで作りもだいたい同じ。マクロ的に before_filter を宣言するクラスメソッドのつくりがちょっと違う。

  • ActionController::Base#add_crumb にリンク名とパスを渡しリストをつくる。ActionController::Base.add_crumb がインスタンスのコンテキストを手にいれるのがすこしへたくそな印象
  • 表示のためのヘルパは、表示用のクラス(Crummy::StandardRenderer というレンダラ)に処理させる方式で、will_paginate っぽい
Breadcrumble

シンプルな Breadcrumbs On Rails。日本の方が作ってる様子。

  • ActionController::Base#add_breadcrumb で(以下略)。マクロ的クラスメソッドがシンプルすぎるかもしれない。collection な URL の named_path の指定はできたい気もする
  • 表示のためのヘルパは、表示用のパーシャルを使う方式で、kaminari っぽい。今はできないが、kaminari みたいに theme オプションを受け付けたりできそう
Loaf

リンクテキスト部分が I18n に対応した Breadcrumbs On Rails という感じ(それ以上の印象がない)。

Gretel

config/initializers 以下に Ruby のファイルを置く設定ファイル方式。

  • config/initializers/breadcrumbs.rb に、パンくずリストのテキストとリンクを名前をつけて設定していく。設定は parent の設定を指定することが可能で、これで階層を表現する。
  • 表示のためのヘルパに、設定ファイルでつけた名前で設定を呼ぶ(<% breadcrumb :issue, @issue %> のように。設定では表示するリンクのリソースを指定することが可能)
  • 標準ではヘルパが content_tag などのメソッドで HTML を生成するが、リストにアクセスするメソッドがあるので、オプション指定でどうにもならないときは ERB などで表示の変更が可能

結論

Gretel が一番よいかなという気がする。パンくずリストの表示という、あまりメインの処理と関係ない(関係ないことはないが)処理のことを考えなくてすむのはありがたい。

なんとなく予想してたけど、Gretel ではわりと view で表示を制御する形になるので(どうあるべきか、は判断しかねる)同じ view をつかいまわすような画面が多いとあつかいづらいかもしれない(helper でラップすればよさそうではある)。また、コントローラでパンくずリストについてあまり意識しなくて済むとはいえ、全く意識しなくていいわけではない。とはいえコントローラの before_filter でなんかいろいろつめてくのもどうなんだろうといつも思うので Gretel でしばらく生きていきます。

Gretel で不都合があれば Breadcrumbs On Rails か Breadcrumble を使う、としたい。
何かオススメがあったら誰かおしえてください。

Evernote をコンソールから扱いたい

背景

Evernote がすごい便利な気がしてきたので活用したいと思いました。

要件

  • コンソールから Evernote にノートを作ったり、既にあるノートを編集したりしたい
  • markdown で書きたい
  • Vim で書きたい

結論

Ruby で書かれている evernote-editor というのが良さそうだった。
良さそうと思ったけど Evernote からとってきてエディタで読み込んだときにデータ構造がちゃんと markdown にできてない気がする…(あとで確認する)。 → 最初から markdown で作ったノートなら大丈夫そうだが、そうでないノートだとデータがこわれる(保存しなくても markdown にパースしてそれを保存しようとするため)。

evernote-editor の使いかた

Rubygems にあるので、gem つかえる環境ならば以下で導入できる。

gem install evernote-editor

すると、evned というコマンドが追加される。
title という名前のノートを作りたいときは以下のようにする。

evned title

初回起動時は、developer token と編集に使いたいエディタを尋ねられる。

編集するときは -e オプションをつける。ノートの候補が複数ある場合は、いくつか候補があるため選択せよといったプロンプトがでるので、自分が編集したいものを選ぶ。

evned -e title

タグをつけるとか Evernoteサンドボックス環境で実行するとかのオプションがある(README 見たらすぐわかる)。

その他の選択肢

他に探した感じでは、 Evernote をコンソールから(Vim から)扱う方法は以下の2つの選択肢があった。

前者の evervim はエディタ(Vim)に Evernote にポストする機能を付加するもので、+pythonvimコンパイルする必要がある。自分が Emacs を使っているならばこのようなアプローチでもかまわないと思う可能性があるが、自分としては、エディタにはテキスト編集以外の機能を盛り込みたくないので今回はパスした。タイトルとかタグとか全部含めてエディタの中で書けるのは良いなと思った。

後者の geeknote は、python で書かれた Evernote クライアントで、テキスト編集部分を任意のエディタで編集することができる。いままではこれを使っていたが、新規ノートを作るときにエディタが使えず微妙に不便だった。ドキュメントをちゃんと読んでいなくて見つけられなかったのかもしれない。ぱっとみの印象では、インタラクティブにコマンドを実行するというより、バッチスタイルで使うのに便利なインタフェースに見えた。

ちなみに Windows 環境では以下が使えるようだった(未確認)。

find_by!

欲しい。
以下のようなイメージ。Rails 4 の all とか take の雰囲気わかってないのでイメージ。

module MyFinderMethods
  def find_by(*args)
    where(*args).first
  end

  def find_by!(*args)
    where(*args).first!
  end
end

class ActiveRecord::Relation
  include MyFinderMethods
end

class ActiveRecord::Base
  class << self
    delegate :find_by, :find_by!, to: :scoped
  end
end

find_by どうでもいいけど find_by! 欲しい。

参考: http://d.hatena.ne.jp/suginoy/20120605/p3