先日書いた記事の中で触れていたDynamoDBのための簡易的なORMについて、随分前から公開はしているものの、せっかくなので記事を書いて紹介しようと思い立った。
位置付けとしては件のメディアストレージ基盤を開発する過程でうまれた副産物的なもの。
名前はDynamoをイタリア語にしただけで、読み方は「ディナモ」。
使い方
Ruby界隈でよく見られるORMの振る舞いを踏襲している、というだけでは些か抽象的すぎるので、具体例をいくつか示したい。
モデルを定義する
class User < Dinamo::Model
hash_key :name, type: :string
field :ruby, type: :string
end
オブジェクトを生成する/保存する
user = User.new(name: 'numb', ruby: 'なんぶ')
user.persisted? #=> false
user.save
user.persisted? #=> true
create
を使っても良い。
user = User.create(name: 'numb', ruby: 'なんぶ')
user.persisted? #=> true
オブジェクトを変更する
user = User.new(name: 'numb', ruby: 'なんぶ')
user.changed? #=> false
user.name = 'なんぶ'
user.changed? #=> true
user.save
user.changed? #=> false
オブジェクトを取得する
user = User.create(name: 'numb', ruby: 'なんぶ')
User.get(name: 'numb') #=> #<User:...>
オブジェクトを削除する
user = User.create(name: 'numb', ruby: 'なんぶ') #=> <User:...>
user.destroyed? #=> false
user.destroy
user.destroyed? #=> true
バリデーション
デフォルトではvalidates_presence_of
のみが動作する。
基本的にバリデーターは自分で実装し、validates_with
を用いて追加する。
class NameLengthValidator < Dinamo::Model::Validation::Validator
def validate(record)
unless options[:length].include?(record.name.length)
record.errors.add(:name, I18n.t("models.user.error.length"))
end
end
end
class User < Dinamo::Model
hash_key :id
field :name, type: :string
validates_with NameLengthValidator, length: 1..30
end
コード中にあるDinamo::Model::Validation::Validator
など(EachValidatorもある)を継承することで容易にバリデータを実装できる。
例外機構
save
・create
・update
にはそれぞれ!
を末尾に加えたメソッドも用意されており、該当するレコードが存在しなかったり、バリデーション違反とされた場合に例外が発生するようになる。
なぜ作ったか
件のメディアストレージ基盤を開発する段階では、DynamoDB向けのORMは観測範囲においてはdynamoid
くらいしか見つからなかった。
ところがdynamoid
はaws-sdk v2には対応しておらず、選択肢から除外せざるを得なかったという経緯がある。
観測範囲内に無いものは作るほかないので、ある程度自分からして扱いやすいインターフェースを備えたORMを書いた結果がdinamoだ。
所感
DynamoDBは使い勝手がいいが、aws-sdk単体で叩き続けるのはさすがに骨が折れる作業だ。
今回かいたdinamoについては、シンプルにDynamoDBを使う分には便利なgemになったように思う。
アソシエーションやマイグレーションといった機構については実装していない。
そもそも必要性について懐疑的であるし、加えて自分にとっては現状必要なものではなかったのが大きいように思う。
追記
という記事をあたためていたら、dynamoidがaws-sdk v2に対応したようだ。
リッチに、かつ、より楽にDynamoDBを使いたいRubyエンジニアはdynamoidを使うといいかもしれない。