clockwork使用

ruby搭配redis使用

jack posted @ 2013年9月11日 19:51 in ruby with tags ruby Redis nest , 1265 阅读

设计的客户端管理和任务分发系统使用redis存储,使用名字空间的方式进行管理

对每一个用户,分配一个id,设计键值

account:id:username
account:id:province
account:id:city
account:id:isp

...

一开始直接使用reids-rb进行操作,redis.get("account:#{id}:username")

后来发现一个操作redis的gem--Ohm,体验了ohm,内部也是这样的键值进行存储的,但是Ohm封装的很好,达到了类似activerecord的效果,不过由于系统需要和其他语言进行交互,如果用Ohm会不大方便。于是找到作者的另一个gem--nest,通过nest可以直接复用原来的设计,方便的操作。

account = Nest.new("account",redis)
account[id][:username].getNest

源码如下,非常简洁。

require "redis"

class Nest < String
  METHODS = [:append, :bitcount, :blpop, :brpop, :brpoplpush, :decr,
  :decrby, :del, :dump, :exists, :expire, :expireat, :get, :getbit,
  :getrange, :getset, :hdel, :hexists, :hget, :hgetall, :hincrby,
  :hincrbyfloat, :hkeys, :hlen, :hmget, :hmset, :hset, :hsetnx, :hvals,
  :incr, :incrby, :incrbyfloat, :lindex, :linsert, :llen, :lpop,
  :lpush, :lpushx, :lrange, :lrem, :lset, :ltrim, :move, :persist,
  :pexpire, :pexpireat, :psetex, :pttl, :publish, :rename, :renamenx,
  :restore, :rpop, :rpoplpush, :rpush, :rpushx, :sadd, :scard,
  :sdiff, :sdiffstore, :set, :setbit, :setex, :setnx, :setrange,
  :sinter, :sinterstore, :sismember, :smembers, :smove, :sort, :spop,
  :srandmember, :srem, :strlen, :subscribe, :sunion, :sunionstore,
  :ttl, :type, :unsubscribe, :watch, :zadd, :zcard, :zcount,
  :zincrby, :zinterstore, :zrange, :zrangebyscore, :zrank, :zrem,
  :zremrangebyrank, :zremrangebyscore, :zrevrange, :zrevrangebyscore,
  :zrevrank, :zscore, :zunionstore]

  attr :redis

  def initialize(key, redis = Redis.current)
    super(key.to_s)
    @redis = redis
  end

  def [](key)
    self.class.new("#{self}:#{key}", redis)
  end

  METHODS.each do |meth|
    define_method(meth) do |*args, &block|
      redis.send(meth, self, *args, &block)
    end
  end
end

Nest继承String类,使用dynamic methods动态定义方法对redis的操作进行封装,初看很简单,但是细细琢磨,有一些ruby中的细节需要熟悉。我们知道ruby每个方法的最后一行会成为返回值,那么initialize后,@redis是不是成为返回值了?答案是否,使用new实例化对象的时候,并不是简单的调用了initialize方法,new方法返回一个Nest对象。根据stackoverflow上的回答:

new is a class method, which generally creates an instance of the class (this deals with the tricky stuff like allocating memory that Ruby shields you from so you don't have to get too dirty).

Then, initialize, an instance method, tells the object to set its internal state up according to the parameters requested.

class Class
  def new(*args, &block)
    obj = allocate

    obj.initialize(*args, &block)
    # actually, this is obj.send(:initialize, …) because initialize is private

    obj
  end
end

Nest继承String,对象的值为字符串,作为redis的键值,Nest中的self就是键值,[]方法new新的Nest对象,形成键值的映射。通过send方法,将动态生成的Nest的实例方法派发到redis对象执行。

简单,优美的Ruby元编程。

blog comments powered by Disqus