Skip to content

Commit

Permalink
Init projects.
Browse files Browse the repository at this point in the history
  • Loading branch information
steventen committed Feb 8, 2014
0 parents commit d39de51
Show file tree
Hide file tree
Showing 10 changed files with 471 additions and 0 deletions.
Binary file added .DS_Store
Binary file not shown.
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# grape-vs-martini

API server example to compare Ruby's [Grape](https://github.com/intridea/grape) Web Framwork to [Martini](https://github.com/codegangsta/martini) Web Framwork in Go

And would respond to the following routes:

GET /projects(.json)
GET /project/:id(.json)

Response format would be like:

{"status": "Success", "data": [...]}
{"status": "Fail", "error_message": "Bad api key"}

An example `curl` command would be like:

curl "http://127.0.0.1:8080/projects?key=61c2339c1bc92bc48120b55513cd568b"

For Grape, port `9292` is used, and for Martini, port `8080` is used.

## How to run the server

### Grape Example

Under `grape-example` folder. The API is mounted directly on Rack, using [puma](https://github.com/puma/puma)

1. Modify database settings in `config.ru`

2. Run `bundle`

3. Run `rackup`

### Martini Example

Under `martini-example` folder

1. Modify database settings in `server.go` file, change variables in `sql.Open()`

2. Run `go run server.go entry.go`

## How to create sample data

Example uses MySQL as default database.

Inside mysql, run:

mysql> create database grape_vs_martini_api;

Dump sample data into database:

mysql -u root -p grape_vs_martini_api < grape_vs_martini_api.sql

Sample data includes 10 companies, each with 50 projects, so there are 500 projects in total.

Sample companies' api keys:

61c2339c1bc92bc48120b55513cd568b
bb27bc6b7330aaac63ac809df83311b8
e35402fa00728faf372abaf71623b7f4
bca4bf9d131ab311760c5b790c8568a5
49886a1c018496abb9ba4bf592c08d36
850eedd94a4d9a01962628faac7ace91
d036682c6ac91a41ac9eac064c02a43b
77a767c39adc71fb241285309ff37ee5
75e82d812630bf6d9371339f3e634801
b5125eeabadcd5b3e55a76694d2b62b8
9 changes: 9 additions & 0 deletions grape-example/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
source 'http://rubygems.org'

gem 'rack', '~> 1.4.5'
gem 'json', '~> 1.8.1'
gem 'grape', github: 'intridea/grape'
gem 'grape-entity'
gem 'mysql2'
gem 'activerecord', '~> 3.2.15'
gem 'puma'
73 changes: 73 additions & 0 deletions grape-example/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
GIT
remote: git://github.com/intridea/grape.git
revision: bc0ad13ee2811a5d67a4c09c7c45dcc833a18429
specs:
grape (0.7.0)
activesupport
builder
hashie (>= 1.2.0)
multi_json (>= 1.3.2)
multi_xml (>= 0.5.2)
rack (>= 1.3.0)
rack-accept
rack-mount
virtus (>= 1.0.0)

GEM
remote: http://rubygems.org/
specs:
activemodel (3.2.16)
activesupport (= 3.2.16)
builder (~> 3.0.0)
activerecord (3.2.16)
activemodel (= 3.2.16)
activesupport (= 3.2.16)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.16)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
arel (3.0.3)
axiom-types (0.0.5)
descendants_tracker (~> 0.0.1)
ice_nine (~> 0.9)
builder (3.0.4)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
descendants_tracker (0.0.3)
equalizer (0.0.9)
grape-entity (0.4.0)
activesupport
multi_json (>= 1.3.2)
hashie (2.0.5)
i18n (0.6.9)
ice_nine (0.11.0)
json (1.8.1)
multi_json (1.8.4)
multi_xml (0.5.5)
mysql2 (0.3.15)
puma (2.7.1)
rack (>= 1.1, < 2.0)
rack (1.4.5)
rack-accept (0.4.5)
rack (>= 0.4)
rack-mount (0.8.3)
rack (>= 1.0.0)
tzinfo (0.3.38)
virtus (1.0.1)
axiom-types (~> 0.0.5)
coercible (~> 1.0)
descendants_tracker (~> 0.0.1)
equalizer (~> 0.0.7)

PLATFORMS
ruby

DEPENDENCIES
activerecord (~> 3.2.15)
grape!
grape-entity
json (~> 1.8.1)
mysql2
puma
rack (~> 1.4.5)
53 changes: 53 additions & 0 deletions grape-example/api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require 'grape'
module MySite
class Project < ActiveRecord::Base
belongs_to :company
end

class Company < ActiveRecord::Base
has_many :projects
end

class API < Grape::API
format :json
default_format :json
# prefix 'api'
cascade false
default_error_formatter :json

helpers do
def current_company
key = params[:key]
@current_company ||= Company.where(:api => key).first
end

