apiary 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +45 -0
- data/README.markdown +34 -0
- data/Rakefile +20 -0
- data/apiary.gemspec +21 -0
- data/lib/apiary.rb +74 -0
- data/lib/apiary/version.rb +3 -0
- data/test/fixtures/basic.rb +19 -0
- data/test/helper.rb +23 -0
- data/test/test_basic.rb +30 -0
- metadata +80 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.2.2)
|
5
|
+
callsite (0.0.4)
|
6
|
+
daemons (1.1.0)
|
7
|
+
em-http-request (0.2.12)
|
8
|
+
addressable (>= 2.0.0)
|
9
|
+
eventmachine (>= 0.12.9)
|
10
|
+
eventmachine (0.12.10)
|
11
|
+
http_router (0.5.0)
|
12
|
+
rack (>= 1.0.0)
|
13
|
+
url_mount (~> 0.2.1)
|
14
|
+
method-args (0.1.0)
|
15
|
+
ruby2ruby (~> 1.2.4)
|
16
|
+
ruby_parser (~> 2.0)
|
17
|
+
sexp_processor (~> 3.0.4)
|
18
|
+
minitest (2.0.2)
|
19
|
+
rack (1.2.1)
|
20
|
+
rake (0.8.7)
|
21
|
+
ruby2ruby (1.2.5)
|
22
|
+
ruby_parser (~> 2.0)
|
23
|
+
sexp_processor (~> 3.0)
|
24
|
+
ruby_parser (2.0.5)
|
25
|
+
sexp_processor (~> 3.0)
|
26
|
+
sexp_processor (3.0.5)
|
27
|
+
thin (1.2.7)
|
28
|
+
daemons (>= 1.0.9)
|
29
|
+
eventmachine (>= 0.12.6)
|
30
|
+
rack (>= 1.0.0)
|
31
|
+
url_mount (0.2.1)
|
32
|
+
rack
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
callsite
|
39
|
+
em-http-request
|
40
|
+
http_router
|
41
|
+
method-args
|
42
|
+
minitest (~> 2.0.0)
|
43
|
+
rack
|
44
|
+
rake
|
45
|
+
thin
|
data/README.markdown
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Apiary
|
2
|
+
|
3
|
+
Stand up simple APIs for consumption.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
Apiary let's you use any existing class and turn it into an API. For instance, say you have a class like this.
|
8
|
+
|
9
|
+
class Temperature
|
10
|
+
def c2f(val)
|
11
|
+
Float(val) * 9 / 5 + 32
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
You can convert this to an API by annotating this class with three lines.
|
16
|
+
|
17
|
+
class Temperature
|
18
|
+
include Apiary # Include Apiary as a module in your class
|
19
|
+
|
20
|
+
version '1.0' # Specifies a version prefix for your api
|
21
|
+
|
22
|
+
get # Marks this method as accessible from GET
|
23
|
+
def c2f(val) # This is now available at /1.0/c2f/:val
|
24
|
+
Float(val) * 9 / 5 + 32
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
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
|
29
|
+
|
30
|
+
curl http://localhost:3000/1.0/c2f/23.45
|
31
|
+
|
32
|
+
And you'll get back
|
33
|
+
|
34
|
+
74.21
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
desc "Run tests"
|
5
|
+
task :test do
|
6
|
+
$: << 'lib'
|
7
|
+
require 'apiary'
|
8
|
+
require 'test/helper'
|
9
|
+
Dir['test/**/test_*.rb'].each { |test| require test }
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'rake/rdoctask'
|
13
|
+
desc "Generate documentation"
|
14
|
+
Rake::RDocTask.new do |rd|
|
15
|
+
rd.main = "README.markdown"
|
16
|
+
rd.rdoc_files.include("README.markdown", "lib/**/*.rb")
|
17
|
+
rd.rdoc_dir = 'rdoc'
|
18
|
+
end
|
19
|
+
|
20
|
+
Bundler::GemHelper.install_tasks
|
data/apiary.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "apiary/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "apiary"
|
7
|
+
s.version = Apiary::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Joshua Hull"]
|
10
|
+
s.email = ["joshbuddy@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/joshbuddy/apiary"
|
12
|
+
s.summary = %q{Convert your existing class into an EM-based API}
|
13
|
+
s.description = %q{Convert your existing class into an EM-based API.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "apiary"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
data/lib/apiary.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'method_args'
|
2
|
+
require 'callsite'
|
3
|
+
require 'http_router'
|
4
|
+
require 'thin'
|
5
|
+
require 'rack'
|
6
|
+
require 'apiary/version'
|
7
|
+
|
8
|
+
module Apiary
|
9
|
+
ApiMethod = Struct.new(:method, :http_method, :path)
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def get(path = nil)
|
14
|
+
__set_routing(:get, path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def post(path = nil)
|
18
|
+
__set_routing(:get, path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def version(number)
|
22
|
+
@version = number
|
23
|
+
end
|
24
|
+
|
25
|
+
def __set_routing(method, path)
|
26
|
+
@http_method, @path = method, path
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_added(m)
|
30
|
+
if @http_method
|
31
|
+
MethodArgs.register(Callsite.parse(caller.first).filename)
|
32
|
+
@cmds ||= []
|
33
|
+
@cmds << ApiMethod.new(m, @http_method, @path)
|
34
|
+
@http_method = nil
|
35
|
+
@path = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def default_path(m)
|
40
|
+
instance_method(m).args.inject(m.to_s) do |path, arg|
|
41
|
+
path << case arg.type
|
42
|
+
when :required then "/:#{arg.name}"
|
43
|
+
when :optional then "/(:#{arg.name})"
|
44
|
+
when :splat then "/*#{arg.name}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def __as_app(&blk)
|
50
|
+
router = HttpRouter.new
|
51
|
+
@cmds.each do |cmd|
|
52
|
+
path = "#{@version}/#{cmd.path || default_path(cmd.method)}".squeeze('/')
|
53
|
+
route = router.add(path)
|
54
|
+
route.send(cmd.http_method) if cmd.http_method
|
55
|
+
route.to { |env|
|
56
|
+
Rack::Response.new((blk ? blk.call : new).send(cmd.method, *env['router.response'].param_values).to_s).finish
|
57
|
+
}
|
58
|
+
end
|
59
|
+
router
|
60
|
+
end
|
61
|
+
|
62
|
+
def run(port = 3000, &blk)
|
63
|
+
api = self
|
64
|
+
raise "No version specified" unless @version
|
65
|
+
Thin::Server.new('0.0.0.0', port) {
|
66
|
+
run api.__as_app(&blk)
|
67
|
+
}.start
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.included(cls)
|
72
|
+
cls.instance_eval "class << self; include ClassMethods; end"
|
73
|
+
end
|
74
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'em-http'
|
3
|
+
require 'test/fixtures/basic'
|
4
|
+
|
5
|
+
class MiniTest::Unit::TestCase
|
6
|
+
def run_with(cls, optional_blk = nil, &blk)
|
7
|
+
EM.run do
|
8
|
+
optional_blk ? cls.run(&optional_blk) : cls.run
|
9
|
+
EM.add_timer(0.5) { assert false, "Stopped EM server because you didn't call done, or reach your done"; EM.stop }
|
10
|
+
EM.next_tick(&blk)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def request(path, method = :get, opts = {}, &blk)
|
15
|
+
raise "you need to be running inside a request" unless EM.reactor_running?
|
16
|
+
http = EventMachine::HttpRequest.new("http://127.0.0.1:3000#{path}").__send__(method, opts)
|
17
|
+
http.callback { EM.next_tick { blk.call(http) } }
|
18
|
+
end
|
19
|
+
|
20
|
+
def done
|
21
|
+
EM.stop
|
22
|
+
end
|
23
|
+
end
|
data/test/test_basic.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
class TestBasic < MiniTest::Unit::TestCase
|
2
|
+
def test_simple
|
3
|
+
run_with(Basic) do
|
4
|
+
request('/1.0/ping') do |http|
|
5
|
+
assert_equal 200, http.response_header.status
|
6
|
+
assert_equal 'ping', http.response
|
7
|
+
done
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_not_found
|
13
|
+
run_with(Basic) do
|
14
|
+
request('/1.0/something_else') do |http|
|
15
|
+
assert_equal 404, http.response_header.status
|
16
|
+
done
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_custom_initializer
|
22
|
+
run_with(Basic, proc{ Basic.new('hi there')}) do
|
23
|
+
request('/1.0/var') do |http|
|
24
|
+
assert_equal 200, http.response_header.status
|
25
|
+
assert_equal 'hi there', http.response
|
26
|
+
done
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apiary
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Joshua Hull
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-16 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Convert your existing class into an EM-based API.
|
23
|
+
email:
|
24
|
+
- joshbuddy@gmail.com
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions: []
|
28
|
+
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- Gemfile.lock
|
35
|
+
- README.markdown
|
36
|
+
- Rakefile
|
37
|
+
- apiary.gemspec
|
38
|
+
- lib/apiary.rb
|
39
|
+
- lib/apiary/version.rb
|
40
|
+
- test/fixtures/basic.rb
|
41
|
+
- test/helper.rb
|
42
|
+
- test/test_basic.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: https://github.com/joshbuddy/apiary
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
hash: 3
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project: apiary
|
73
|
+
rubygems_version: 1.3.7
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Convert your existing class into an EM-based API
|
77
|
+
test_files:
|
78
|
+
- test/fixtures/basic.rb
|
79
|
+
- test/helper.rb
|
80
|
+
- test/test_basic.rb
|