rack-putty 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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE +27 -0
- data/README.md +68 -0
- data/Rakefile +1 -0
- data/lib/rack-putty.rb +8 -0
- data/lib/rack-putty/middleware.rb +18 -0
- data/lib/rack-putty/router.rb +146 -0
- data/lib/rack-putty/router/extract_params.rb +69 -0
- data/lib/rack-putty/version.rb +5 -0
- data/rack-putty.gemspec +26 -0
- data/spec/integration/router_spec.rb +141 -0
- data/spec/spec_helper.rb +11 -0
- metadata +143 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2013 Apollic Software, LLC. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions are
|
5
|
+
met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above
|
10
|
+
copyright notice, this list of conditions and the following disclaimer
|
11
|
+
in the documentation and/or other materials provided with the
|
12
|
+
distribution.
|
13
|
+
* Neither the name of Apollic Software, LLC nor the names of its
|
14
|
+
contributors may be used to endorse or promote products derived from
|
15
|
+
this software without specific prior written permission.
|
16
|
+
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
19
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
20
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
21
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
22
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
23
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
24
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
25
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Rack::Putty
|
2
|
+
|
3
|
+
Simple web framework built on rack for mapping sinatra-like routes to middleware stacks.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rack-putty'
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
require 'rack-putty'
|
15
|
+
|
16
|
+
module YourApp
|
17
|
+
class Router
|
18
|
+
include Rack::Putty::Router
|
19
|
+
|
20
|
+
class SerializeResponse
|
21
|
+
def self.call(env)
|
22
|
+
headers = { 'Content-Type' => 'text/plain' }.merge(env.response_headers || {})
|
23
|
+
if env.response
|
24
|
+
[200, headers, [env.response]]
|
25
|
+
else
|
26
|
+
[404, headers, ['Not Found']]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class CorsHeaders
|
32
|
+
def initialize(app)
|
33
|
+
@app = app
|
34
|
+
end
|
35
|
+
|
36
|
+
def call(env)
|
37
|
+
status, headers, body = @app.call(env)
|
38
|
+
headers.merge!(
|
39
|
+
'Access-Control-Allow-Origin' => '*',
|
40
|
+
) if env['HTTP_ORIGIN']
|
41
|
+
[status, headers, body]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
stack_base SerializeResponse
|
46
|
+
middleware CorsHeaders
|
47
|
+
|
48
|
+
class HelloWorld < Rack::Putty::Middleware
|
49
|
+
def action(env)
|
50
|
+
env.response = 'Hello World'
|
51
|
+
env
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
get '/' do |builder|
|
56
|
+
builder.use HelloWorld
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
## Contributing
|
63
|
+
|
64
|
+
1. Fork it
|
65
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
66
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
67
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
68
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/rack-putty.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Rack
|
2
|
+
module Putty
|
3
|
+
|
4
|
+
class Middleware
|
5
|
+
def initialize(app, options = {})
|
6
|
+
@app, @options = app, options
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
response = action(env)
|
11
|
+
|
12
|
+
# Allow middleware to cause stack to end early
|
13
|
+
response.kind_of?(Hash) ? @app.call(env) : response
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'rack/mount'
|
2
|
+
|
3
|
+
class Rack::Mount::RouteSet
|
4
|
+
def merge_routes(routes)
|
5
|
+
routes.each { |r| merge_route(r) }
|
6
|
+
rehash
|
7
|
+
end
|
8
|
+
|
9
|
+
def merge_route(route)
|
10
|
+
@routes << route
|
11
|
+
|
12
|
+
@recognition_key_analyzer << route.conditions
|
13
|
+
|
14
|
+
@named_routes[route.name] = route if route.name
|
15
|
+
@generation_route_keys << route.generation_keys
|
16
|
+
|
17
|
+
expire!
|
18
|
+
route
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Rack
|
23
|
+
module Putty
|
24
|
+
|
25
|
+
module Router
|
26
|
+
MissingStackBaseError = Class.new(StandardError)
|
27
|
+
|
28
|
+
autoload :ExtractParams, 'rack-putty/router/extract_params'
|
29
|
+
|
30
|
+
def self.included(base)
|
31
|
+
base.extend(ClassMethods)
|
32
|
+
base.routes.rehash
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(env)
|
36
|
+
self.class.routes.call(env)
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
def stack_base(klass)
|
41
|
+
@stack_base = klass
|
42
|
+
end
|
43
|
+
|
44
|
+
def middleware(klass, *args)
|
45
|
+
@middleware ||= []
|
46
|
+
@middleware << [klass, args]
|
47
|
+
end
|
48
|
+
|
49
|
+
def mount(klass)
|
50
|
+
routes.merge_routes klass.routes.instance_variable_get("@routes")
|
51
|
+
end
|
52
|
+
|
53
|
+
def routes
|
54
|
+
@routes ||= Rack::Mount::RouteSet.new
|
55
|
+
end
|
56
|
+
|
57
|
+
#### This section heavily "inspired" by sinatra
|
58
|
+
|
59
|
+
# Defining a `GET` handler also automatically defines
|
60
|
+
# a `HEAD` handler.
|
61
|
+
def get(path, opts={}, &block)
|
62
|
+
route('GET', path, opts, &block)
|
63
|
+
route('HEAD', path, opts, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def put(path, opts={}, &bk) route 'PUT', path, opts, &bk end
|
67
|
+
def post(path, opts={}, &bk) route 'POST', path, opts, &bk end
|
68
|
+
def patch(path, opts={}, &bk) route 'PATCH', path, opts, &bk end
|
69
|
+
def delete(path, opts={}, &bk) route 'DELETE', path, opts, &bk end
|
70
|
+
def head(path, opts={}, &bk) route 'HEAD', path, opts, &bk end
|
71
|
+
def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
|
72
|
+
|
73
|
+
def match(path, opts={}, &bk)
|
74
|
+
get(path, opts, &bk)
|
75
|
+
put(path, opts, &bk)
|
76
|
+
post(path, opts, &bk)
|
77
|
+
patch(path, opts, &bk)
|
78
|
+
delete(path, opts, &bk)
|
79
|
+
options(path, opts, &bk)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def route(verb, path, options={}, &block)
|
85
|
+
path, params = compile_path(path)
|
86
|
+
|
87
|
+
return if route_exists?(verb, path)
|
88
|
+
|
89
|
+
unless @stack_base
|
90
|
+
raise MissingStackBaseError.new("You need to call `stack_base` with a base app class to be passed to Rack::Builder.new.")
|
91
|
+
end
|
92
|
+
|
93
|
+
builder = Rack::Builder.new(@stack_base)
|
94
|
+
builder.use(ExtractParams, path, params)
|
95
|
+
|
96
|
+
(@middleware || []).each do |i|
|
97
|
+
klass, args = i
|
98
|
+
builder.use(klass, *args)
|
99
|
+
end
|
100
|
+
|
101
|
+
block.call(builder)
|
102
|
+
|
103
|
+
routes.add_route(builder.to_app, :request_method => verb, :path_info => path)
|
104
|
+
routes.rehash
|
105
|
+
end
|
106
|
+
|
107
|
+
def route_exists?(verb, path)
|
108
|
+
@added_routes ||= []
|
109
|
+
return true if @added_routes.include?("#{verb}#{path}")
|
110
|
+
@added_routes << "#{verb}#{path}"
|
111
|
+
false
|
112
|
+
end
|
113
|
+
|
114
|
+
def compile_path(path)
|
115
|
+
keys = []
|
116
|
+
if path.respond_to? :to_str
|
117
|
+
ignore = ""
|
118
|
+
pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c|
|
119
|
+
ignore << escaped(c).join if c.match(/[\.@]/)
|
120
|
+
encoded(c)
|
121
|
+
end
|
122
|
+
pattern.gsub!(/((:\w+)|\*)/) do |match|
|
123
|
+
if match == "*"
|
124
|
+
keys << 'splat'
|
125
|
+
"(.*?)"
|
126
|
+
else
|
127
|
+
keys << $2[1..-1]
|
128
|
+
"([^#{ignore}/?#]+)"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
[/\A#{pattern}\z/, keys]
|
132
|
+
elsif path.respond_to?(:keys) && path.respond_to?(:match)
|
133
|
+
[path, path.keys]
|
134
|
+
elsif path.respond_to?(:names) && path.respond_to?(:match)
|
135
|
+
[path, path.names]
|
136
|
+
elsif path.respond_to? :match
|
137
|
+
[path, keys]
|
138
|
+
else
|
139
|
+
raise TypeError, path
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Rack
|
2
|
+
module Putty
|
3
|
+
|
4
|
+
module Router
|
5
|
+
|
6
|
+
class ExtractParams
|
7
|
+
attr_accessor :pattern, :keys
|
8
|
+
|
9
|
+
def initialize(app, pattern, keys)
|
10
|
+
@app, @pattern, @keys = app, pattern, keys
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
add_request(env)
|
15
|
+
extract_params(env)
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def add_request(env)
|
22
|
+
env['request'] = Rack::Request.new(env)
|
23
|
+
end
|
24
|
+
|
25
|
+
def extract_params(env)
|
26
|
+
route = env[Rack::Mount::Prefix::KEY]
|
27
|
+
route = '/' if route.empty?
|
28
|
+
return unless match = pattern.match(route)
|
29
|
+
values = match.captures.to_a.map { |v| URI.decode_www_form_component(v) if v }
|
30
|
+
|
31
|
+
params = env['request'].params.dup
|
32
|
+
|
33
|
+
if env['CONTENT_TYPE'] =~ /\Amultipart/
|
34
|
+
env['data'] = params.select { |k,v| Hash === v && v.has_key?(:filename) }
|
35
|
+
params = params.reject { |k,v| Hash === v && v.has_key?(:filename) }
|
36
|
+
end
|
37
|
+
|
38
|
+
if values.any?
|
39
|
+
params.merge!('captures' => values)
|
40
|
+
keys.zip(values) { |k,v| Array === params[k] ? params[k] << v : params[k] = v if v }
|
41
|
+
end
|
42
|
+
|
43
|
+
env['params'] = indifferent_params(params)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Enable string or symbol key access to the nested params hash.
|
47
|
+
def indifferent_params(object)
|
48
|
+
case object
|
49
|
+
when Hash
|
50
|
+
new_hash = indifferent_hash
|
51
|
+
object.each { |key, value| new_hash[key] = indifferent_params(value) }
|
52
|
+
new_hash
|
53
|
+
when Array
|
54
|
+
object.map { |item| indifferent_params(item) }
|
55
|
+
else
|
56
|
+
object
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Creates a Hash with indifferent access.
|
61
|
+
def indifferent_hash
|
62
|
+
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/rack-putty.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rack-putty/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "rack-putty"
|
8
|
+
gem.version = Rack::Putty::VERSION
|
9
|
+
gem.authors = ["Jonathan Rudenberg", "Jesse Stuart"]
|
10
|
+
gem.email = ["jonathan@titanous.com", "jesse@jessestuart.ca"]
|
11
|
+
gem.description = %q{Simple web framework built on rack for mapping sinatra-like routes to middleware stacks.}
|
12
|
+
gem.summary = %q{Simple web framework built on rack.}
|
13
|
+
gem.homepage = ""
|
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-mount', '~> 0.8.3'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'rack-test', '~> 0.6.1'
|
23
|
+
gem.add_development_dependency 'rspec', '~> 2.11'
|
24
|
+
gem.add_development_dependency 'bundler'
|
25
|
+
gem.add_development_dependency 'rake'
|
26
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
describe Rack::Putty::Router do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
class SerializeResponse
|
8
|
+
def self.call(env)
|
9
|
+
headers = { 'Content-Type' => 'text/plain' }.merge(env['response_headers'] || {})
|
10
|
+
if env['response']
|
11
|
+
[200, headers, [serialize_response(headers, env['response'])]]
|
12
|
+
else
|
13
|
+
[404, headers, ['Not Found']]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.serialize_response(headers, response)
|
18
|
+
if headers['Content-Type'] =~ /json/
|
19
|
+
response.to_json
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class TestMiddleware < Rack::Putty::Middleware
|
25
|
+
def action(env)
|
26
|
+
env['response'] ||= {}
|
27
|
+
env['response']['params'] = env['params']
|
28
|
+
env['response_headers'] = { 'Content-Type' => 'application/json' }
|
29
|
+
env
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class OtherTestMiddleware < Rack::Putty::Middleware
|
34
|
+
def action(env)
|
35
|
+
env['response'] ||= {}
|
36
|
+
env['response'][@options[:key]] = @options[:val]
|
37
|
+
env
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class TestMiddlewarePrematureResponse < Rack::Putty::Middleware
|
42
|
+
def action(env)
|
43
|
+
[200, { 'Content-Type' => 'text/plain' }, ['Premature-Response']]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class TestMountedApp
|
48
|
+
include Rack::Putty::Router
|
49
|
+
|
50
|
+
stack_base SerializeResponse
|
51
|
+
middleware OtherTestMiddleware, :key => 'foo', :val => 'bar'
|
52
|
+
middleware OtherTestMiddleware, :key => 'biz', :val => 'baz'
|
53
|
+
|
54
|
+
get '/chunky/:bacon' do |b|
|
55
|
+
b.use TestMiddleware
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class PrefixMountedApp
|
60
|
+
def initialize(app)
|
61
|
+
@app = app
|
62
|
+
end
|
63
|
+
|
64
|
+
def call(env)
|
65
|
+
myprefix = '/prefix'
|
66
|
+
|
67
|
+
if env['PATH_INFO'].start_with?(myprefix)
|
68
|
+
env['SCRIPT_NAME'] = env['SCRIPT_NAME'][0..-2] if env['SCRIPT_NAME'].end_with?('/') # strip trailing slash
|
69
|
+
env['SCRIPT_NAME'] += myprefix
|
70
|
+
|
71
|
+
env['PATH_INFO'].sub! myprefix, ''
|
72
|
+
@app.call(env)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class TestApp
|
78
|
+
include Rack::Putty::Router
|
79
|
+
|
80
|
+
stack_base SerializeResponse
|
81
|
+
|
82
|
+
get '/foo/:bar' do |b|
|
83
|
+
b.use TestMiddleware
|
84
|
+
end
|
85
|
+
|
86
|
+
get '/premature/response' do |b|
|
87
|
+
b.use TestMiddlewarePrematureResponse
|
88
|
+
b.use TestMiddleware
|
89
|
+
end
|
90
|
+
|
91
|
+
post %r{^/foo/([^/]+)/bar} do |b|
|
92
|
+
b.use TestMiddleware
|
93
|
+
end
|
94
|
+
|
95
|
+
mount TestMountedApp
|
96
|
+
end
|
97
|
+
|
98
|
+
def app
|
99
|
+
TestApp.new
|
100
|
+
end
|
101
|
+
|
102
|
+
let(:env) { {} }
|
103
|
+
|
104
|
+
context "as a mounted app with a prefix" do
|
105
|
+
let(:app) { PrefixMountedApp.new(TestApp.new) }
|
106
|
+
|
107
|
+
it "still matches the path name" do
|
108
|
+
get '/prefix/foo/baz', {}, env
|
109
|
+
expect(last_response.status).to eq(200)
|
110
|
+
expect(JSON.parse(last_response.body)['params']['bar']).to eq('baz')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should extract params" do
|
115
|
+
get '/foo/baz', nil, env
|
116
|
+
expect(last_response.status).to eq(200)
|
117
|
+
expect(JSON.parse(last_response.body)['params']['bar']).to eq('baz')
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should merge both sets of params" do
|
121
|
+
post '/foo/baz/bar?chunky=bacon', {}, env
|
122
|
+
expect(last_response.status).to eq(200)
|
123
|
+
actual_body = JSON.parse(last_response.body)
|
124
|
+
expect(actual_body['params']['chunky']).to eq('bacon')
|
125
|
+
expect(actual_body['params']['captures']).to include('baz')
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should work with mount" do
|
129
|
+
get '/chunky/crunch', {}, env
|
130
|
+
expect(last_response.status).to eq(200)
|
131
|
+
body = JSON.parse(last_response.body)
|
132
|
+
expect(body['params']['bacon']).to eq('crunch')
|
133
|
+
expect(body['foo']).to eq('bar')
|
134
|
+
expect(body['biz']).to eq('baz')
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should allow middleware to prematurely respond" do
|
138
|
+
get '/premature/response', {}, env
|
139
|
+
expect(last_response.body).to eq('Premature-Response')
|
140
|
+
end
|
141
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-putty
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jonathan Rudenberg
|
9
|
+
- Jesse Stuart
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-04-07 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rack-mount
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.8.3
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 0.8.3
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rack-test
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 0.6.1
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.6.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.11'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '2.11'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: bundler
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: rake
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :development
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
description: Simple web framework built on rack for mapping sinatra-like routes to
|
96
|
+
middleware stacks.
|
97
|
+
email:
|
98
|
+
- jonathan@titanous.com
|
99
|
+
- jesse@jessestuart.ca
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- Gemfile
|
106
|
+
- LICENSE
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- lib/rack-putty.rb
|
110
|
+
- lib/rack-putty/middleware.rb
|
111
|
+
- lib/rack-putty/router.rb
|
112
|
+
- lib/rack-putty/router/extract_params.rb
|
113
|
+
- lib/rack-putty/version.rb
|
114
|
+
- rack-putty.gemspec
|
115
|
+
- spec/integration/router_spec.rb
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
homepage: ''
|
118
|
+
licenses: []
|
119
|
+
post_install_message:
|
120
|
+
rdoc_options: []
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ! '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 1.8.23
|
138
|
+
signing_key:
|
139
|
+
specification_version: 3
|
140
|
+
summary: Simple web framework built on rack.
|
141
|
+
test_files:
|
142
|
+
- spec/integration/router_spec.rb
|
143
|
+
- spec/spec_helper.rb
|