def authenticate!
error!({ "status" => "Fail", "error_message" => "Bad Key" }, 401) unless current_company
end
end

rescue_from :all do |e|
Rack::Response.new({ "status" => "Fail", "error_message" => e.message }.to_json, 405)
end

before do
authenticate!
end

get "projects" do
projects = current_company.projects
present :data, projects, :with => APIEntities::Project
present :status, "Success"
end

get "projects/:id" do
project = current_company.projects.where(id: params[:id]).first
if project
{"data" => {"id" => project.id, "name" => project.name}, "status" => "Success"}
else
# error!({ "status" => "Fail", "error_message" => "Failed to save project" }, 404)
{ "status" => "Fail", "error_message" => "Failed to save project" }
end
end
end
end
13 changes: 13 additions & 0 deletions grape-example/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'active_record'
Dir[File.dirname(__FILE__) + '/*.rb'].each {|f| require f}

ActiveRecord::Base.establish_connection(
:adapter => 'mysql2',
:database => 'grape_vs_martini_api',
:host => "127.0.0.1",
:pool => 25,
:username => "root",
:password => "abc123"
)
#\ -s puma
run MySite::API
10 changes: 10 additions & 0 deletions grape-example/entities.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'grape-entity'

module MySite
module APIEntities
class Project < Grape::Entity
expose :id
expose :name
end
end
end
105 changes: 105 additions & 0 deletions grape_vs_martini_api.sql

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions martini-example/entry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package main

import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)

type Project struct {
Id int `json:"id"`
Name string `json:"name"`
}

type Company struct {
Id int `json:"id"`
Api string `json:"api_key"`
}

func GetCompany(db *sql.DB, key string) (Company, int) {
var company_id int
var api_key string
err := db.QueryRow("select id, api from companies where api = ? limit 1", key).Scan(&company_id, &api_key)
switch {
case err == sql.ErrNoRows:
return Company{}, 0
case err != nil:
fmt.Println(err)
return Company{}, -1
default:
return Company{company_id, api_key}, company_id
}

}

func GetProject(db *sql.DB, company_id int, project_id int) (Project, int) {
var (
id int
name string
)
err := db.QueryRow("select id, name from projects where id = ? and company_id = ? limit 1", project_id, company_id).Scan(&id, &name)
switch {
case err == sql.ErrNoRows:
return Project{}, 0
case err != nil:
fmt.Println(err)
return Project{}, -1
default:
return Project{id, name}, id
}

}

func GetProjects(db *sql.DB, companyId int) []Project {
projects, err := db.Query("select id, name from projects where company_id = ?", companyId)
if err != nil {
fmt.Println(err)
}

var (
id int
name string
)

p := make([]Project, 0)
defer projects.Close()
for projects.Next() {
err := projects.Scan(&id, &name)
if err != nil {
fmt.Println(err)
} else {
p = append(p, Project{id, name})
}
}
return p
}
67 changes: 67 additions & 0 deletions martini-example/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"database/sql"
"fmt"
"github.com/codegangsta/martini"
"github.com/codegangsta/martini-contrib/render"
_ "github.com/go-sql-driver/mysql"
"net/http"
"strconv"
)

var db *sql.DB

func main() {
db, err := sql.Open("mysql", "root:abc123@tcp(127.0.0.1:3306)/grape_vs_martini_api")

if err != nil {
fmt.Println(err)
}
defer db.Close()

m := martini.Classic()
m.Use(render.Renderer())

m.Use(func(res http.ResponseWriter, req *http.Request, r render.Render) {
api_key := ""
api_key = req.URL.Query().Get("key")
if api_key == "" {
r.JSON(404, map[string]interface{}{"status": "Fail", "error_message": "Need api key"})
} else {
// r.JSON(200, map[string]interface{}{"key": api_key})
current_company, company_id := GetCompany(db, api_key)
if company_id < 0 {
r.JSON(404, map[string]interface{}{"status": "Fail", "error_message": "Bad api key"})
} else {
m.Map(current_company)
}
}
})

m.Get("/", func() string {
return "Hello world!"
})

m.Get("/projects", func(current_company Company, r render.Render) {
projects := GetProjects(db, current_company.Id)
r.JSON(200, map[string]interface{}{"status": "Success", "data": projects})
})

m.Get("/projects/:id", func(current_company Company, params martini.Params, r render.Render) {
paramId, err := strconv.Atoi(params["id"])
if err != nil {
r.JSON(404, map[string]interface{}{"status": "Fail", "error_message": err.Error()})
return
}
project, id := GetProject(db, current_company.Id, paramId)
if id > 0 {
r.JSON(200, map[string]interface{}{"status": "Success", "data": project})
} else {
r.JSON(404, map[string]interface{}{"status": "Fail", "error_message": "Project not found"})
}
})

// m.Run()
http.ListenAndServe(":8080", m)
}

0 comments on commit d39de51

Please sign in to comment.