2013年5月29日水曜日

redis-objectsがなくてもプロジェクトは回るが

このエントリーをはてなブックマークに追加

あるととても便利だと思います

はじめに

エンジニアの@ryooo321です。
よろしくお願いします。
ご存知の方も多いかもしれませんが、今回はRubyからRedisを使う際にとても扱いやすいredis-objectsをご紹介したいと思います。

https://github.com/nateware/redis-objects


特徴

・ORMではない

・Redisのデータ型(counter, value, list, hash, set, sorted-set)をサポート

・ロック機能も利用可能



好きなところ

・モデルの任意のプロパティをRedisに保存するような感覚で利用できる点。

・Redisのキー情報をシームレスに管理できる点。

・ARモデルの一部プロパティをRedisに移したりできるので、データストア単位でなく理想の単位でモデルを定義できる点。


This is not an ORM. People that are wrapping ORM’s around Redis are missing the point.
(readmeより引用)
作者の方はこのように言っており、本当にとても使いやすい形になっております。

目次

1. Counter
2. List
3. Set
4. Hash
5. Sorted Set
6. global
7. Lock
8. redisオブジェクト


1. Counter

数値をアトミックにincrement/decrementする型です。

class User < ActiveRecord::Base
  include Redis::Objects
  counter :friend_count, :start => 0
  # startは省略可
end

@user = User.find(id)

# インクリメント
@user.friend_count.increment

# 渡したブロックで例外発生もしくはnilが帰ったときは、decrementを発行して数値を戻す
@user.friend_count.increment do |new_value|
  raise 'friend count is limited by 20' if 20 < new_value
  true
end

# Userを取得せずにアクセス
friend_count = User.get_counter(:friend_count, user_id)
User.increment_counter(:friend_count, user_id)
Userを取得せずにアクセスできる点、オブジェクト経由で操作する時はuser_idを渡さないでよい点(キーがシームレス)が便利です。


2. List

配列をアトミックに操作できる型です。

class User < ActiveRecord::Base
  include Redis::Objects
  list :friend_ids, :maxlength => 20
  # startは省略可
end

@user = User.find(id)

# rubyの配列のように使えます(Enumerable)
# 追加
@user.friend_ids << 123
@user.friend_ids.push(123)

# 変更
@user.friend_ids[2] = 123

# 削除
@user.friend_ids.delete(123)

# 取得
@user.friend_ids[2]
@user.friend_ids[2..4]
@user.friend_ids.at(2)
@user.friend_ids.first
@user.friend_ids.each do |user_id|
  # hoge
end
@user.friend_ids.values

Userを取得せずに操作するメソッドは現在ありません。

しかし、⬇⬇⬇このようにしてUserオブジェクトなしにアクセスできます。

name = :friend_ids
friend_ids = Redis::List.new(User.redis_field_key(name, user_id), User.redis, User.redis_objects[name])
friend_ids.values


3. Set

重複無効な配列をアトミックに操作できる型です。

class User < ActiveRecord::Base
  include Redis::Objects
  set :friend_ids
end

@user = User.find(id)

# rubyの配列のように使えます(Enumerable)
@user.friend_ids << 123
@user.friend_ids.each do |user_id|
  # hoge
end

# setなので、重複したものは無視
@user.friend_ids << 123
@user.friend_ids << 123 # 2回目は無視
@user.friend_ids.member?(me.id)

# 共通の友達
@user.friend_ids & me.friend_ids

# どちらかの友達
@user.friend_ids | me.friend_ids
@user.friend_ids + me.friend_ids

# 共通でない友達(差分)
@user.friend_ids ^ me.friend_ids
@user.friend_ids - me.friend_ids

# Userを取得せずに操作するメソッドは現在ありませんが、List同様(前述)に取得できます


4. Hash

ハッシュをアトミックに操作できる型です。

class User < ActiveRecord::Base
  include Redis::Objects
  hash_key :item_count_map
end

@user = User.find(id)

# rubyのHashのように使えます(Enumerable)
@user.item_count_map[item_id] = 10
@user.item_count_map.each do |item_id, count|
  # hoge
end

# まとめて操作
@user.item_count_map.bulk_set(:a => 10, :b => 20, :c => 30)
@user.item_count_map.bulk_get(:a, :b)
# => {:a => 10, :b => 20}
@user.item_count_map.bulk_values(:a, :b)
# => [10, 20]

# 個別にインクリメント
@user.item_count_map.incr(:a, 50)
@user.item_count_map[:a]
# => 60

# Userを取得せずに操作するメソッドは現在ありませんが、List同様(前述)に取得できます


5. Sorted Set

ソート済みハッシュをアトミックに操作できる型です。

class User < ActiveRecord::Base
  include Redis::Objects
  sorted_set :article_rate
end

@user = User.find(id)

# articleごとに評価スコアを登録
@user.article_rate[:a] = 30
@user.article_rate[:b] = 50
@user.article_rate[:c] = 10

@user.article_rate.score(:c)
# => 10

# 順位
# 昇順
@user.article_rate.rank(:b)
# => 2

# 降順
@user.article_rate.revrank(:b)
# => 0

# スコアの範囲取得
@user.article_rate.rangebyscore(0, 100, :limit => 1)
# => [:c]
@user.article_rate.members(:with_scores => true)
# => [[:c, 10], [:a, 30], [:b, 50]]

# atomicなインクリメント
@user.article_rate.incr(:c, 100)
@user.article_rate.score(:c)
# => 110

# Userを取得せずに操作するメソッドは現在ありませんが、List同様(前述)に取得できます


6. global

すべてのデータ型クラスでglobalオプションが使えます。

trueを指定すると、クラス単位でredisキーが共通になります。

※ 同モデルのオブジェクトすべてが、同じキャッシュを見るような状態です。

class User < ActiveRecord::Base
  include Redis::Objects
  sorted_set :article_rate
  sorted_set :event_point, :global => true
end

# これは@userごとのランキング
@user.article_rate.rank(:a)

# これは全ユーザーのランキング
@user.event_point.rank(:a)


7. Lock

lockの実装としてはredisにフラグ値をsetし、そのフラグがある場合は処理を待ち合わせる作りです。

同じhoge_lockを使っている箇所とで排他制御にできます。

class User < ActiveRecord::Base
  include Redis::Objects
  lock :hoge, :expiration => 20.second, :timeout => 1.second
  
  # lockのオプション
  # timeout :
  #    指定された時間以上にロック解除を待った場合、例外を投げます。
  # expiration :
  #    万が一ロックが解除されない状態になっても、指定された時間が経つとredis側でロック解除します。
end

@user = User.find(id)

# 処理Aと処理Bは排他的に動く
@user.hoge_lock.lock do
  # 処理A
end
@user.hoge_lock.lock do
  # 処理B
end



8. redisオブジェクト

redisオブジェクトはredis gemのオブジェクトで、redis-objectsで未実装のAPIもredisオブジェクト経由で呼び出せる場合があります。

class User < ActiveRecord::Base
  include Redis::Objects
  value :hoge
end

@user = User.find(id)

# redisオブジェクトへは、下記のようにアクセスできます。
redis = User.redis
redis = @user.redis

redis.pipelined do
  redis.set "foo", "bar"
  redis.incr "baz"
end


おわりに

本稿にお付き合い下さいましてありがとうございました。

この素晴らしいプロダクトを、みなさまが少しでもよいと思っていただければ幸いです。



一緒に働きたい方、絶賛 募集中

京都でスキルアップしたいエンジニアの皆さん、ご応募お待ちしています!
社内にはプロジェクターが使えるバースペースがあり、業務後のコミュニケーションの場となっています。

京都でスキルアップしたい学生さん、アルバイトも可能なのでご応募お待ちしています!
オフィスワークでドリンク飲み放題、時給は高く、シフトの自由度も高いです。

大阪、滋賀、神戸から通勤実績あります
イラストレーターさん、シナリオライターさんも募集中(アルバイト可)です!


2013年5月11日土曜日

あんさんぶるガールズ!のイメージソングCD「ときめき☆アンサンブル」を頂きました!

このエントリーをはてなブックマークに追加
エンジニアの草苅です。

4月29日に開催されたM3で、ななひらさんによるあんさんぶるガールズ!のイメージソングCD「ときめき☆アンサンブル」が頒布されました!

弊社は京都の会社ですので、どうにかして手に入れられないか、当日東京に行く人はいないのかと話合っていたのですが、ななひらさんのご厚意によりプレゼントして頂けることになりました!

そして、プレゼントして頂いたCDがこちらです!


届いた日は、朝からあんガルチームの部屋で流して、チーム一同聞き入っていました!
ななひらさんのかわいい歌声や、曲の完成度の高さに加えて、あんガルのカード枠を再現したパッケージなどもとても凝っていて、チーム一同感動しっぱなしでした!

CDの1曲目に収録されている「ダニエルは女の子です!」は、現在ニコニコ動画でも公開されており、ニコニコインディーズ総合順位1位になっているようです。すごい!まだの方はぜひ下のリンクから聞いてみてください。

ななひら x U-ske - ダニエルは女の子です! ‐ ニコニコ動画(原宿)


あんガル運営チームは二次創作を応援します!

今後、あんガルの二次創作がさらに盛り上がっていくといいなと感じています。
現在、公式サイトにて二次創作ガイドラインを公開する準備をしており、より多くの方に二次創作で楽しんで頂けるような仕組み作りや、二次創作をやりやすくするための素材公開も検討中です。

続報は公式ツイッターアカウントでご報告しますので、よろしければぜひフォローお願いします。


さいごに少し宣伝…

あんさんぶるガールズ!を一緒に作りませんか?

Happy Elements 株式会社ではイラストレーター・デザイナーを募集中です。
中の人になって、京都で一緒にあんガルを作ってみませんか?社員・アルバイトどちらでも、ご応募お待ちしています!