Skip to content

Commit

Permalink
First version of working example
Browse files Browse the repository at this point in the history
  • Loading branch information
djones committed May 11, 2012
1 parent 12e77e3 commit b14d1a0
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 3 deletions.
10 changes: 10 additions & 0 deletions Gemfile
@@ -0,0 +1,10 @@
source 'https://rubygems.org'

gem 'pg'
gem 'em-postgresql-adapter', :git => 'git://github.com/leftbee/em-postgresql-adapter.git'
gem 'rack-fiber_pool', :require => 'rack/fiber_pool'
gem 'em-synchrony', :git => 'git://github.com/igrigorik/em-synchrony.git',
:require => ['em-synchrony', 'em-synchrony/activerecord']

gem 'grape'
gem 'goliath'
85 changes: 85 additions & 0 deletions Gemfile.lock
@@ -0,0 +1,85 @@
GIT
remote: git://github.com/igrigorik/em-synchrony.git
revision: de0efbb3c7d19df352697fa20960a5f84d082a7e
specs:
em-synchrony (1.0.1)
eventmachine (>= 1.0.0.beta.1)

GIT
remote: git://github.com/leftbee/em-postgresql-adapter.git
revision: c9b31df6d3177ac22f94b08d6b722009ac089215
specs:
em-postgresql-adapter (0.3)
activerecord (>= 3.1.0)
eventmachine
pg (>= 0.8.0)

GEM
remote: https://rubygems.org/
specs:
activemodel (3.2.3)
activesupport (= 3.2.3)
builder (~> 3.0.0)
activerecord (3.2.3)
activemodel (= 3.2.3)
activesupport (= 3.2.3)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.3)
i18n (~> 0.6)
multi_json (~> 1.0)
arel (3.0.2)
async-rack (0.5.1)
rack (~> 1.1)
builder (3.0.0)
eventmachine (1.0.0.beta.4)
goliath (0.9.4)
async-rack
em-synchrony (>= 1.0.0)
eventmachine (>= 1.0.0.beta.3)
http_parser.rb
http_router (~> 0.9.0)
log4r
multi_json
rack (>= 1.2.2)
rack-contrib
rack-respond_to
grape (0.2.0)
hashie (~> 1.2)
multi_json
multi_xml
rack
rack-mount
hashie (1.2.0)
http_parser.rb (0.5.3)
http_router (0.9.7)
rack (>= 1.0.0)
url_mount (~> 0.2.1)
i18n (0.6.0)
log4r (1.1.10)
multi_json (1.3.4)
multi_xml (0.4.4)
pg (0.13.2)
rack (1.4.1)
rack-accept-media-types (0.9)
rack-contrib (1.1.0)
rack (>= 0.9.1)
rack-fiber_pool (0.9.2)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-respond_to (0.9.8)
rack-accept-media-types (>= 0.6)
tzinfo (0.3.33)
url_mount (0.2.1)
rack

PLATFORMS
ruby

DEPENDENCIES
em-postgresql-adapter!
em-synchrony!
goliath
grape
pg
rack-fiber_pool
1 change: 1 addition & 0 deletions Procfile
@@ -0,0 +1 @@
web: bundle exec ruby server.rb -sv -e prod -p $PORT
98 changes: 95 additions & 3 deletions README.md
@@ -1,4 +1,96 @@
grape-goliath-example
=====================
# [Grape](https://github.com/intridea/grape) + [Goliath](https://github.com/postrank-labs/goliath) Example

A non-blocking example application of Grape and Goliath being used together with a Postgres database
## What is this?

* Grape is micro-framework for creating REST-like APIs in Ruby.
* Goliath is a non-blocking Ruby web server

Together you can create a highly scalable API and use the nice features of Grape to specify how your REST API will work.

This example features:

* ActiveRecord models
* Postgres
* Non-blocking adapters and dependancies
* Deployment on Heroku

## Getting Started

Create and migrate your database with:

$> rake db:setup

Start the server and you're done!

$> ruby server.rb -vs

Next let's list all the posts in the database:

$> curl http://localhost:9000/v1/posts.json
=> []

There are none yet.

## Adding a Post

We do a HTTP post to create a new post.

$> curl -X POST -d '{"post":{"title":"David Jones","body":"this is my message"}}' http://localhost:9000/v1/posts/create

Now let's list all the posts again.

$> curl http://localhost:9000/v1/posts.json
=> [{"body":"this is my message","created_at":"2012-05-11T13:35:03-07:00","id":1,"title":"David Jones","updated_at":"2012-05-11T13:35:03-07:00"}]

Now you see your first post has shown up.

# Next Steps

This is just a basic Grape API example. You can see the post specified in `app/api/posts_api.rb`. You could expand that API and add your own models in `app/models`.

