Permalink
Browse files

Initial import

  • Loading branch information...
danlucraft committed May 24, 2009
0 parents commit bcffad497106c39576756889fbc1efdeb09e2f4b
Showing with 1,048 additions and 0 deletions.
  1. +26 −0 README.md
  2. +125 −0 app.rb
  3. +180 −0 domain.rb
  4. +48 −0 login-signup.rb
  5. +71 −0 public/css/custom.css
  6. +27 −0 public/css/ie.css
  7. +30 −0 public/css/print.css
  8. +252 −0 public/css/screen.css
  9. +92 −0 redis.conf
  10. +12 −0 views/_posts.erb
  11. +9 −0 views/_user_list.erb
  12. +10 −0 views/footer.erb
  13. +29 −0 views/header.erb
  14. +35 −0 views/index.erb
  15. +56 −0 views/login.erb
  16. +28 −0 views/profile.erb
  17. +18 −0 views/timeline.erb
@@ -0,0 +1,26 @@
+Retwis-RB
+=========
+
+An example application using the Redis key-value database.
+
+Daniel Lucraft (dan@fluentradical.com)
+
+Requirements
+------------
+
+ * Ruby
+ * Sinatra: sudo gem install sinatra
+ * Redis: http://code.google.com/p/redis/
+
+Starting Application
+--------------------
+
+Make sure the redis server is running.
+Run:
+
+ ruby app.rb
+
+License
+-------
+
+MIT
125 app.rb
@@ -0,0 +1,125 @@
+
+require 'rubygems'
+require 'sinatra'
+require 'erb'
+require 'redis'
+
+require 'domain'
+require 'login-signup'
+
+set :sessions, true
+
+def redis
+ $redis ||= Redis.new
+end
+
+# redis.flush_db
+
+before do
+ keys = redis.keys("*")
+ p keys
+end
+
+get '/' do
+ @posts = @logged_in_user.timeline
+ erb :index
+end
+
+get '/timeline' do
+ @posts = Timeline.page(1)
+ erb :timeline
+end
+
+post '/post' do
+ if params[:content].length == 0
+ @posting_error = "You didn't enter anything."
+ elsif params[:content].length > 140
+ @posting_error = "Keep it to 140 characters please!"
+ end
+ if @posting_error
+ @posts = @logged_in_user.timeline
+ erb :index
+ else
+ Post.create(@logged_in_user, params[:content])
+ redirect '/'
+ end
+end
+
+get '/:follower/follow/:followee' do |follower_username, followee_username|
+ follower = User.find_by_username(follower_username)
+ followee = User.find_by_username(followee_username)
+ follower.follow(followee)
+ redirect "/" + followee_username
+end
+
+get '/:follower/stopfollow/:followee' do |follower_username, followee_username|
+ follower = User.find_by_username(follower_username)
+ followee = User.find_by_username(followee_username)
+ follower.stop_following(followee)
+ redirect "/" + followee_username
+end
+
+get '/:username' do |username|
+ @user = User.find_by_username(username)
+
+ @posts = @user.posts
+ @followers = @user.followers
+ @followees = @user.followees
+ erb :profile
+end
+
+
+helpers do
+ def link_to_user(user)
+ f = <<-HTML
+ <a href="/#{user.username}">#{user.username}</a>
+ HTML
+ end
+
+ def pluralize(singular, plural, count)
+ if count == 1
+ count.to_s + " " + singular
+ else
+ count.to_s + " " + plural
+ end
+ end
+
+ def time_ago_in_words(time)
+ distance_in_seconds = (Time.now - time).round
+ case distance_in_seconds
+ when 0..10
+ return "just now"
+ when 10..60
+ return "less than a minute ago"
+ end
+ distance_in_minutes = (distance_in_seconds/60).round
+ case distance_in_minutes
+ when 0..1
+ return "a minute ago"
+ when 2..45
+ return distance_in_minutes.round.to_s + " minutes ago"
+ when 46..89
+ return "about an hour ago"
+ when 90..1439
+ return (distance_in_minutes/60).round.to_s + " hours ago"
+ when 1440..2879
+ return "about a day ago"
+ when 2880..43199
+ (distance_in_minutes / 1440).round.to_s + " days ago"
+ when 43200..86399
+ "about a month ago"
+ when 86400..525599
+ (distance_in_minutes / 43200).round.to_s + " months ago"
+ when 525600..1051199
+ "about a year ago"
+ else
+ "over " + (distance_in_minutes / 525600).round.to_s + " years ago"
+ end
+ end
+end
+
+
+
+
+
+
180 domain.rb
@@ -0,0 +1,180 @@
+
+require 'redis'
+
+class Timeline
+ def self.page(page)
+ from = (page-1)*10
+ to = (page)*10
+ post_ids = redis.list_range("timeline", from, to)
+ post_ids.map {|post_id| Post.new(post_id)}
+ end
+end
+
+class Model
+ def initialize(id)
+ @id = id
+ end
+
+ def ==(other)
+ @id.to_s == other.id.to_s
+ end
+
+ attr_reader :id
+
+ def self.property(name)
+ klass = self.name.downcase
+ self.class_eval <<-RUBY
+ def #{name}
+ _#{name}
+ end
+
+ def _#{name}
+ redis.get("#{klass}:id:" + id.to_s + ":#{name}")
+ end
+
+ def #{name}=(val)
+ redis.set("#{klass}:id:" + id.to_s + ":#{name}", val)
+ end
+ RUBY
+ end
+end
+
+class User < Model
+ def self.find_by_username(username)
+ if id = redis.get("user:username:#{username}")
+ User.new(id)
+ end
+ end
+
+ def self.find_by_id(id)
+ if redis.key?("user:id:#{id}:username")
+ User.new(id)
+ end
+ end
+
+ def self.create(username, password)
+ user_id = redis.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)
+ User.new(user_id)
+ end
+
+ def self.new_users
+ redis.list_range("users", 0, 10).map do |user_id|
+ User.new(user_id)
+ end
+ end
+
+ def self.new_salt
+ arr = %w(a b c d e f)
+ (0..6).to_a.map{ arr[rand(6)] }.join
+ end
+
+ def self.hash_pw(salt, password)
+ Digest::MD5.hexdigest(salt + password)
+ end
+
+ property :username
+ property :salt
+ property :hashed_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|
+ 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|
+ 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)
+ end
+
+ def add_timeline_post(post)
+ redis.push_head("user:id:#{id}:timeline", post.id)
+ end
+
+ def follow(user)
+ return if user == self
+ redis.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)
+ user.remove_follower(self)
+ end
+
+ def following?(user)
+ redis.set_member?("user:id:#{id}:followees", user.id)
+ end
+
+ def followers
+ redis.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|
+ User.new(user_id)
+ end
+ end
+
+ protected
+
+ def add_follower(user)
+ redis.set_add("user:id:#{id}:followers", user.id)
+ end
+
+ def remove_follower(user)
+ redis.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 = 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)
+ post.user.followers.each do |follower|
+ follower.add_timeline_post(post)
+ end
+ end
+
+ property :content
+ property :user_id
+ property :created_at
+
+ def created_at
+ Time.parse(_created_at)
+ end
+
+ def user
+ User.new(user_id)
+ end
+end
+
+
+
+
+
+
+
+
+
@@ -0,0 +1,48 @@
+
+before do
+ unless %w(/login /signup).include?(request.path_info) or
+ request.path_info =~ /\.css$/ or
+ @logged_in_user = User.find_by_id(session["user_id"])
+ redirect '/login', 303
+ end
+ puts "logged in as:#{@logged_in_user.username}" if @logged_in_user
+end
+
+get '/login' do
+ erb :login
+end
+
+post '/login' do
+ if user = User.find_by_username(params[:username]) and
+ User.hash_pw(user.salt, params[:password]) == user.hashed_password
+ session["user_id"] = user.id
+ redirect '/'
+ else
+ @login_error = "Incorrect username or password"
+ erb :login
+ end
+end
+
+post '/signup' do
+ if redis.key?("user:username:#{params[:username]}")
+ @signup_error = "That username is taken."
+ elsif params[:username].length < 4
+ @signup_error = "Username must be at least 4 characters"
+ elsif params[:password].length < 6
+ @signup_error = "Password must be at least 6 characters!"
+ elsif params[:password] != params[:password_confirmation]
+ @signup_error = "Passwords do not match!"
+ end
+ if @signup_error
+ erb :login
+ else
+ user = User.create(params[:username], params[:password])
+ session["user_id"] = user.id
+ redirect "/"
+ end
+end
+
+get '/logout' do
+ session["user_id"] = nil
+ redirect '/login'
+end
Oops, something went wrong.

0 comments on commit bcffad4

Please sign in to comment.