4 months ago
Mass Assignment Protection, DataMapper, and Sinatra
Tip: You can use the DM Rails adapter MassAggisnmentSecurity sub-module inside Sinatra if you’d rather limit attribute access at the model level, rather than handling it in the controller (which certainly feels cleaner to me).
For example:
require 'dm-rails/mass_assignment_security'
class Status
include DataMapper::Resource
include DataMapper::MassAssignmentSecurity
attr_accessible :account_id, :contents, :source
end
You’ll, of cours,e need to install the dm-rails gem.
Edit: Just a not eI discovered after updating my gems yesterday. You cannot have two versions of Rack installed when using the Rails gem inside Sinatra. Rails will load Rack 1.4 first, and cause Sinatra to be unable to load Rack 1.3.5.
4 months ago
Vesper; a Sinatra web framework
When it comes to structuring larger Sinatra apps, I’ve mostly just “rolled my own” following a basic MVC style architecture. Vesper’s aim seems to be to take some of the work out of that, and it does look cleaner than a couple others that I’ve looked at. I’m definitely going to be keeping an eye on this one and probably testing it out soon.
2 months ago
Sinatra Producton Server Setup
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.
2 months ago
Structuring Sinatra Applications
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}
1 month ago
Bash Script to set up Sinatra App
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.