rack-app 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -3
- data/Gemfile.lock +37 -0
- data/README.md +5 -1
- data/VERSION +1 -1
- data/example/config.ru +7 -4
- data/example/{first_controller.rb → mounted_controller.rb} +1 -1
- data/example/ping.sh +5 -0
- data/lib/rack/app.rb +12 -8
- data/lib/rack/app/{syntax_sugar.rb → class_methods.rb} +9 -7
- data/lib/rack/app/endpoint.rb +6 -1
- data/lib/rack/app/request_helpers.rb +28 -7
- data/lib/rack/app/router.rb +38 -0
- data/lib/rack/app/router/dynamic.rb +76 -0
- data/lib/rack/app/router/static.rb +23 -0
- data/lib/rack/app/runner.rb +1 -1
- data/lib/rack/app/utils.rb +20 -0
- data/spike/routing_time.rb +40 -0
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d997c9befda0175c0b2b1c176a9e26ed16610608
|
4
|
+
data.tar.gz: 262f5320b2e82ea7330694dd02d0b66bc1699cd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 641be2541ffe9cca5a775a3f691678db4685ef6c99c4f31584266e229123b3c6d3bea85e511724c7bb8880c6cb84d55cf52a6f48f5498f0bb8b2a876b78b4f13
|
7
|
+
data.tar.gz: 0c2c35043330ab3b389cb97d1d05104cff383b7bd5b96b875ab0fe012c07919491bf06b624053aa8a8b13873ac362985e08bc256a71d705cfa3a5464d517ece7
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rack-app (0.2.0)
|
5
|
+
rack
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.2.5)
|
11
|
+
rack (1.6.4)
|
12
|
+
rake (10.4.2)
|
13
|
+
rspec (3.3.0)
|
14
|
+
rspec-core (~> 3.3.0)
|
15
|
+
rspec-expectations (~> 3.3.0)
|
16
|
+
rspec-mocks (~> 3.3.0)
|
17
|
+
rspec-core (3.3.2)
|
18
|
+
rspec-support (~> 3.3.0)
|
19
|
+
rspec-expectations (3.3.1)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.3.0)
|
22
|
+
rspec-mocks (3.3.2)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.3.0)
|
25
|
+
rspec-support (3.3.0)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
bundler (~> 1.10)
|
32
|
+
rack-app!
|
33
|
+
rake (~> 10.0)
|
34
|
+
rspec
|
35
|
+
|
36
|
+
BUNDLED WITH
|
37
|
+
1.10.6
|
data/README.md
CHANGED
@@ -6,6 +6,7 @@ The idea behind is simple.
|
|
6
6
|
Have a little framework that can allow you write pure rack apps,
|
7
7
|
that will do nothing more than what you defined.
|
8
8
|
|
9
|
+
This includes that it do not depend on fat libs like activesupport.
|
9
10
|
|
10
11
|
## Installation
|
11
12
|
|
@@ -56,7 +57,10 @@ run YourAwesomeApp
|
|
56
57
|
|
57
58
|
* benchmark for rails, padrino, sinatra, grape etc to prove awesomeness
|
58
59
|
* more verbose readme
|
59
|
-
* drink less coffee
|
60
|
+
* drink less coffee
|
61
|
+
* support restful endpoints
|
62
|
+
* params
|
63
|
+
* route-matching
|
60
64
|
|
61
65
|
## Development
|
62
66
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/example/config.ru
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__),'..','lib'))
|
2
2
|
require 'rack/app'
|
3
3
|
|
4
|
-
require_relative '
|
4
|
+
require_relative 'mounted_controller'
|
5
5
|
|
6
|
-
class
|
6
|
+
class YourAwesomeApp < Rack::APP
|
7
7
|
|
8
|
-
mount
|
8
|
+
mount MountedController
|
9
9
|
|
10
10
|
get '/hello' do
|
11
11
|
'Hello World!'
|
@@ -13,9 +13,12 @@ class SampleApp < Rack::APP
|
|
13
13
|
|
14
14
|
get '/nope' do
|
15
15
|
response.write 'nope nope nope...'
|
16
|
+
end
|
16
17
|
|
18
|
+
get '/users/:user_id' do
|
19
|
+
params['user_id']
|
17
20
|
end
|
18
21
|
|
19
22
|
end
|
20
23
|
|
21
|
-
run
|
24
|
+
run YourAwesomeApp
|
data/example/ping.sh
ADDED
data/lib/rack/app.rb
CHANGED
@@ -5,27 +5,31 @@ class Rack::APP
|
|
5
5
|
|
6
6
|
require 'rack/app/version'
|
7
7
|
|
8
|
+
require 'rack/app/utils'
|
9
|
+
require 'rack/app/router'
|
8
10
|
require 'rack/app/endpoint'
|
9
11
|
require 'rack/app/endpoint/not_found'
|
10
|
-
|
11
12
|
require 'rack/app/runner'
|
12
13
|
|
13
|
-
require 'rack/app/
|
14
|
-
extend Rack::APP::
|
14
|
+
require 'rack/app/class_methods'
|
15
|
+
extend Rack::APP::ClassMethods
|
15
16
|
|
16
17
|
require 'rack/app/request_helpers'
|
17
18
|
|
18
19
|
include Rack::APP::RequestHelpers
|
19
20
|
|
21
|
+
def self.call(request_env)
|
22
|
+
Rack::APP::Runner.response_for(self,request_env)
|
23
|
+
end
|
24
|
+
|
20
25
|
attr_reader :request, :response
|
21
26
|
|
22
|
-
|
27
|
+
protected
|
28
|
+
|
29
|
+
def initialize(request, response,options = {})
|
23
30
|
@response = response
|
24
31
|
@request = request
|
25
|
-
|
26
|
-
|
27
|
-
def self.call(request_env)
|
28
|
-
Rack::APP::Runner.response_for(self,request_env)
|
32
|
+
@options = options
|
29
33
|
end
|
30
34
|
|
31
35
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Rack::APP::
|
1
|
+
module Rack::APP::ClassMethods
|
2
2
|
|
3
3
|
def description(*description_texts)
|
4
4
|
@last_description = description_texts.join("\n")
|
@@ -32,10 +32,13 @@ module Rack::APP::SyntaxSugar
|
|
32
32
|
add_route('PATCH', path, &block)
|
33
33
|
end
|
34
34
|
|
35
|
+
def router
|
36
|
+
@static_router ||= Rack::APP::Router.new
|
37
|
+
end
|
38
|
+
|
35
39
|
def add_route(request_method, request_path, &block)
|
36
|
-
request_key = [request_method, request_path]
|
37
40
|
|
38
|
-
endpoint =
|
41
|
+
endpoint = Rack::APP::Endpoint.new(
|
39
42
|
self,
|
40
43
|
{
|
41
44
|
request_method: request_method,
|
@@ -45,13 +48,12 @@ module Rack::APP::SyntaxSugar
|
|
45
48
|
&block
|
46
49
|
)
|
47
50
|
|
51
|
+
router.add_endpoint(request_method,request_path,endpoint)
|
52
|
+
|
48
53
|
@last_description = nil
|
49
54
|
endpoint
|
50
55
|
end
|
51
56
|
|
52
|
-
def endpoints
|
53
|
-
@endpoints ||= {}
|
54
|
-
end
|
55
57
|
|
56
58
|
def mount(api_class)
|
57
59
|
|
@@ -59,7 +61,7 @@ module Rack::APP::SyntaxSugar
|
|
59
61
|
raise(ArgumentError, 'Invalid class given for mount, must be a Rack::APP')
|
60
62
|
end
|
61
63
|
|
62
|
-
|
64
|
+
router.merge!(api_class.router)
|
63
65
|
|
64
66
|
nil
|
65
67
|
end
|
data/lib/rack/app/endpoint.rb
CHANGED
@@ -4,6 +4,7 @@ class Rack::APP::Endpoint
|
|
4
4
|
@properties = properties
|
5
5
|
@logic_block = logic_block
|
6
6
|
@api_class = api_class
|
7
|
+
@path_params_matcher = {}
|
7
8
|
end
|
8
9
|
|
9
10
|
def execute(request_env)
|
@@ -11,7 +12,7 @@ class Rack::APP::Endpoint
|
|
11
12
|
request = Rack::Request.new(request_env)
|
12
13
|
response = Rack::Response.new
|
13
14
|
|
14
|
-
request_handler = @api_class.new(request, response)
|
15
|
+
request_handler = @api_class.new(request, response,{path_params_matcher: @path_params_matcher})
|
15
16
|
call_return = request_handler.instance_exec(&@logic_block)
|
16
17
|
|
17
18
|
return call_return if is_a_rack_response_finish?(call_return)
|
@@ -21,6 +22,10 @@ class Rack::APP::Endpoint
|
|
21
22
|
|
22
23
|
end
|
23
24
|
|
25
|
+
def register_path_params_matcher(params_matcher)
|
26
|
+
@path_params_matcher.merge!(params_matcher)
|
27
|
+
end
|
28
|
+
|
24
29
|
protected
|
25
30
|
|
26
31
|
def add_response_body_if_missing(call_return, response)
|
@@ -2,15 +2,36 @@ require 'cgi'
|
|
2
2
|
module Rack::APP::RequestHelpers
|
3
3
|
|
4
4
|
def params
|
5
|
-
@__request_params__ ||=
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
@__request_params__ ||= -> {
|
6
|
+
|
7
|
+
raw_params = CGI.parse(request.env['QUERY_STRING'].to_s).freeze.reduce({}) do |params_collection, (k, v)|
|
8
|
+
if v.is_a?(Array) and v.length === 1
|
9
|
+
params_collection[k]= v[0]
|
10
|
+
else
|
11
|
+
params_collection[k]= v
|
12
|
+
end
|
13
|
+
|
14
|
+
params_collection
|
10
15
|
end
|
11
16
|
|
12
|
-
|
13
|
-
|
17
|
+
if @options[:path_params_matcher].is_a?(Hash) and not @options[:path_params_matcher].empty?
|
18
|
+
|
19
|
+
request_path_parts = Rack::APP::Utils.normalize_path(request.env['REQUEST_PATH']).split('/')
|
20
|
+
|
21
|
+
path_params = request_path_parts.each.with_index.reduce({}) do |params_col,(path_part,index)|
|
22
|
+
if @options[:path_params_matcher][index]
|
23
|
+
params_col[@options[:path_params_matcher][index]]= path_part
|
24
|
+
end
|
25
|
+
params_col
|
26
|
+
end
|
27
|
+
|
28
|
+
raw_params.merge!(path_params)
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
raw_params
|
33
|
+
|
34
|
+
}.call
|
14
35
|
end
|
15
36
|
|
16
37
|
def status(new_status=nil)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Rack::APP::Router
|
2
|
+
|
3
|
+
require 'rack/app/router/static'
|
4
|
+
require 'rack/app/router/dynamic'
|
5
|
+
|
6
|
+
def add_endpoint(request_method, request_path, endpoint)
|
7
|
+
if defined_path_is_dynamic?(Rack::APP::Utils.normalize_path(request_path))
|
8
|
+
@dynamic_router.add_endpoint(request_method, request_path, endpoint)
|
9
|
+
else
|
10
|
+
@static_router.add_endpoint(request_method, request_path, endpoint)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch_endpoint(request_method, request_path)
|
15
|
+
@static_router.fetch_endpoint(request_method, request_path) or
|
16
|
+
@dynamic_router.fetch_endpoint(request_method, request_path) or
|
17
|
+
Rack::APP::Endpoint::NOT_FOUND
|
18
|
+
end
|
19
|
+
|
20
|
+
def merge!(router)
|
21
|
+
raise(ArgumentError, "invalid router object, must be instance of #{self.class}") unless router.is_a?(self.class)
|
22
|
+
@static_router.merge!(router.instance_variable_get(:@static_router))
|
23
|
+
@dynamic_router.merge!(router.instance_variable_get(:@dynamic_router))
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@static_router = Rack::APP::Router::Static.new
|
31
|
+
@dynamic_router = Rack::APP::Router::Dynamic.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def defined_path_is_dynamic?(path_str)
|
35
|
+
!!(path_str.to_s =~ /\/:\w+/i)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class Rack::APP::Router::Dynamic
|
2
|
+
|
3
|
+
ANY = 'Rack::APP::Router::Dynamic::ANY'.freeze
|
4
|
+
|
5
|
+
def add_endpoint(request_method, request_path, endpoint)
|
6
|
+
request_path = Rack::APP::Utils.normalize_path(request_path)
|
7
|
+
|
8
|
+
current_cluster = main_cluster(request_method)
|
9
|
+
path_params = {}
|
10
|
+
request_path.split('/').each.with_index do |path_part, index|
|
11
|
+
|
12
|
+
new_cluster_name = if path_part_is_dynamic?(path_part)
|
13
|
+
path_params[index]= path_part.sub(/^:/,'')
|
14
|
+
ANY
|
15
|
+
else
|
16
|
+
path_part
|
17
|
+
end
|
18
|
+
|
19
|
+
current_cluster = (current_cluster[new_cluster_name] ||= {})
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
current_cluster[:endpoint]= endpoint
|
24
|
+
current_cluster[:endpoint].register_path_params_matcher(path_params)
|
25
|
+
|
26
|
+
endpoint
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_endpoint(request_method, request_path)
|
30
|
+
normalized_request_path = Rack::APP::Utils.normalize_path(request_path)
|
31
|
+
|
32
|
+
current_cluster = main_cluster(request_method)
|
33
|
+
normalized_request_path.split('/').each do |path_part|
|
34
|
+
current_cluster = current_cluster[path_part] || current_cluster[ANY]
|
35
|
+
return nil if current_cluster.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
current_cluster[:endpoint]
|
39
|
+
end
|
40
|
+
|
41
|
+
def merge!(router)
|
42
|
+
raise(ArgumentError,"invalid route object, must be instance of #{self.class.to_s}") unless router.is_a?(self.class)
|
43
|
+
deep_merge!(@http_method_cluster,router.instance_variable_get(:@http_method_cluster))
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@http_method_cluster = {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def path_part_is_dynamic?(path_part_str)
|
54
|
+
!!(path_part_str.to_s =~ /^:\w+$/i)
|
55
|
+
end
|
56
|
+
|
57
|
+
def deep_merge!(hash,other_hash)
|
58
|
+
other_hash.each_pair do |current_key, other_value|
|
59
|
+
|
60
|
+
this_value = hash[current_key]
|
61
|
+
|
62
|
+
hash[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
|
63
|
+
deep_merge!(this_value,other_value)
|
64
|
+
else
|
65
|
+
other_value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
hash
|
70
|
+
end
|
71
|
+
|
72
|
+
def main_cluster(request_method)
|
73
|
+
(@http_method_cluster[request_method.to_s.upcase] ||= {})
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Rack::APP::Router::Static
|
2
|
+
|
3
|
+
def add_endpoint(request_method, request_path, endpoint)
|
4
|
+
@endpoints[[request_method.to_s.upcase, Rack::APP::Utils.normalize_path(request_path)]]= endpoint
|
5
|
+
end
|
6
|
+
|
7
|
+
def fetch_endpoint(request_method, request_path)
|
8
|
+
@endpoints[[request_method, request_path]]
|
9
|
+
end
|
10
|
+
|
11
|
+
def merge!(static_router)
|
12
|
+
raise(ArgumentError,"Invalid argument given, must be instance of a #{self.class.to_s}") unless static_router.is_a?(self.class)
|
13
|
+
@endpoints.merge!(static_router.instance_variable_get(:@endpoints))
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@endpoints = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/rack/app/runner.rb
CHANGED
@@ -9,7 +9,7 @@ module Rack::APP::Runner
|
|
9
9
|
protected
|
10
10
|
|
11
11
|
def fetch_endpoint(api_class, request_method, request_path)
|
12
|
-
api_class.
|
12
|
+
api_class.router.fetch_endpoint(request_method, request_path)
|
13
13
|
end
|
14
14
|
|
15
15
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rack::APP::Utils
|
2
|
+
extend self
|
3
|
+
|
4
|
+
# Normalizes URI path.
|
5
|
+
#
|
6
|
+
# Strips off trailing slash and ensures there is a leading slash.
|
7
|
+
#
|
8
|
+
# normalize_path("/foo") # => "/foo"
|
9
|
+
# normalize_path("/foo/") # => "/foo"
|
10
|
+
# normalize_path("foo") # => "/foo"
|
11
|
+
# normalize_path("") # => "/"
|
12
|
+
def normalize_path(path)
|
13
|
+
path = "/#{path}"
|
14
|
+
path.squeeze!('/')
|
15
|
+
path.sub!(%r{/+\Z}, '')
|
16
|
+
path = '/' if path == ''
|
17
|
+
path
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'rack/app'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
static_router = Rack::APP::Router::Static.new
|
6
|
+
dynamic_router = Rack::APP::Router::Dynamic.new
|
7
|
+
|
8
|
+
classic_router = []
|
9
|
+
|
10
|
+
endpoint_paths = []
|
11
|
+
10000.times do
|
12
|
+
endpoint_paths << ('/' + 7.times.map { SecureRandom.uuid }.join('/'))
|
13
|
+
|
14
|
+
static_router.add_endpoint('GET', endpoint_paths.last, -> {})
|
15
|
+
static_router.add_endpoint('GET', endpoint_paths.last, -> {})
|
16
|
+
classic_router << ['GET',endpoint_paths.last,->{}]
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
start_time = Time.now
|
21
|
+
endpoint_paths.each do |request_path|
|
22
|
+
static_router.fetch_endpoint('GET',request_path)
|
23
|
+
end
|
24
|
+
finish_time_of_static = Time.now - start_time
|
25
|
+
|
26
|
+
start_time = Time.now
|
27
|
+
endpoint_paths.each do |request_path|
|
28
|
+
dynamic_router.fetch_endpoint('GET',request_path)
|
29
|
+
end
|
30
|
+
finish_time_of_dynamic = Time.now - start_time
|
31
|
+
|
32
|
+
start_time = Time.now
|
33
|
+
endpoint_paths.each do |request_path|
|
34
|
+
classic_router.find{|ary| ary[0] == 'GET' and ary[1] == request_path }
|
35
|
+
end
|
36
|
+
finish_time_of_classic = Time.now - start_time
|
37
|
+
|
38
|
+
puts "time taken by static: #{finish_time_of_static}",
|
39
|
+
"time taken by dynamic: #{finish_time_of_dynamic}",
|
40
|
+
"time taken by classic(mock): #{finish_time_of_classic}"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-app
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Luzsi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -78,20 +78,27 @@ files:
|
|
78
78
|
- ".rspec"
|
79
79
|
- CODE_OF_CONDUCT.md
|
80
80
|
- Gemfile
|
81
|
+
- Gemfile.lock
|
81
82
|
- README.md
|
82
83
|
- Rakefile
|
83
84
|
- VERSION
|
84
85
|
- bin/setup
|
85
86
|
- example/config.ru
|
86
|
-
- example/
|
87
|
+
- example/mounted_controller.rb
|
88
|
+
- example/ping.sh
|
87
89
|
- lib/rack/app.rb
|
90
|
+
- lib/rack/app/class_methods.rb
|
88
91
|
- lib/rack/app/endpoint.rb
|
89
92
|
- lib/rack/app/endpoint/not_found.rb
|
90
93
|
- lib/rack/app/request_helpers.rb
|
94
|
+
- lib/rack/app/router.rb
|
95
|
+
- lib/rack/app/router/dynamic.rb
|
96
|
+
- lib/rack/app/router/static.rb
|
91
97
|
- lib/rack/app/runner.rb
|
92
|
-
- lib/rack/app/
|
98
|
+
- lib/rack/app/utils.rb
|
93
99
|
- lib/rack/app/version.rb
|
94
100
|
- rack-app.gemspec
|
101
|
+
- spike/routing_time.rb
|
95
102
|
homepage: https://github.com/adamluzsi/rack-app.rb
|
96
103
|
licenses: []
|
97
104
|
metadata: {}
|