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