Note: This file can be viewed as both a readme.txt in the root of the repo and online
Introduction
Heroku is a pretty awesome way to host and deploy Rack apps very quickly using git. I'm a big fan of camping, and keen to try out the new camping 2.0 so Heroku seeming like a great place to test it out (did I mention their pricing starts at free?)
The heroku docs are great, I found out pretty much everything I needed from there (I'm a long way from being very proficient in git, ruby, rack or camping)
This brief guide is what I ended up with (with a minimal amount of hassle!). I've essentially deployed a tiny 'hello world' app and the blog example (straight out the box), along with this guide.
Update - Apr-2010
Camping 2.0 is now released as a gem, check out the Update Notes in line with the below to use the latest gem instead of copying in the source.
Part 1 - Hello World
Create a folder structure of:
- my_app
- lib Update Note - you don't need this folder and it's contents if you use the gem
- camping
- ar.rb
- etc.
- camping.rb
- camping
- lib Update Note - you don't need this folder and it's contents if you use the gem
- src
- hello.rb
- .gems
- config.ru
Update Note - You can ignore the '/lib' directory and you don't need to add the $unshift line (now commented out) in hello.rb
The lib directory is the '/lib' directory from the camping github structure. Check it out elsewhere and copy it in. We need to do this as camping 2.0 is not yet released as a gem and thus is not installed on Heroku
Create a super simple camping app in hello.rb - no tricks here, just note the unshift is pointing at the 'lib' directory so we are pulling in the 2.0 version
#$:.unshift File.dirname(__FILE__) + '/../lib' # Uncomment this line if you're not using the gem and have the source in the '/lib' folder instead
require 'camping'
Camping.goes :Hello
module Hello::Controllers
class Index < R '/'
def get
render :hello
end
end
end
module Hello::Views
def hello
p "Hello World!"
end
end
Edit config.ru to fill out your rack config
require 'src/hello'
run Hello
Markaby is not installed out the box on Heroku, so we need to specify it in our local gems list in .gems
Update Note - Add in the camping 2 gem to .gems if you're not using the source
markaby
camping --version '>= 2.0'
You can check that works locally (assuming you have all the prereqs installed (e.g. heroku, rack, mongrel, metaid, markaby etc.). From inside my_app directory
$ rackup -p 3301 config.ru
That should start the server on http://localhost:3301
Getting this up on Heroku is now dead easy, (you need to be signed up for Heroku) then just run the below (this is straight from the Heroku docs)
$ git init
$ git add .
$ git commit -m 'lets go camping 2.0'
$ heroku create
$ git push heroku master
This should give you a success message and a link to your newly created app (likely to sound very zen).
Easy huh? Yep, almost embarrassingly so. Though in order to get a real app up there are a few more steps we need. Namely the database, some static file serving and we probably want a couple more apps up there (remember camping is the little wheels!).
Top up the kettle and lets crack on...
Part 2- Adding the blog
Add the new files below
- my_app
- config
- database.yml
- db
- (leave this db directory empty for now)
- lib Update Note - you don't need this folder and it's contents if you use the gem
- camping
- ar.rb
- etc.
- camping.rb
- camping
- public
- css
- site.css
- js
- css
- src
- blog.rb
- hello.rb
- .gems
- config.ru
- config
Add the blog.rb (exactly as is from the camping examples folder) into our src folder.
We're going to want a local development database (sqlite3), and to use the postgresSQL database provided by Heroku when in production.
Locally we'll specify that in the same was as heroku does (the rails way) so create database.yml in the local config directory, pointing at a sqlite3 database in the db folder.
development:
adapter: sqlite3
database: db/development.sqlite3
pool: 5
timeout: 5000
Heroku automatically generates a similar file for you on their system, so in production this is all sorted for us. We need to tell the blog app to use the appropriate DB in each environment. We can make use of the environment variables set by Heroku to do this.
Add the following to the top of config.ru (see below for the full file)
require 'src/blog'
# load the config files (this will be either ours locally or Heroku
dbconfig = YAML.load(File.read('config/database.yml'))
# decide if we are running on Heroku based on the presence of their DB environment variable and use the appropriate DB
environment = ENV['DATABASE_URL'] ? 'production' : 'development'
# attach to the DB and run the create method for the Blog app
Blog::Models::Base.establish_connection dbconfig[environment]
Blog.create if Blog.respond_to? :create
There is some really clever stuff about copying databases (using taps) from production to dev on the Heroku Import/Export help page
We want to be able to run our existing hello world app to so we need to use rack's url mapper to route these about. Still in config.ru change the 'run Hello' line too
# mount both apps (see note about blog on other URLs that '/'
apps = Rack::URLMap.new(
'/hello' => Hello,
'/' => Blog
)
run apps
We're good to try locally now (remember to restart to see your changes). You should see output as the blog tables are generated. The details for the blog user are 'admin'/'camping'. NOTE - this may be me doing something wrong, but I can't make the user sign in if I mount the blog app anywhere but '/'. I'm going to ask about and check it's not a bug
$ rackup -p 3301 config.ru
Hopefully that all works! I wanted to be able to have some static files (js, css) and not to have to serve them through camping. Easy to add these in using Rack:Static to serve the files.
I've added these in subfolders to a container folder called public, partially for (rails) conventions sake, and partially because I don't want to mount static folders at the root. Add the below to config.ru anywhere above the 'run apps', you may want to add some test content so you can see this work
# loop through each folder in 'public' and mount it at '/subfolder-name', e.g. '/css' etc.
Dir.chdir('public') do
public_dirs = (Dir.glob("*").find_all{|entry| File::directory?(entry)}).collect{|dir| '/' + dir}
use Rack::Static, :urls => public_dirs, :root => 'public'
end
No big trick there, remember your public folder is not mounted, only its subfolders. So any asset files themselves in '/public' will not be accessible. The collect block is just prepending a '/' to each folder name. You can just do that as an array and a fixed list of folders, but I'm forgetful about stuff like that and want to make it work for all folders!
We could probably increase the cache times there for Heroku, using the Heroku information on caching.
As promised the full config.ru expand if you need it, and the code to run locally (remember to restart to see your changes). Go on add another post!
require 'src/hello'
require 'src/blog'
# load the config files (this will be either ours locally or Heroku
dbconfig = YAML.load(File.read('config/database.yml'))
# decide if we are running on Heroku based on the presence of their DB environment variable and use the appropriate DB
environment = ENV['DATABASE_URL'] ? 'production' : 'development'
# attach to the DB and run the create method for the Blog app
Blog::Models::Base.establish_connection dbconfig[environment]
Blog.create if Blog.respond_to? :create
# loop through each folder in 'public' and mount it at '/subfolder-name', e.g. '/css' etc.
Dir.chdir('public') do
public_dirs = (Dir.glob("*").find_all{|entry| File::directory?(entry)}).collect{|dir| '/' + dir}
use Rack::Static, :urls => public_dirs, :root => 'public'
end
# mount both apps (see note about blog on other URLs that '/'
apps = Rack::URLMap.new(
'/' => Blog,
'/hello' => Hello
)
run apps
$ rackup -p 3301 config.ru
Getting this to Heroku is pretty easy. We don't want to add the database and config dirs to git, but everything else we want to add. The git status will show you what you are adding/commiting (e.g. you should see config.ru, and the contents of the public dir in this list too)
$ git add public
$ git add src/blog.rb
$ git status
$ git commit -m "adding the blog"
$ git push heroku master
Check out your hard work on Heroku! You should see no posts here as the production DB is in use. The hello world app should be running on '/hello' and the test css file in '/css/site.css' (ok that's a boring blank, but its not a camping error!)
As an example:
test
What else did we learn?
- Blueprint CSS and Syntax highlighter play nice with zero effort
- RubyMine IDE is great, I can't quite justify the price tag though as a hobbyist (I'd pay instantly if I did this all day!)
- Want a staging server? Create a new app online on Heroku and add a new remote to git, push to that other remote to test
Comments & Corrections
Please send comments or corrections below or via Twitter to @share_and_enjoy