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 ADDED
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
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
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = FileList["spec/**/*_spec.rb"]
6
+ end
7
+
8
+ task :spec => :test
9
+
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
@@ -0,0 +1,14 @@
1
+ module Krack
2
+ class Error < StandardError
3
+
4
+ attr_accessor :status
5
+
6
+ def initialize(status=500)
7
+ @status = status
8
+ end
9
+
10
+ def respond
11
+ {error: {status: status, message: Rack::Utils::HTTP_STATUS_CODES[status]}}
12
+ end
13
+ end
14
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Krack
2
+ VERSION = "0.0.1"
3
+ end
data/lib/krack.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rack'
2
+ require 'multi_json'
3
+
4
+ require 'krack/version'
5
+ require 'krack/error'
6
+ require 'krack/router'
7
+ require 'krack/endpoint'
@@ -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
@@ -0,0 +1,4 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/spec'
3
+
4
+ require_relative '../lib/krack'
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: