Sinatra promotional site tutorial
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
- Pushed on 09/17/2011 by Christian
