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

Comment on this article?