Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Josh Hull
committed
Jan 16, 2011
0 parents
commit 987ca2f
Showing
9 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.bundle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
source :rubygems | ||
|
||
gem 'callsite' | ||
gem 'method-args' | ||
gem 'thin' | ||
gem 'http_router' | ||
gem 'rack' | ||
gem 'rake' | ||
gem 'em-http-request' | ||
gem 'minitest', '~> 2.0.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
GEM | ||
remote: http://rubygems.org/ | ||
specs: | ||
addressable (2.2.2) | ||
callsite (0.0.4) | ||
daemons (1.1.0) | ||
em-http-request (0.2.12) | ||
addressable (>= 2.0.0) | ||
eventmachine (>= 0.12.9) | ||
eventmachine (0.12.10) | ||
http_router (0.5.0) | ||
rack (>= 1.0.0) | ||
url_mount (~> 0.2.1) | ||
method-args (0.1.0) | ||
ruby2ruby (~> 1.2.4) | ||
ruby_parser (~> 2.0) | ||
sexp_processor (~> 3.0.4) | ||
minitest (2.0.2) | ||
rack (1.2.1) | ||
rake (0.8.7) | ||
ruby2ruby (1.2.5) | ||
ruby_parser (~> 2.0) | ||
sexp_processor (~> 3.0) | ||
ruby_parser (2.0.5) | ||
sexp_processor (~> 3.0) | ||
sexp_processor (3.0.5) | ||
thin (1.2.7) | ||
daemons (>= 1.0.9) | ||
eventmachine (>= 0.12.6) | ||
rack (>= 1.0.0) | ||
url_mount (0.2.1) | ||
rack | ||
|
||
PLATFORMS | ||
ruby | ||
|
||
DEPENDENCIES | ||
callsite | ||
em-http-request | ||
http_router | ||
method-args | ||
minitest (~> 2.0.0) | ||
rack | ||
rake | ||
thin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Apiary | ||
|
||
Stand up simple APIs for consumption. | ||
|
||
## Usage | ||
|
||
Apiary let's you use any existing class and turn it into an API. For instance, say you have a class like this. | ||
|
||
class Temperature | ||
def c2f(val) | ||
Float(val) * 9 / 5 + 32 | ||
end | ||
end | ||
|
||
You can convert this to an API by annotating this class with three lines. | ||
|
||
class Temperature | ||
include Apiary | ||
|
||
version '1.0' | ||
|
||
get | ||
def c2f(val) | ||
Float(val) * 9 / 5 + 32 | ||
end | ||
end | ||
|
||
Now, your API is complete! You can run this with `Temperature.run`. This will create a server on port 3000. You can hit it with | ||
|
||
curl http://localhost:3000/1.0/c2f/23.45 | ||
|
||
And you'll get back | ||
|
||
74.21 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
require 'rubygems' | ||
require 'bundler' | ||
|
||
desc "Run tests" | ||
task :test do | ||
$: << 'lib' | ||
require 'apiary' | ||
require 'test/helper' | ||
Dir['test/**/test_*.rb'].each { |test| require test } | ||
end | ||
|
||
require 'rake/rdoctask' | ||
desc "Generate documentation" | ||
Rake::RDocTask.new do |rd| | ||
rd.main = "README.rdoc" | ||
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb") | ||
rd.rdoc_dir = 'rdoc' | ||
end | ||
|
||
#Bundler::GemHelper.install_tasks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
require 'method_args' | ||
require 'callsite' | ||
require 'http_router' | ||
require 'thin' | ||
require 'rack' | ||
|
||
module Apiary | ||
ApiMethod = Struct.new(:method, :http_method, :path) | ||
|
||
module ClassMethods | ||
|
||
def get(path = nil) | ||
__set_routing(:get, path) | ||
end | ||
|
||
def post(path = nil) | ||
__set_routing(:get, path) | ||
end | ||
|
||
def version(number) | ||
@version = number | ||
end | ||
|
||
def __set_routing(method, path) | ||
@method, @path = method, path | ||
end | ||
|
||
def method_added(m) | ||
MethodArgs.register(Callsite.parse(caller.first).filename) | ||
@cmds ||= [] | ||
@cmds << ApiMethod.new(m, @http_method, @path) | ||
end | ||
|
||
def default_path(m) | ||
instance_method(m).args.inject(m.to_s) { |path, arg| | ||
path << case arg.type | ||
when :required | ||
"/:#{arg.name}" | ||
when :optional | ||
"/(:#{arg.name})" | ||
when :splat | ||
"/*#{arg.name}" | ||
end | ||
} | ||
end | ||
|
||
def run(port = 3000) | ||
raise "No version specified" unless @version | ||
router = HttpRouter.new | ||
@cmds.each do |cmd| | ||
path = "#{@version}/#{cmd.path || default_path(cmd.method)}".squeeze('/') | ||
route = router.add(path) | ||
route.send(cmd.http_method) if cmd.http_method | ||
route.to { |env| | ||
Rack::Response.new(new.send(cmd.method, *env['router.response'].param_values).to_s).finish | ||
} | ||
end | ||
Thin::Server.new('0.0.0.0', port) { | ||
run router | ||
}.start | ||
end | ||
end | ||
|
||
def self.included(cls) | ||
cls.instance_eval "class << self; include ClassMethods; end" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
class Basic | ||
include Apiary | ||
|
||
version '1.0' | ||
|
||
get | ||
def ping | ||
'ping' | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
require 'minitest/autorun' | ||
require 'em-http' | ||
require 'test/fixtures/basic' | ||
|
||
class MiniTest::Unit::TestCase | ||
def run_with(cls, &blk) | ||
EM.run do | ||
cls.run | ||
EM.add_timer(0.5) { EM.stop } | ||
EM.next_tick(&blk) | ||
end | ||
end | ||
|
||
def request(path, method = :get, opts = {}, &blk) | ||
raise "you need to be running inside a request" unless EM.reactor_running? | ||
http = EventMachine::HttpRequest.new("http://127.0.0.1:3000#{path}").__send__(method, opts) | ||
http.callback { EM.next_tick { blk.call(http) } } | ||
end | ||
|
||
def done | ||
EM.stop | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
class TestBasic < MiniTest::Unit::TestCase | ||
def test_simple | ||
run_with(Basic) do | ||
request('/1.0/ping') do |http| | ||
assert_equal 200, http.response_header.status | ||
assert_equal 'ping', http.response | ||
done | ||
end | ||
end | ||
end | ||
|
||
def test_not_found | ||
run_with(Basic) do | ||
request('/1.0/something_else') do |http| | ||
assert_equal 404, http.response_header.status | ||
done | ||
end | ||
end | ||
end | ||
end |