Skip to content

Instantly share code, notes, and snippets.

@antirez
Created April 17, 2011 18:09
Show Gist options
  • Save antirez/924301 to your computer and use it in GitHub Desktop.
Save antirez/924301 to your computer and use it in GitHub Desktop.
require 'rubygems'
require 'json'
require 'redis'
class RedisComments
def initialize(redis,namespace,sort_proc=nil)
@r = redis
@namespace = namespace
@sort_proc = sort_proc
end
def thread_key(thread_id)
"thread:#{@namespace}:#{thread_id}"
end
def insert(thread_id,comment)
raise "no parent_id field" if !comment.has_key?('parent_id')
key = thread_key(thread_id)
id = @r.hincrby(key,:nextid,1)
@r.hset(key,id,comment.to_json)
return id.to_i
end
def edit(thread_id,comment_id,comment)
key = thread_key(thread_id)
old = @r.hget(key,id)
return false if !old
comment['parent_id'] = JSON.parse(old)['parent_id']
@r.hset(key,id,comment.to_json)
return true
end
def remove_thread(thread_id)
@r.del(thread_key(thread_id))
end
def comments_in_thread(thread_id)
@r.hlen(thread_key(thread_id)).to_i-1
end
def del_comment(thread_id,comment_id)
# TODO? You may want to make sure there are no parents.
# If there are parents we can call edit() with "comment removed"
# or something like that.
#
# A probably wiser implementation is to *never* use this method
# and instead flag the comment as deleted. Then when rendering we
# can display it in a special way if there ara replies, otherwise
# we can avoid displaying deleted comments that are leafs.
@r.hdel(thread_key(thread_id),comment_id)
end
def render_comments(thread_id,&block)
byparent = {}
@r.hgetall(thread_key(thread_id)).each{|id,comment|
next if id == "nextid"
c = JSON.parse(comment)
c['id'] = id.to_i
parent_id = c['parent_id'].to_i
byparent[parent_id] = [] if !byparent.has_key?(parent_id)
byparent[parent_id] << c
}
render_comments_rec(byparent,-1,0,block)
end
def render_comments_rec(byparent,parent_id,level,block)
thislevel = byparent[parent_id]
thislevel = @sort_proc.call(thislevel,level) if @sort_proc
thislevel.each{|c|
c['level'] = level
block.call(c)
if byparent[c['id']]
render_comments_rec(byparent,c['id'],level+1,block)
end
}
end
end
# In this example we want comments at top level sorted in reversed chronological
# order, but all the sub trees sorted in plain chronological order.
comments = RedisComments.new(Redis.new,"mycomments",proc{|c,level|
if level == 0
c.sort {|a,b| b['ctime'] <=> a['ctime']}
else
c.sort {|a,b| a['ctime'] <=> b['ctime']}
end
})
comments.remove_thread(50)
first_id = comments.insert(50,
{'body' => 'First comment at top level','parent_id'=>-1,'ctime'=>1000}
)
second_id = comments.insert(50,
{'body' => 'Second comment at top level','parent_id'=>-1,'ctime'=>1001}
)
id = comments.insert(50,
{'body' => 'reply number one','parent_id'=>second_id,'ctime'=>1002}
)
id = comments.insert(50,
{'body' => 'reply to reply','parent_id'=>id,'ctime'=>1003}
)
id = comments.insert(50,
{'body' => 'reply number two','parent_id'=>second_id,'ctime'=>1002}
)
rendered_comments = comments.render_comments(50) {|c|
puts (" "*c['level']) + c['body']
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment