Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

mtweet

  • Loading branch information...
commit d8280162e529a20c0144a867480f1eb46c4f491f 1 parent 61f221c
Steve Yen authored
20 README.md
View
@@ -1,21 +1,20 @@
-Retwis-RB
+mtweet
=========
-
-An example Twitter application using the Redis key-value database.
-
-Daniel Lucraft (dan@fluentradical.com)
+An example Twitter application using the Membase NoSQL database.
Requirements
------------
* Ruby
* Sinatra: sudo gem install sinatra
- * Redis: http://code.google.com/p/redis/
+ * Memcache-client: sudo gem install memcache-client
+ * Membase: http://membase.org
Starting Application
--------------------
-Make sure the redis server is running.
+Make sure the membase server is running (at 127.0.0.1:11211).
+
Run:
ruby app.rb
@@ -24,3 +23,10 @@ License
-------
MIT
+
+Thanks
+------
+
+Daniel Lucraft (dan@fluentradical.com) for the original Retwis-rb
+at http://danlucraft.com/blog/tag/retwis-rb
+
11 app.rb
View
@@ -2,19 +2,14 @@
require 'rubygems'
require 'sinatra'
require 'erb'
-require 'rubyredis'
-
+require 'store'
require 'domain'
require 'login-signup'
set :sessions, true
-def redis
- $redis ||= RedisClient.new(:timeout => nil)
-end
-
-before do
- keys = redis.keys("*")
+def mb
+ $mb ||= Store.new(['127.0.0.1:11211'])
end
get '/' do
65 domain.rb
View
@@ -3,7 +3,7 @@ class Timeline
def self.page(page)
from = (page-1)*10
to = (page)*10
- post_ids = redis.list_range("timeline", from, to)
+ post_ids = mb.list_range("timeline", from, to)
post_ids.map {|post_id| Post.new(post_id)}
end
end
@@ -27,11 +27,11 @@ def #{name}
end
def _#{name}
- redis.get("#{klass}:id:" + id.to_s + ":#{name}")
+ mb.get("#{klass}:id:" + id.to_s + ":#{name}")
end
def #{name}=(val)
- redis.set("#{klass}:id:" + id.to_s + ":#{name}", val)
+ mb.set("#{klass}:id:" + id.to_s + ":#{name}", val)
end
RUBY
end
@@ -39,30 +39,30 @@ def #{name}=(val)
class User < Model
def self.find_by_username(username)
- if id = redis.get("user:username:#{username}")
+ if id = mb.get("user:username:#{username}")
User.new(id)
end
end
def self.find_by_id(id)
- if redis.key?("user:id:#{id}:username")
+ if mb.exists?("user:id:#{id}:username")
User.new(id)
end
end
def self.create(username, password)
- user_id = redis.incr("user:uid")
+ user_id = mb.incr("user:uid")
salt = User.new_salt
- redis.set("user:id:#{user_id}:username", username)
- redis.set("user:username:#{username}", user_id)
- redis.set("user:id:#{user_id}:salt", salt)
- redis.set("user:id:#{user_id}:hashed_password", hash_pw(salt, password))
- redis.push_head("users", user_id)
+ mb.set("user:id:#{user_id}:username", username)
+ mb.set("user:username:#{username}", user_id)
+ mb.set("user:id:#{user_id}:salt", salt)
+ mb.set("user:id:#{user_id}:hashed_password", hash_pw(salt, password))
+ mb.push("users", user_id)
User.new(user_id)
end
def self.new_users
- redis.list_range("users", 0, 10).map do |user_id|
+ mb.list_range("users", 0, 10).map do |user_id|
User.new(user_id)
end
end
@@ -82,61 +82,61 @@ def self.hash_pw(salt, password)
def posts(page=1)
from, to = (page-1)*10, page*10
- redis.list_range("user:id:#{id}:posts", from, to).map do |post_id|
+ mb.list_range("user:id:#{id}:posts", from, to).map do |post_id|
Post.new(post_id)
end
end
def timeline(page=1)
from, to = (page-1)*10, page*10
- redis.list_range("user:id:#{id}:timeline", from, to).map do |post_id|
+ mb.list_range("user:id:#{id}:timeline", from, to).map do |post_id|
Post.new(post_id)
end
end
def mentions(page=1)
from, to = (page-1)*10, page*10
- redis.list_range("user:id:#{id}:mentions", from, to).map do |post_id|
+ mb.list_range("user:id:#{id}:mentions", from, to).map do |post_id|
Post.new(post_id)
end
end
def add_post(post)
- redis.push_head("user:id:#{id}:posts", post.id)
- redis.push_head("user:id:#{id}:timeline", post.id)
+ mb.push("user:id:#{id}:posts", post.id)
+ mb.push("user:id:#{id}:timeline", post.id)
end
def add_timeline_post(post)
- redis.push_head("user:id:#{id}:timeline", post.id)
+ mb.push("user:id:#{id}:timeline", post.id)
end
def add_mention(post)
- redis.push_head("user:id:#{id}:mentions", post.id)
+ mb.push("user:id:#{id}:mentions", post.id)
end
def follow(user)
return if user == self
- redis.set_add("user:id:#{id}:followees", user.id)
+ mb.set_add("user:id:#{id}:followees", user.id)
user.add_follower(self)
end
def stop_following(user)
- redis.set_delete("user:id:#{id}:followees", user.id)
+ mb.set_delete("user:id:#{id}:followees", user.id)
user.remove_follower(self)
end
def following?(user)
- redis.set_member?("user:id:#{id}:followees", user.id)
+ mb.set_member?("user:id:#{id}:followees", user.id)
end
def followers
- redis.set_members("user:id:#{id}:followers").map do |user_id|
+ mb.set_members("user:id:#{id}:followers").map do |user_id|
User.new(user_id)
end
end
def followees
- redis.set_members("user:id:#{id}:followees").map do |user_id|
+ mb.set_members("user:id:#{id}:followees").map do |user_id|
User.new(user_id)
end
end
@@ -144,23 +144,23 @@ def followees
protected
def add_follower(user)
- redis.set_add("user:id:#{id}:followers", user.id)
+ mb.set_add("user:id:#{id}:followers", user.id)
end
def remove_follower(user)
- redis.set_delete("user:id:#{id}:followers", user.id)
+ mb.set_delete("user:id:#{id}:followers", user.id)
end
end
class Post < Model
def self.create(user, content)
- post_id = redis.incr("post:uid")
+ post_id = mb.incr("post:uid")
post = Post.new(post_id)
post.content = content
post.user_id = user.id
post.created_at = Time.now.to_s
post.user.add_post(post)
- redis.push_head("timeline", post_id)
+ mb.push("timeline", post_id)
post.user.followers.each do |follower|
follower.add_timeline_post(post)
end
@@ -183,12 +183,3 @@ def user
User.new(user_id)
end
end
-
-
-
-
-
-
-
-
-
2  login-signup.rb
View
@@ -26,7 +26,7 @@
post '/signup' do
if params[:username] !~ /^\w+$/
@signup_error = "Username must only contain letters, numbers and underscores."
- elsif redis.key?("user:username:#{params[:username]}")
+ elsif mb.exists?("user:username:#{params[:username]}")
@signup_error = "That username is taken."
elsif params[:username].length < 4
@signup_error = "Username must be at least 4 characters"
70 store.rb
View
@@ -0,0 +1,70 @@
+# A storage abstraction over memcached-client API.
+#
+require 'memcache'
+
+class Store < MemCache
+ def to_s
+ "Store to #{self.servers}"
+ end
+
+ def exists?(key)
+ self.get(key) != nil
+ end
+
+ def incr(key)
+ n = super(key)
+ return n if n
+ self.add(key, '0', 0, true)
+ self.incr(key)
+ end
+
+ def list_range(key, from, to)
+ list = self.get(key, true) # Ex: nil or "+1+2+3".
+ return [] unless list
+ (list.split('+').drop(1) || []).slice(from, to - from + 1)
+ end
+
+ def push(key, value, prefix="+")
+ # Lists are '+' delimited.
+ if self.prepend(key, "#{prefix}#{value}").match("NOT_STORED")
+ self.add(key, '', 0, true)
+ self.push(key, value)
+ end
+ end
+
+ def set_members_hash(key)
+ s = self.get(key, true) # Ex: nil or "+1+2-1"
+ return {} unless s
+ acts = s.split(/[^\+-]/) # Ex: ["+", "+", "-"]
+ vals = s.split(/[\+-]/).drop(1) # Ex: ["1", "2", "1"]
+ m = {}
+ i = acts.length - 1
+ while i >= 0
+ val = vals[i]
+ if acts[i] == '+'
+ m[val] = true
+ else
+ m.delete(val)
+ end
+ i = i - 1
+ end
+ m
+ end
+
+ def set_members(key)
+ self.set_members_hash(key).keys()
+ end
+
+ def set_add(key, value)
+ self.push(key, value, prefix="+")
+ end
+
+ def set_delete(key, value)
+ self.push(key, value, prefix="-")
+ end
+
+ def set_member?(key, value)
+ self.set_members_hash(key)[value]
+ end
+end
+
7 views/footer.erb
View
@@ -2,9 +2,10 @@
</div>
<div class="span-24 last">
<hr />
- Retwis-RB is a simple Twitter clone written in Ruby and Sinatra to show off
- the <a href="http://code.google.com/p/redis/">Redis key-value database</a>.
- The code is on <a href="http://github.com/danlucraft/retwis-rb">Github</a>.
+ mtweet is a simple twitter clone that uses
+ the <a href="http://www.membase.org">Membase database</a>.
+ Thanks to <a href="http://github.com/danlucraft/retwis-rb">retwis-rb</a>
+ for the original fork source.
</div>
</div>
</body>
4 views/header.erb
View
@@ -1,6 +1,6 @@
<ntml>
<head>
- <title>Retwis-RB</title>
+ <title>mtweet</title>
<link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/print.css" type="text/css" media="print">
<!--[if IE]>
@@ -13,7 +13,7 @@
<div class="container">
<div id="header" class="span-24">
<div class="span-12">
- <h1>Retwis-RB</h1>
+ <h1>mtweet</h1>
</div>
<div class="span-12 last right-align">
<% if @logged_in_user %>
Please sign in to comment.
Something went wrong with that request. Please try again.