We are building an app which allows visitors to:

  • Submit links
  • Vote links up or down
  • Sort links by the newest or hot


The tutorial assumes that you have ruby installed.

1. Install Gems

1
gem install sinatra sqlite3 dm-sqlite-adapter data_mapper sinatra-reloader haml

2. Create Structure

mkdir -p reddit/public/css
touch reddit/reddit.rb reddit/public/css/style.css reddit/public/css/bootstrap.css

3. Add Code

reddit.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
require 'sinatra'
require 'data_mapper'
require 'haml'
require 'sinatra/reloader'

DataMapper::setup(:default,"sqlite3://#{Dir.pwd}/example.db")

class Link
  include DataMapper::Resource
  property :id, Serial
  property :title, String
  property :url, Text
  property :score, Integer
  property :points, Integer, :default => 0
  property :created_at, Time

  attr_accessor :score

  def calculate_score
    time_elapsed = (Time.now - self.created_at) / 3600
    self.score = ((self.points-1) / (time_elapsed+2)**1.8).real
  end

  def self.all_sorted_desc
    self.all.each { |item| item.calculate_score }.sort { |a,b| a.score <=> b.score }.reverse
  end
end

DataMapper.finalize.auto_upgrade!

get '/' do
  @links = Link.all :order => :id.desc
  haml :index
end

get '/hot' do
  @links = Link.all_sorted_desc
  haml :index
end

post '/' do
  l = Link.new
  l.title = params[:title]
  l.url = params[:url]
  l.created_at = Time.now
  l.save
  redirect back
end

put '/:id/vote/:type' do
  l = Link.get params[:id]
  l.points += params[:type].to_i
  l.save
  redirect back
end

Require Gems: Lines 1-4 call our dependencies.

Setup Database: Line 6 initiates our database named example.db.

Create Table: Lines 8-15 create our Link table with several attributes.

Access Virtual Attribute: Line 17 makes our virtual attribute called score accessible outside of the Link class.

Define Algorithm: Lines 19-22 define the algorithm used to calculate thescore of each link.

Sort Links: Lines 24-26 prepare a sorted array of links using the score calculated using our algorithm.

Initialize Database: Line 29 finalizes our database and upgrades our schema as changes are made to the model.

New Links: Lines 31-34 return a page of the newest links sorted by id.

Hot Links: Lines 36-39 return a page of the hottest links sorted by score based on our algorithm.

Post Link: Lines 41-48 create a new Link record with data provided from the in-page form and redirects the user to the current page.

Vote: Lines 50-55 update Link record points according to a user’s vote and redirects the user to the current page.

4. Add Haml

reddit.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
__END__

@@ layout
%html
  %head
    %link(rel="stylesheet" href="/css/bootstrap.css")
    %link(rel="stylesheet" href="/css/style.css")
  %body
    .container
      #main
        .title Learn Sinatra
        .options  
          %a{:href => ('/')} New 
          | 
          %a{:href => ('/hot')} Hot
        = yield

@@ index
#links-list 
  -@links.each do |l| 
    .row
      .span3
        %span.span
          %form{:action => "#{l.id}/vote/1", :method => "post"}
            %input{:type => "hidden", :name => "_method", :value => "put"}
            %input{:type => "submit", :value => "⇡"}
        %span.points
          #{l.points}
        %span.span
          %form{:action => "#{l.id}/vote/-1", :method => "post"}
            %input{:type => "hidden", :name => "_method", :value=> "put"}
            %input{:type => "submit", :value => "⇣"}        
      .span6
        %span.link-title
          %h3
            %a{:href => (l.url)} #{l.title}
#add-link
  %form{:action => "/", :method => "post"}
    %input{:type => "text", :name => "title", :placeholder => "Title"}
    %input{:type => "text", :name => "url", :placeholder => "Url"}
    %input{:type => "submit", :value => "Submit"} 

5. Add style.css

style.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
body {
  background-color: #2E8183;
}

#links-list {
  margin-top: 25px;
}

.title {
  font-size: 28px;
  text-align: center;
  font-weight: bold;
}

.options {
  font-size: 18px;
  text-align: center;
  margin-top: 12px;
}

.points {
  display: inline;
  float: left;
  margin-left: 20px;
  position: relative;
  top: 5px;
}

.link-title {
  display: inline;
  float: left;
  margin-left: 20px;
  position: relative;
  bottom: 5px;
}

.container {
    background-color: white;
    width: 500px;
    border-radius: 5px;
    padding-left: 25px;
    padding-right: 25px;
    padding-top: 25px;
    margin-top: 10px;
}

6. Copy Conents of Bootstrap File into bootstrap.css

7. Start Server

ruby reddit.rb

Github

Version 1.0 Github | Tutorial

Version 2.0 Github | Tutorial

Notes

Comments