ruby搭配redis使用
2013年9月11日 19:51
设计的客户端管理和任务分发系统使用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元编程。