Created
February 24, 2012 11:23
-
-
Save FND/1900281 to your computer and use it in GitHub Desktop.
simple Redis-based microblogging application
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
StatusQ client | |
""" | |
from __future__ import absolute_import, division, with_statement | |
import sys | |
from time import time | |
from redis import StrictRedis | |
from pretty import date as pretty_date | |
# TODO: read from config | |
HOST = "localhost" | |
PORT = 6379 | |
REDIS_DB = 0 | |
COMMANDS = {} | |
def main(args): | |
args = [unicode(arg, "utf-8") for arg in args] | |
cmd = args[1] | |
params = args[2:] | |
db = StrictRedis(host=HOST, port=PORT, db=REDIS_DB) | |
# TODO: use pipeline transactions | |
COMMANDS[cmd](db, *params) | |
return True | |
def command(func): | |
""" | |
decorator which adds the respective method to the COMMANDS dictionary | |
""" | |
COMMANDS[func.__name__] = func | |
return func | |
@command | |
def read(db, username, _type="overlords"): # TODO: rename `_type` argument | |
# TODO: authenticate user | |
if _type == "overlords": | |
key = "users:%s:%s" % (username, "stream") | |
elif _type == "all": | |
key = "posts:%s" % username | |
else: | |
raise TypeError, "invalid stream type" | |
posts = db.lrange(key, 0, 10) | |
if len(posts) == 0: | |
print "no posts here yet" | |
for pid in posts: | |
msg = db.get("posts:%s" % pid) | |
author = db.get("posts:%s:author" % pid) | |
timestamp = float(db.get("posts:%s:timestamp" % pid)) | |
print "[%s] (%s) %s" % (author, pretty_date(int(timestamp)), msg) | |
@command | |
def post(db, username, msg): | |
# TODO: authenticate user, validate message | |
pid = db.incr("posts:enum") | |
db.set("posts:%s" % pid, msg) | |
db.set("posts:%s:author" % pid, username) | |
db.set("posts:%s:timestamp" % pid, time()) | |
db.lpush("posts", pid) | |
db.lpush("posts:%s" % username, pid) | |
for minion in db.smembers("users:%s:minions" % username): | |
db.lpush("users:%s:stream" % minion, pid) | |
# TODO: mentions | |
#for user in extract_usernames($txt) | |
# LPUSH users:$user:pings $pid | |
@command | |
def watch(db, username, *overlords): | |
# TODO: authenticate user | |
for overlord in overlords: | |
db.sadd("users:%s:overlords" % username, overlord) | |
db.sadd("users:%s:minions" % overlord, username) | |
@command | |
def unwatch(db, username, *overlords): | |
# TODO: authenticate user | |
for overlord in overlords: | |
db.sadd("users:%s:overlords" % username, overlord) | |
db.sadd("users:%s:minions" % overlord, username) | |
@command | |
def create_user(db, username, password): | |
# TODO: validate input | |
uid = db.incr("users:enum") | |
db.set("users:%s:uid" % username, uid) # XXX: unnecessary? | |
db.set("users:%s" % username, password) # TODO: hash password | |
db.sadd("users", username) | |
if __name__ == "__main__": | |
status = not main(sys.argv) | |
sys.exit(status) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# INT = integer | |
# STR = string | |
# () = set | |
# [] = list | |
# == USERS == | |
# | |
# users = () # usernames -- XXX: use list? | |
# users | |
# enum = INT | |
# $username = STR # password -- XXX: more efficient to use $uid? | |
# uid = INT | |
# password = INT | |
# minions = () # usernames -- TODO: rename ("observers", "acolytes")? -- XXX: use list? | |
# overlords = () # usernames -- TODO: rename? -- XXX: use list? | |
# stream = [] # post IDs (reverse chronological order) | |
# pings = [] # post IDs (reverse chronological order) -- TODO: rename? | |
# | |
# == POSTS == | |
# | |
# posts = [] # post IDs (reverse chronological order) | |
# posts | |
# enum = INT | |
# $username = [] # post IDs (reverse chronological order) -- XXX: key collision hazard | |
# $pid = STR | |
# author = INT | |
# timestamp = INT # epoch | |
create_user: $username $password | |
$uid = INCR users:enum | |
SET users:$username:uid $uid # XXX: unnecessary? | |
SET users:$username hash($password) | |
SADD users $username | |
create_post: $username $msg | |
$pid = INCR posts:enum | |
SET posts:$pid $msg | |
SET posts:$pid:author $username | |
SET posts:$pid:timestamp now() | |
LPUSH posts $pid | |
$minions = SMEMBERS users:$username:minions | |
for $minion in $minions | |
LPUSH users:$minion:stream $pid | |
for $user in extract_usernames($msg) | |
LPUSH users:$user:pings $pid | |
watch: $subject $object | |
SADD users:$subject:overlords $object | |
SADD users:$object:minions $subject | |
# stream is not changed retroactively (for performance reasons - otherwise | |
# we'd have to traverse the entire stream, retrieving each post's timestamp | |
# to inject $object's posts - though immutable history might also be | |
# considered desirable) | |
unwatch: $subject $object | |
SREM users:$subject:overlords $object | |
SREM users:$object:minions $subject | |
# stream is not changed retroactively (for performance reasons - otherwise | |
# we'd have to traverse the entire stream, retrieving each post's author to | |
# discard $object's posts - though immutable history might also be | |
# considered desirable) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.PHONY: client server terminate reset clean | |
client: | |
./redis-cli | |
server: terminate | |
./redis-server > ./redis.log & echo $$! > ./.pid | |
terminate: | |
kill -TERM `cat .pid` || true | |
rm .pid || true | |
reset: terminate clean | |
sleep 1 | |
rm dump.rdb || true | |
clean: | |
rm redis.log || true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment