[Ruby on Rails] dm-redis-adapterでRedisのデータをModelのように扱う

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Ruby on RailsにてRedisに登録するデータを扱う際に、通常のActiveRecordのModelのようにデータを操作できる dm-redis-adapter を紹介したいと思います。これを使うと、Redisに登録するデータを、以下のように扱うことができます。

  • 一つのModelを一レコードのように扱い、複数のプロパティを持たせて登録する
  • 登録するデータにidを持たせ、自動的にインクリメントした値を登録する
  • モデル名.create のように、ActiveRecordと同じようなメソッドを使用してデータを操作する


以下に、Gemの使用方法やソースの例を記述します。

Gemの使用準備

dm-redis-adapterをRuby on Railsで扱う方法についてです。データの登録先としてRedisを使うので、予めインストールしておいてください。(もしくはサーバなどに用意しておく)

1.Gemのインストール

Gemfileに以下を記述し、$ bundle install します。

gem 'redis-objects'
gem 'dm-core'
gem 'dm-redis-adapter'

2.接続情報とRedisオブジェクトの作成

/config/initializersフォルダ内に、Redisの接続情報とオブジェクトを作成するためのソースを作成します。今回は「redis.rb」というファイルを作成し、以下のように記述しました。

/config/initializers/redis.rb
Redis.current = Redis.new(:host => '127.0.0.1', :port => 6379)

上記例は、ローカルでRedisを実行する場合のものです。(確認はしてませんが)サーバなど別マシン上でRedisを実行する場合は、「:host」にそのマシンのIPを指定してください。

Modelの定義

Redisの登録するデータについて、Modelを定義します。今回はUserモデルを作成し

  • idを保持し、idにて検索できるようにする
  • 項目として「name」「email」を持つようにする


ようにしました。以下、Userモデルのソースです。

/app/models/user.rb
DataMapper.setup(:default, {:adapter  => "redis"})

class User
  include Redis::Objects
  include DataMapper::Resource
 
  property :id, Serial

  value :name
  value :email
end

User.finalize

一番上、一番下の行は「お約束」のようなものです(笑)。重要なのは3〜11行目(つまりUserクラス)で

  • UserクラスはActiveRecordを継承せず、Redis::Object、DataMapper::Resourceをincludeする
  • 7行目でidをプロパティとして定義し、AutoIncrementさせるため「Serial」で定義する
  • 保持したい項目(今回はname、email)を「value」として定義する


を行っています。

Redisへの登録・参照・更新・削除

上記のUserモデルを使用して、データを操作します。今回はRspecにて記述しました。RSpecを実行する前に、以下のコマンドでRedisを起動します。

$ radis-server
/spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, :type => :model do
  include Redis::Objects

  before do
    Redis.current.flushall
  end

  after do
    Redis.current.flushall
  end

  describe 'User Data' do
    before do
      user = User.create
      user.name = 'TestUser'
      user.email = 'test@ggg.com'
      @id1 = user.id

      user = User.create
      user.name = 'TogoJuzo'
      user.email = 'duke@ggg.com'
      @id2 = user.id
    end

    it 'should find.' do
      user = User.first(id: @id1)
      name = user.name.value
      email = user.email.value

      expect(name).to eq('TestUser')
      expect(email).to eq('test@ggg.com')

      user = User.first(id: @id2)
      name = user.name.value
      email = user.email.value

      expect(name).to eq('TogoJuzo')
      expect(email).to eq('duke@ggg.com')
    end

    it 'should update.' do
      user = User.first(id: @id2)
      user.email = 'm16@ggg.com'

      updated = User.first(id: @id2)
      email = updated.email.value
      expect(email).to eq('m16@ggg.com')
    end

    it 'should delete.' do
      user = User.first(id: @id1)
      user.destroy

      user = User.first(id: @id1)
      expect(user).to be_nil
    end
  end
end

データの登録

15〜25行目のbeforeの中で、Userモデルを登録しています。このbeforeを実行すると、Redisには以下のようにデータが登録されます。

$ redis-cli

127.0.0.1:6379> keys *
1) "user:1:email"
2) "user:2:name"
3) "users:id:all"
4) "user:1:name"
5) "user:2:email"
6) "users:id:serial"

Redisのキーとして「user:idの値:項目名」が登録され、それぞれが別レコードとして保持されていることが分かります。登録されている値を確認してみると、以下のようになります。

127.0.0.1:6379> get user:1:email
"test@ggg.com"
127.0.0.1:6379> get user:1:name
"TestUser"

この「user:idの値:項目名」をキーとして登録していることで、dm-redis-adapterがRedisに登録するデータをModelのように扱うことを実現しているようです。

データの参照

27〜41行目で、データの参照を行っています。firstメソッドの引数に、対象のidを指定することで、ActiveRecordのModelに近い形で参照しています。

データの更新

43〜50行目で、データの更新を行っています。注意すべき点は、項目の値を書き換えた後(45行目)、updateなどの更新のためのメソッドを呼び出していないことです。

データの削除

52〜58行目で、データの削除を行っています。こちらもdestroyメソッドを使用し、ActiveRecordのModelに近い形で実現しています。注意すべき点は、destoryしたデータはfirstでは取得出来ないが、Redis上では物理的には削除されていないことです(redis-cli上で確認すると、データが存在する)。Redis内のキー「users:id:all」「users:id:serial」を使用して、dm-redis-adapterが削除扱いとしていると思われるのですが、現時点では未確認です。

まとめ

dm-redis-adapterを使い、key-valueの組み合わせであるRedis上のデータをModelに近い形で扱うことができました。