Go back to the main page

Sinatra promotional site tutorial

This article is over 2 years old. Proceed with caution.

Regards ♨ – Minimul

 

Making a promotional site with Sinatra

Sunnydirtroad.com is a web site put up to get some feedback on an idea (promotional site). Typically, what is needed here is a one page site that collects emails. Moreover, you want a protected admin page where the email responses can be viewed as well as some analytics. This is a "simple" use case but has some gotchas if you are coming over from Rails-only experience.

Pre requites

  • Ubuntu Server
  • Passenger and Apache
  • Editing as root user
  • "Development" environment setup

1. Install gems

gem install sinatra
gem install sqlite3
gem install rack-flash

2. Basic sinatra file setup

mkdir /var/www/sunnydirtroad # referred to as DocumentRoot is some parts of the article
cd /var/www/sunnydirtroad

2a. Create config.ru

vim config.ru
# contents of config.ru 
require 'rubygems'
require 'sinatra'

root_dir = File.dirname(__FILE__)

set :environment,ENV['RACK_ENV'].to_sym
set :root,root_dir

disable :run


log = File.new('sinatra.log','a')
$stdout.reopen(log)
$stderr.reopen(log)

require 'app'
run Sinatra::Application
# end contents of ./config.ru

2b. Add app.rb

cd /var/www/sunnydirtroad
vim app.rb
# contents of app.rb
require 'rubygems'
require 'sqlite3'
require 'logger'
require 'rack-flash'
enable :sessions
use Rack::Flash

configure do 
  LOGGER = Logger.new('sinatra.log')
end

helpers do
  include Rack::Utils
  alias_method :h, :escape_html

  def logger
    LOGGER
  end

  def page_title
    'Rent Berenstain Bear books'
  end

end

get '/' do
  @page_title = page_title
  erb :index
end

2c. Setup basic homepage

cd /var/www/sunnydirtroad
mkdir views
mkdir stylesheets
touch stylesheets/master.css
touch views/index.erb
touch views/layout.erb # Sinatra will automatically use this file for layout
2d. Edit layout.erb
vim views/layout.erb
<!DOCTYPE html> 
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title><%= h(@page_title) %></title>
  <link rel="stylesheet" href="/stylesheets/master.css" type="text/css" media="screen" />
</head>
<body>
  <div class="container">
  <%= yield %>
  </div>
</body>
</html>

2e. Edit views/index.rb
vim views/index.erb
<div id="content">
  <h1 id="header"><%= h(@page_title) %></h1>
</div>

3. Enable Apache

3a. Make sure permissions are correct

# I like to make a little permissions file "./perm.sh" in the document root
# contents of document root/perms.sh
chown -R www-data.www-data .
chmod -R 755 .
# end contents
chmod +x perms.sh
./perms.sh # run it

3b. Apache hook-up

cd /etc/apache2
vim ./sites-available/sunnydirtroad
# contents of sunnydirtroad
<VirtualHost *:80>
  ServerName sunnydirtroad
  DocumentRoot /var/www/sunnydirtroad/public
  RackEnv development
</VirtualHost>
# end contents of sunnydirtroad
# Enable sunnydirtroad
a2ensite sunnydirtroad
# Add sunnydirtroad to localhosts
vim /etc/hosts
# partial contents of /etc/hosts
127.0.0.1       sunnydirtroad
# end partial contents of /etc/hosts

3c. Enable Apache and goto site

# First create {DocumentRoot}/tmp/always_restart.txt so Sinatra reloads every 
# time on a refresh. Of course don't do this in production
cd /var/www/sunnydirtroad
touch tmp/always_restart.txt
# Reload Apache
/etc/init.d/apache2 reload
# Fire up in browser
http://sunnydirtroad

4. Collect emails

4a. Add the form html code to views/index.erb

  <h3 id="interested"><span>Interested? Send your email:</span></h3>
  <form method="post">
    <input name="email" size="30" /> <button type="submit">Send</button>
  </form>

