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.
@@ -0,0 +1,2 @@
1
+ .bundle
2
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ gem 'callsite'
4
+ gem 'method-args'
5
+ gem 'thin'
6
+ gem 'http_router'
7
+ gem 'rack'
8
+ gem 'rake'
9
+ gem 'em-http-request'
10
+ gem 'minitest', '~> 2.0.0'
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Apiary
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,19 @@
1
+ class Basic
2
+ include Apiary
3
+
4
+ version '1.0'
5
+
6
+ def initialize(var = nil)
7
+ @var = var
8
+ end
9
+
10
+ get
11
+ def ping
12
+ 'ping'
13
+ end
14
+
15
+ get
16
+ def var
17
+ @var
18
+ end
19
+ end
@@ -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
@@ -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