krack 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 +2 -0
- data/README.md +20 -0
- data/Rakefile +9 -0
- data/krack.gemspec +25 -0
- data/lib/krack/endpoint.rb +49 -0
- data/lib/krack/error.rb +14 -0
- data/lib/krack/router.rb +47 -0
- data/lib/krack/version.rb +3 -0
- data/lib/krack.rb +7 -0
- data/spec/router_spec.rb +65 -0
- data/spec/spec_helper.rb +4 -0
- metadata +124 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Krack
|
2
|
+
|
3
|
+
Simple JSON APIs on Rack. Like so:
|
4
|
+
|
5
|
+
# config.ru
|
6
|
+
|
7
|
+
require 'krack'
|
8
|
+
|
9
|
+
module Widgets
|
10
|
+
class Show
|
11
|
+
def respond
|
12
|
+
widget = Widget.find(params["id"])
|
13
|
+
{widget: widget.as_json}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
run Krack::Router.new {
|
19
|
+
get "/widgets/:id", Widgets::Show
|
20
|
+
}
|
data/Rakefile
ADDED
data/krack.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'krack/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "krack"
|
8
|
+
gem.version = Krack::VERSION
|
9
|
+
gem.authors = ["Kovalo"]
|
10
|
+
gem.email = ["team@kovalo.com"]
|
11
|
+
gem.description = "Simple JSON APIs on Rack"
|
12
|
+
gem.summary = gem.description
|
13
|
+
gem.homepage = "https://github.com/kovalo/krack"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_runtime_dependency 'rack', '~> 1.5.2'
|
21
|
+
gem.add_runtime_dependency 'multi_json'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'rake'
|
24
|
+
gem.add_development_dependency 'minitest'
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Krack
|
2
|
+
class Endpoint
|
3
|
+
attr_reader :env, :request, :response, :params
|
4
|
+
|
5
|
+
def call(env)
|
6
|
+
dup.call!(env)
|
7
|
+
end
|
8
|
+
|
9
|
+
def call!(env)
|
10
|
+
@env = env
|
11
|
+
@request = Rack::Request.new(env)
|
12
|
+
@response = Rack::Response.new([], 200, {"Content-Type" => "application/json"})
|
13
|
+
@params = env["krack.params"].merge(@request.params)
|
14
|
+
|
15
|
+
on_call
|
16
|
+
data = begin
|
17
|
+
respond_or_halt
|
18
|
+
rescue
|
19
|
+
on_error($!)
|
20
|
+
end
|
21
|
+
|
22
|
+
@response.write(output(data))
|
23
|
+
@response.finish
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_or_halt
|
27
|
+
halt = catch(:halt) { return respond }
|
28
|
+
on_halt(halt)
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_call; end
|
32
|
+
|
33
|
+
def on_error(error)
|
34
|
+
Krack::Error.new(500).respond
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_halt(halt)
|
38
|
+
Krack::Error.new(halt).respond
|
39
|
+
end
|
40
|
+
|
41
|
+
def output(data)
|
42
|
+
MultiJson.dump(data, :time_format => :ruby)
|
43
|
+
end
|
44
|
+
|
45
|
+
def respond
|
46
|
+
raise "#respond method is not defined in #{self.class}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/krack/error.rb
ADDED
data/lib/krack/router.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Krack
|
2
|
+
class Router
|
3
|
+
attr_reader :routes
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
@routes = []
|
7
|
+
instance_eval(&block) if block
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@routes.each do |verb, route, app|
|
12
|
+
next unless verb == env["REQUEST_METHOD"]
|
13
|
+
next unless match = env["PATH_INFO"].match(route)
|
14
|
+
|
15
|
+
env["krack.params"] = Hash[match.names.zip(match.captures)]
|
16
|
+
return app.dup.call(env)
|
17
|
+
end
|
18
|
+
not_found
|
19
|
+
end
|
20
|
+
|
21
|
+
def not_found
|
22
|
+
[404, {"Content-Type" => "text/plain"}, ["Not found"]]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Defines methods for each HTTP verb. These methods just call #map
|
26
|
+
# with the corresponding verb argument.
|
27
|
+
%w[get post put delete patch].each do |verb|
|
28
|
+
define_method(verb) { |route, to| map(verb.upcase, route, to) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def map(verb, route, to)
|
32
|
+
# Converts route params to regex named groups, like so:
|
33
|
+
# "/deals/:id" -> "/deals/(?<id>\w+)"
|
34
|
+
route.gsub!(/:\w+/) { |param| "(?<#{param[1..-1]}>\\w+)" }
|
35
|
+
|
36
|
+
# Allow optional trailing slash, add start/end tokens
|
37
|
+
route = "\\A#{route}\\/?\\z"
|
38
|
+
|
39
|
+
# App can be either a Rack class or an instance of such, e.g.
|
40
|
+
# Endpoints::Deals::Near or lambda { |env| ... }
|
41
|
+
# What goes into @routes is something that responds to #call
|
42
|
+
app = to.respond_to?(:call) ? to : to.new
|
43
|
+
|
44
|
+
@routes << [verb, Regexp.new(route), app]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/krack.rb
ADDED
data/spec/router_spec.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe Krack::Router do
|
4
|
+
before do
|
5
|
+
@router = Krack::Router.new {
|
6
|
+
get "/deals", lambda { |env| 1 }
|
7
|
+
get "/deals/:id", lambda { |env| 2 }
|
8
|
+
get "/deals/foo/bar", lambda { |env| 3 }
|
9
|
+
post "/deals/:one/:two", lambda { |env| 4 }
|
10
|
+
get "/users/best", lambda { |env| 5 }
|
11
|
+
get "/users/:id", lambda { |env| 6 }
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should match a simple route" do
|
16
|
+
env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/deals"}
|
17
|
+
@router.call(env).must_equal 1
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow trailing slash" do
|
21
|
+
env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/deals/"}
|
22
|
+
@router.call(env).must_equal 1
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should match a longer simple route" do
|
26
|
+
env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/deals/foo/bar"}
|
27
|
+
@router.call(env).must_equal 3
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not match when request method is wrong" do
|
31
|
+
env = {"REQUEST_METHOD" => "POST", "PATH_INFO" => "/deals"}
|
32
|
+
@router.call(env).first.must_equal 404
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should not allow a partial match" do
|
36
|
+
env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/foo/deals"}
|
37
|
+
@router.call(env).first.must_equal 404
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should match a route with param" do
|
41
|
+
env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/deals/42"}
|
42
|
+
@router.call(env).must_equal 2
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should populate env krack.params" do
|
46
|
+
env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/deals/42"}
|
47
|
+
@router.call(env)
|
48
|
+
env["krack.params"].must_equal({"id" => "42"})
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should match a route with multiple params" do
|
52
|
+
env = {"REQUEST_METHOD" => "POST", "PATH_INFO" => "/deals/42/99"}
|
53
|
+
@router.call(env).must_equal 4
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should choose the first defined route when multiple routes match" do
|
57
|
+
env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/users/best"}
|
58
|
+
@router.call(env).must_equal 5
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should allow non-integer params" do
|
62
|
+
env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/users/worst"}
|
63
|
+
@router.call(env).must_equal 6
|
64
|
+
end
|
65
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: krack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kovalo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.5.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.5.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: multi_json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: minitest
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Simple JSON APIs on Rack
|
79
|
+
email:
|
80
|
+
- team@kovalo.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- Gemfile
|
87
|
+
- README.md
|
88
|
+
- Rakefile
|
89
|
+
- krack.gemspec
|
90
|
+
- lib/krack.rb
|
91
|
+
- lib/krack/endpoint.rb
|
92
|
+
- lib/krack/error.rb
|
93
|
+
- lib/krack/router.rb
|
94
|
+
- lib/krack/version.rb
|
95
|
+
- spec/router_spec.rb
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
homepage: https://github.com/kovalo/krack
|
98
|
+
licenses: []
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 1.8.23
|
118
|
+
signing_key:
|
119
|
+
specification_version: 3
|
120
|
+
summary: Simple JSON APIs on Rack
|
121
|
+
test_files:
|
122
|
+
- spec/router_spec.rb
|
123
|
+
- spec/spec_helper.rb
|
124
|
+
has_rdoc:
|