4b. Store the submitted emails. Setup a sqlite3 database and a basic 3 column email table.

cd /var/www/sunnydirtroad
sqlite3 dirtroad.db
# using "id integer primary key" will make a autoincrement column don't use INT use INTEGER
sqlite> create table emails (id integer primary key,email varchar(60),created_at datetime);
sqlite> insert into emails values (null,'test@oddpost.com',datetime('now'));
sqlite> select * from emails;
1|test@oddpost.com|2010-11-08 17:13:04
.quit
# Make sure the ownership is correct
chown www-data.www-data dirtroad.db

4c. Backend Sinatra code for storing the emails. Add to the configure section (see step 2b) and a post '/' to app.rb.


# Edit the configure section to your app.rb code see step 2b.
configure do 
  LOGGER = Logger.new('sinatra.log')
  DB = SQLite3::Database.new("dirtroad.db")
  DB.results_as_hash = true
end

# Add this below the "get '/' do" section
post '/' do
  @page_title = page_title
  DB.execute("INSERT INTO emails VALUES (NULL,?,datetime('now'),?)",params[:email],request.ip)
  flash[:notice] = 'Ok, got it. Will keep you updated on the progress of Sunny Dirt Road.'
  redirect '/'
end

5. Make a admin page in where you check the responses. To protect people's emails, make it a protected page.

5a. Set up a new page /check/responses

# First add ADMIN_USERNAME and ADMIN_PASSWD in the configure block in app.rb
configure do 
  LOGGER = Logger.new('sinatra.log')
  DB = SQLite3::Database.new("dirtroad.db")
  DB.results_as_hash = true
  ADMIN_USERNAME  = 'dirtuser'
  ADMIN_PASSWD    = 'dirtpass'
end
# Second, add the protected and authorized helpers in app.rb
helpers do
  include Rack::Utils
  alias_method :h, :escape_html

  def logger
    LOGGER
  end

  def page_title
    'Rent Berenstain Bear books'
  end

  def protected!
    response['WWW-Authenticate'] = %(Basic realm="SunnyDirtRoad Administration") and \
    throw(:halt, [401, "Not authorized\n"]) and \
    return unless authorized?
  end

  def authorized?
    return false unless ADMIN_USERNAME && ADMIN_PASSWD
    @auth ||=  Rack::Auth::Basic::Request.new(request.env)
    @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == [ADMIN_USERNAME, ADMIN_PASSWD]
  end
end
# Third, also in app.rb setup the route
get '/check/responses' do
  protected!
  @page_title = 'Check responses'
  @data = DB.execute("SELECT * FROM emails")
  erb :check_responses
end

5b. Create the views/check_responses.erb page

# Contents of views/check_responses.erb
<table id="crTable">
  <tr><td>Email</td><td>Ip addr</td><td>Date</td></tr>
  <% @data.each do |d| %>
  <%= sprintf('<tr><td>%s</td><td>%s</td><td>%s</td></tr>',h(d['email']),d['ip_addr'],d['created_at']) %>
  <% end %>
</table>

6. Add Analytics to views/layout.erb

<!DOCTYPE html> 
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title><%= h(@page_title) %></title>
  <link rel="stylesheet" href="/stylesheets/master.css" type="text/css" media="screen" />
</head>
<body>
  <div class="container">
  <%= yield %>
  </div>
<% if Sinatra::Application.production? and @analytics %>
  <script type="text/javascript">

    var _gaq = _gaq || [];
    _gaq.push(['_setAccount', 'UA-XXXXXXX-4']);
    _gaq.push(['_trackPageview']);

    (function() {
      var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
      ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
      var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
    })();

  </script>
<% end %>
</body>
</html>

Conclusion

Building a promotional site is the perfect use case for the lightweight (server resource wise) ruby web framework, Sinatra. All the code for this tutorial can be found here