# Deploy on Heroku

First we create a new Heroku application

$> heroku create --stack cedar YOURAPPNAME

Next we push the code to Heroku

$> git push heroku master

You'll see a URL at the end of your deploy. Use that to fill in the YOURAPPNAME in the next step.

$> curl http://YOURAPPNAME.herokuapp.com/v1/posts.json
=> []

Next you could use the "Adding a Post" example above to write your first post to the server.

# Extras

*Locally only*, you can use Rails-like database commands.

Drop your database with:

$> rake db:drop

Create your database with:

$> rake db:create

Migrate your database with:

$> rake db:migrate

Create and migrate your database with

$> rake db:setup

# Resources

* [The Grapes of Rapid](http://www.confreaks.com/videos/475-rubyconf2010-the-grapes-of-rapid) - everything you need to know about Grape
* [Building high-performance (Ruby) web services](http://www.confreaks.com/videos/653-gogaruco2011-0-60-with-goliath-building-high-performance-ruby-web-services) - everything you need to know about Goliath

# Todo

* Unify all the 'require' statements.
* Make it work with databases other than Postgres
47 changes: 47 additions & 0 deletions Rakefile
@@ -0,0 +1,47 @@
require "rubygems"
require "bundler/setup"
require 'em-synchrony/activerecord'
require 'yaml'
require 'erb'

namespace :db do
desc "loads database configuration in for other tasks to run"
task :load_config do
ActiveRecord::Base.configurations = db_conf

# drop and create need to be performed with a connection to the 'postgres' (system) database
ActiveRecord::Base.establish_connection db_conf["production"].merge('database' => 'postgres',
'schema_search_path' => 'public')
end

desc "creates and migrates your database"
task :setup => :load_config do
Rake::Task["db:create"].invoke
Rake::Task["db:migrate"].invoke
end

desc "migrate your database"
task :migrate do
ActiveRecord::Base.establish_connection db_conf["production"]

ActiveRecord::Migrator.migrate(
ActiveRecord::Migrator.migrations_paths,
ENV["VERSION"] ? ENV["VERSION"].to_i : nil
)
end

desc 'Drops the database'
task :drop => :load_config do
ActiveRecord::Base.connection.drop_database db_conf['production']['database']
end

desc 'Creates the database'
task :create => :load_config do
ActiveRecord::Base.connection.create_database db_conf['production']['database']
end

end

def db_conf
config = YAML.load(ERB.new(File.read('config/database.yml')).result)
end
20 changes: 20 additions & 0 deletions app/api/posts_api.rb
@@ -0,0 +1,20 @@
class PostsAPI < Grape::API

version 'v1', :using => :path
format :json

resource 'posts' do
get "/" do
Post.all
end

get "/:id" do
Post.find(params['id'])
end

post "/create" do
Post.create(params['post'])
end
end

end
5 changes: 5 additions & 0 deletions app/models/post.rb
@@ -0,0 +1,5 @@
class Post < ActiveRecord::Base

validates :title, :presence => true

end
22 changes: 22 additions & 0 deletions config/application.rb
@@ -0,0 +1,22 @@
require 'uri'
require 'em-synchrony/activerecord'
require 'yaml'
require 'erb'

# Sets up database configuration
db = URI.parse(ENV['DATABASE_URL'] || 'http://localhost')
if db.scheme == 'postgres' # Heroku environment
ActiveRecord::Base.establish_connection(
:adapter => db.scheme == 'postgres' ? 'postgresql' : db.scheme,
:host => db.host,
:username => db.user,
:password => db.password,
:database => db.path[1..-1],
:encoding => 'utf8'
)
else # local environment
environment = ENV['DATABASE_URL'] ? 'production' : 'development'
db = YAML.load(ERB.new(File.read('config/database.yml')).result)[environment]
ActiveRecord::Base.establish_connection(db)
end

9 changes: 9 additions & 0 deletions config/database.yml
@@ -0,0 +1,9 @@
production: &production
adapter: em_postgresql
encoding: unicode
database: goliathgrape
pool: 5
username: root
password:
development:
<<: *production
11 changes: 11 additions & 0 deletions db/migrate/1_create_posts.rb
@@ -0,0 +1,11 @@
class CreatePosts < ActiveRecord::Migration

def change
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
end

end
15 changes: 15 additions & 0 deletions server.rb
@@ -0,0 +1,15 @@
require "rubygems"
require "bundler/setup"
require 'goliath'
require 'em-synchrony/activerecord'
require 'grape'
require './app/api/posts_api'
require './app/models/post'

class Application < Goliath::API

def response(env)
::PostsAPI.call(env)
end

end

0 comments on commit b14d1a0

Please sign in to comment.