Last week, I talked about how I structure my Sinatra apps. Today I decided to automate that process, so here’s a small shell script to auto-generate the directory structure and a few essential base files.
March 2012
17 posts
I thought it might be interesting to show how I structure my Sinatra applications. The app shown below (itemize.cc) is fairly small, so some of this may be a bit overkill, but I decided to go ahead and go with this level of structure, so I could easily expand the app in the future without having to worry about things getting disorganized.
![]()
Things of note:
The app.yml file is a config file that stores app-specific config items. For instance, in this case, it has the db connection string, wkhtmltopdf path, whether or not to run migrations on start up, and SMTP server details.
Inside the main app, I typically pull in files by directory, so dropping in new files is painless.
Often, I’ll also include a lang directory, with language files (e.g. en-us.rb) and then set the default language in app.yml as well.
You can see it all come together like so:
require 'rubygems'
require 'sinatra'
configure do
@@config = YAML.load_file("./app.yml") rescue nil || {}
end
Dir["./helpers/*.rb"].each {|file| require file}
DataMapper.setup(:default, ENV['DATABASE_URL'] || @@config["db_connection_string"])
DataMapper::Property::String.length(255)
Dir["./models/*.rb"].each {|file| require file}
DataMapper.finalize
if @@config["db_run_migrations"] == true
DataMapper.auto_migrate!
DataMapper.auto_upgrade!
end
not_found do
erb :not_found
end
error do
erb :error
end
get '/' do
erb :index
end
Dir["./routes/*.rb"].each {|file| require file}
Let me preface this by saying that I am probably late to the party on this one.
I’ve been using Git as my VCS for a long time, and I love it, but I have to admit, I’ve been pretty disorganized in the way in which i use it. My Git workflow has been basically this:
Do some work, get to a point where things work, make a commit, push.
I’d make the occasional branch if was doing something radically different, like a big rewrite, or refactoring, throw in an occasional rebase or filter-branch, sure, but that was basically how it went.
For most of the work I do, I am the only developer involved, or there may be one other person reviewing the code and making small commits. So this method has worked OK, in that let me do things like roll back to an earlier commit when I would screw something up.
However, it never felt like I was utilizing Git to its fullest potential, and I’d never really thought about Git as a tool for documenting the development life-cycle of my code. That is, not until a few days ago when a good friend introduced me to Git-flow.
Git-flow is both a methodology and a set of command-line tools to put that methodology into practice. In a nutshell, you work off of a development branch, creating hotfixes, support code, and features that follow a standard naming convention, and then get merged into the development branch. From there, you create releases, which them merge with the master, once you’ve done all of your testing and tidying up. So your master branch never has anything other than stable release code merged into it and your development branch has a full history of all changes, all properly categorized.
I’ve only been at this a few days, but i absolutely love it. It takes a bit of getting used to, but I love the history that it creates, the built-in safety net, and the structure it imposes on the code. I’m still adjusting, but I think it’s definitely been a worthwhile switch that will save a lot of headache in the future, especially when multiple developers are involved.
Say Hello to Itemize.cc
I’ve been working on a little personal project and yesterday I put it up on the web for all to see (and this morning i found and fixed a fatal bug….. go figure). So here it is: Itemize.cc
It’s a very simple invoicing tool. It lets you quickly create and share invoices with your clients, and gives them a big, friendly button that they can click to pay your invoice through PayPal. It also gives them, and you, an option to download the invoice as a PDF file.
That’s about it. There’s no sign-up or registration required. It’s just very basic, simple invoicing.
I still need to do some work on it, optimization for mobile, that kind of thing, so consider this a beta release.
Itemize was built with Sinatra.
I’ll admit to being guilty of forgetting about the print styles, myself, when it comes to my own stuff, or at least of having had it fall into the category of “haven’t gotten around to it yet.”
A while back, we made an unofficial company policy, if you will, to stop referring to the people who used our applications as users. We stopped calling them that on the web sites, in the documentation, and even in the code.
We started calling them people, instead, because that’s what they are. The term “users” made it sound like we cater to a bunch of junkies or ingrates. We don’t. We build apps for people.
It sounds like such a small thing, but over time, it really shapes your perspective, which can have a big impact on the way you think about your UX. You’re no longer developing for some generic ‘User’ object, you’re creating an experience for a real, live person.
People and users have different needs. Users require instruction and access to a presentation layer that allows them to do stuff. A person needs to understand, needs to feel important, needs empathy, has to pick the kid up from soccer practice at 4, hold their phone in one hand and the baby in the other, and so on.
Person carries with it a degree of respect and responsibility that User does not.
I’m generally not a big fan of splash pages. I think they do little more than to create another barrier of entry and make more work for the person visiting your site. These once-common, but thankfully less-frequently occurring pages still have their occasional uses, though.
Recently, I was working on a site for a client who was running a big promotion and they really wanted to advertise it with a splash page on their site, which is powered by Wordpress. After some discussion about what would be the most effective and least painful for their visitors, we decided on a splash page that would appear no more than once a day, no matter hwo many times the visitor went to the site. The visitor would be able to click through the splash page, and it would display for no more than 20 seconds (long enough to read it) if they didn’t.
At first, this seemed simple. I would just add ‘index.html’ to the DirectoryIndex directive in their .htaccess file and upload a static HTML file for the splash page, with a JavaScript redirect.
However, that doesn’t exactly work - Wordpress’ own rewrite scripts will send you into an infiniate loop of redirects if you go this route.
So here’s my simple solution. Go to your currently active theme and open the header.php file. You’re going to build your splash page inside there.
Place your splash page HTML at the very top of this file, wrapped in a couple of conditonal statements that make sure the page isn’t being accessed via an internal link, and that check for the existence of the ‘seen’ cookie.
<?php if ((strpos($_SERVER['HTTP_REFERER'], get_bloginfo('home')) === false)
&& !$_SERVER['QUERY_STRING'] && !isset($_COOKIE['seen'])): ?>
<!DOCTYPE html>
<head>
<title>Your Site</title>
</head>
<body>
<a href="index.php">Continue to main site...</a>
<script type="text/javascript">
var days = 1 // change this to make the cookie last longer than 24hrs
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
document.cookie = "seen=true"+expires+"; path=/";
setTimeout(function() {
window.location.href="/";
}, 20000);
</script>
</body>
</html>
<?php
exit;
endif;
?>
There you go. that’s all there is to it.
Note the JavaScript in the splash page, it’s very important. We set a cookie called ‘seen’ with a value of true whenever the page is visited, then we redirect to the website root after 20 seconds. Without this, your visitors will see the splash page every time they return to the website.
Quickly test responsive designs at multiple device resolutions.
![]()
I love a good -inator.
…is that Tim Cook needs to stop trying to give Steve Jobs presentations and find his own voice. Mr. Cook is a very sharp, smart guy, but yesterday’s presentation felt very forced and like a bad impersonation. Phill Schiller’s presentation was much better.
For many years, Jobs’ voice was Apple’s voice, and changing that can certainly have a big impact on your brand, but poorly executing it is, I think, even worse.
Jobs’ death was tragic, and regardless of your feelings about him, or Apple, there’s no denying that it had a huge impact on a lot of people who need and want time to mourn that loss, and that’s fine, but at some point you have to embrace the change in leadership for its new potentials and possibilities.
So no more strained, Steve Jobs-esque presentations, please. Let’s get to know Tim Cook.
I recently had to set up a couple of web servers to handle multiple Sinatra applications in production. Going from a fresh install of the operating system (Ubuntu in this case) to fully working server, with Nginx, Passenger, Ruby, and Git-based deployment is a bit of a lengthy process, so I thought I’d take a few moments to outline the steps I took below.
A Couple of Notes:
- I was setting up some very lightweight applications, which didn’t use a database, so there are no database installation instructions outlined below, and the default configs for Passenger, Nginx, etc. as far as memory usage, pool sizes, and so on, were fine. If you’re deploying beefier applications, you will need to allocate resources accordingly.
- My server environment, in this case, was Ubuntu on an EC2 instance. All firewall configuration was done beforehand from the EC2 console. If you’re setting up on a different VPS provider, you’ll need to manually configure your firewall via iptables.
Now, on to the good bits. We’ll be jumping back and forth between our local machine and the remove server a bit, so let’s start things off….
On the SERVER
Update the OS
sudo apt-get update sudo apt-get upgrade
Set the Hostname
sudo nano /etc/hostname
Install Utilities
sudo apt-get install wget build-essential
Install & Configure Git
sudo apt-get install git-core
git config --global user.name = "<name>"
git congit --global user.email = "<email>"
Install & Configure Gitosis
git clone git://eagain.net/gitosis.git
cd gitosis
sudo apt-get install python-setuptools
sudo python setup.py install
sudo adduser --system --shell /bin/sh --gecos 'git version control' --group --disabled-password --home /home/git git
Copy your SSH keypair to ~/.ssh then set permissions, initialize Gitosis and ensure proper permissions on update hooks.
chmod 0600 ~/.ssh/id_rsa
sudo -H -u git gitosis-init < ~/.ssh/id_rsa.pub
sudo chmod 755 /home/git/repositories/gitosis-admin.git/hooks/post-update
To avoid pesky ownership permissions, let’s just add Git to the sudoers file so that we can run our post-update hooks without issue. If you’re super concerned about security, you can skip this step and and just make Git the owner of each application root directory.
sudo visudo git ALL=(ALL) NOPASSWD:ALL
Install RVM
sudo bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
umask g+w
source /etc/profile.d/rvm.sh
sudo apt-get install openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion
Now give your user permissions to do stuff in RVM.
sudo chown -R [user]:[user] /usr/local/rvm
Install Ruby & RubyGems
rvm install 1.9.2
source "/usr/local/rvm/scripts/rvm"
rvm --default use 1.9.2
Install Gems
gem install rack rake sinatra thin bundler
Install Nginx & Mod Rails
sudo apt-get install libcurl4-openssl-dev libssl-dev zlib1g-dev gem install passenger
rvmsudo passenger-install-nginx-module
Select the first option and set install dir to /etc/nginx (purely personal preference, but it feels more consistent than having it in /opt)
Install Nginx Control Script
cd /opt
sudo wget -O init-deb.sh http://library.linode.com/assets/604-init-deb.sh
mv /opt/init-deb.sh /etc/init.d/nginx
chmod +x /etc/init.d/nginx
/usr/sbin/update-rc.d -f nginx defaults
Since we changed the install directory, we have to edit the control scripts.
sudo nano /etc/init.d/nginx
edit PATH and DAEMON to point to the right location
Make some directories
cd /srv
sudo mkdir www
sudo mkdir
www-logs
sudo mkdir www/<project>
sudo chmod -R 0777 www
sudo chmod -R 0777 www-logs
Set up Nginx Virtual Hosts
sudo nano /etc/nginx/conf/nginx.conf
Below is a sample nginx.conf file:
worker_processes 1;
events {
worker_connections 1024;
}
http {
passenger_root /usr/local/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.11;
passenger_ruby /usr/local/rvm/wrappers/ruby-1.9.2-p290/ruby;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name www.<project> <project>;
access_log /srv/www-logs/<project>.access.log;
error_log /srv/www-logs/<project>.error.log;
root /srv/www/<project>/public;
passenger_enabled on;
location /static {
root /srv/www/<project>/public;
index index.html;
}
}
}
Set up Gitosis Hosting for Repos
cd ~
git clone git@<server>:gitosis-admin.git
cd gitosis-admin
nano gitosis.conf
Below is a sample gitosis.conf setup:
[group <company>]
writable = <project>
members = <user>
Then commit it.
git add .
git commit -a -m "adding <project> repos"
git push
Go to your LOCAL repos!
Add remotes to repos and Push
cd ~/www/<project>
git remote add production git@<server>:<project>.git
git push production master
Back to the SERVER!
Set up Deploy Hooks
sudo nano /home/git/repositories/<project>.git/hooks/post-update
Below is a sample post-update script:
#!/bin/sh
git archive --format=tar HEAD | (cd /srv/www/<project>/ && tar xf -) sudo touch /srv/www/<project>/tmp/restart.txt
Make it executable.
sudo chmod 0755 /home/git/repositories/<project>.git/hooks/post-update
And now back to the LOCAL repos!
Deploy Stuff
cd ~/www/<project>
mkdir tmp
touch tmp/restart.txt
git add .
git commit -a -m "adding restart.txt for passenger"
git push production
Back to the SERVER!
Restart Nginx
sudo /etc/init.d/nginx restart
And that should be it. You should now be good to go.
Every time I use a weakly typed language: “I wish this language had strong typing.”
Every time I use a strongly typed language: “I wish this language had weak typing.”
Things are getting busy at work. We’re rolling out several new apps for web and various mobile platforms. Nearly each of these apps has an accompanying REST API, but each is very different. Documentation was starting to get a bit fragmented and tedious.
So I spent a few hours last week/this weekend building Happy Docs, a lightweight CMS for creating HTML documentation for REST APIs (Get it? HTML+API+docs=Happy Docs) and I have been given the OK to open source it.
The current feature set is very MVP with a few small bonuses, like the ability to have private (password-protected) projects, exporting to PDF, and rudimentary theme support on the public docs. the admin UI also still needs some clean-up, I feel, but it’s all perfectly functional and I like where it’s heading.
So if you’re building APIs and looking for a good way to document them, check it out. We think it’s pretty nifty.
(Happy Docs was built in CodeIgniter runs on PHP5.3+ and requires MySQL and wkhtmltopdf)