hobby 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +7 -0
- data/LICENSE +1 -0
- data/hobby.gemspec +5 -14
- data/lib/hobby/app.rb +36 -43
- data/lib/hobby/router.rb +26 -14
- data/lib/hobby/rspec/router.rb +35 -0
- data/lib/hobby.rb +3 -4
- data/spec/app_spec.rb +146 -0
- data/spec/apps/Env.rb +15 -0
- data/spec/apps/Main.rb +4 -0
- data/spec/apps/Map.rb +5 -0
- data/spec/apps/Nested.rb +17 -0
- data/spec/apps/OneRouteRouter.rb +8 -0
- data/spec/apps/Use.rb +15 -0
- data/spec/apps/WithoutPath.rb +3 -0
- data/spec/helper.rb +21 -0
- data/spec/router_group_spec.rb +46 -0
- data/spec/router_spec.rb +37 -0
- metadata +31 -114
- data/lib/hobby/router/route.rb +0 -20
- data/test/helper.rb +0 -5
- data/test/test_app.rb +0 -189
- data/test/test_router.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42e7f7a0e7818909ac5d95c50484655aab19fac1
|
4
|
+
data.tar.gz: 7b0f857a9f4ceb6dfdb0eff86d6d546d2b285a6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2f3740ca3b95e942af4bf6dc4fb5f8d5acfdd21bd6c48f0544b8f262de0f004856a31547f3739ad440a11195c80a0348e4c4237afb026d4fa566dc6cb138ad2
|
7
|
+
data.tar.gz: fe87a3f5ca1be2c5ee1a346cdeb9e26800c642e4de8251217f8461ba91c8dbb63c7b52ea1a208edb35f6851d27b47e385df4bfb153849065e826fc1b2ace1fed
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/hobby.gemspec
CHANGED
@@ -4,12 +4,11 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'hobby'
|
7
|
-
spec.version = '0.0.
|
8
|
-
spec.authors = ['
|
9
|
-
spec.email = ['
|
10
|
-
spec.
|
11
|
-
spec.
|
12
|
-
spec.homepage = 'https://github.com/patriciomacadden/hobbit'
|
7
|
+
spec.version = '0.0.1'
|
8
|
+
spec.authors = ['Anatoly Cherno']
|
9
|
+
spec.email = ['anatoly.cherno@gmail.com']
|
10
|
+
spec.summary = %q{A minimal DSL over rack}
|
11
|
+
spec.homepage = 'https://github.com/ch1c0t/hobby'
|
13
12
|
spec.license = 'MIT'
|
14
13
|
|
15
14
|
spec.files = `git ls-files`.split($/)
|
@@ -18,12 +17,4 @@ Gem::Specification.new do |spec|
|
|
18
17
|
spec.require_paths = ['lib']
|
19
18
|
|
20
19
|
spec.add_dependency 'rack'
|
21
|
-
spec.add_dependency 'include_constants'
|
22
|
-
|
23
|
-
spec.add_development_dependency 'bundler', '~> 1.3'
|
24
|
-
spec.add_development_dependency 'codeclimate-test-reporter'
|
25
|
-
spec.add_development_dependency 'rack-test'
|
26
|
-
spec.add_development_dependency 'rake'
|
27
|
-
spec.add_development_dependency 'minitest'
|
28
|
-
spec.add_development_dependency 'minitest-power_assert'
|
29
20
|
end
|
data/lib/hobby/app.rb
CHANGED
@@ -1,66 +1,59 @@
|
|
1
1
|
module Hobby
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
module App
|
3
|
+
def self.included app
|
4
|
+
app.extend Singleton
|
5
|
+
app.builder, app.router = Rack::Builder.new, Router.new
|
6
|
+
app.request, app.response = Rack::Request, Rack::Response
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
end
|
9
|
+
module Singleton
|
10
|
+
attr_accessor :builder, :router, :request, :response
|
11
|
+
|
12
|
+
def new (*)
|
13
|
+
builder.run super
|
14
|
+
builder.to_app
|
16
15
|
end
|
17
16
|
|
17
|
+
extend Forwardable
|
18
|
+
delegate [:map, :use] => :builder
|
19
|
+
|
18
20
|
Verbs.each do |verb|
|
19
|
-
define_method verb.downcase do |path, &route|
|
21
|
+
define_method verb.downcase do |path = '/', &route|
|
20
22
|
router.add_route verb, path, &route
|
21
23
|
end
|
22
24
|
end
|
23
|
-
|
24
|
-
alias :_new :new
|
25
|
-
def new *args, &block
|
26
|
-
builder.run _new(*args, &block)
|
27
|
-
builder
|
28
|
-
end
|
29
|
-
|
30
|
-
extend Forwardable
|
31
|
-
delegate [:map, :use] => :builder
|
32
25
|
end
|
33
26
|
|
34
|
-
attr_reader :env, :request, :response
|
35
|
-
|
36
27
|
def call env
|
37
28
|
dup.handle env
|
38
29
|
end
|
39
30
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@response = Response.new
|
31
|
+
protected
|
32
|
+
def handle env
|
33
|
+
route = self.class.router.route_for (@env = env)
|
44
34
|
|
45
|
-
|
46
|
-
|
35
|
+
if route
|
36
|
+
response.write instance_exec &route
|
37
|
+
else
|
38
|
+
response.status = 404
|
39
|
+
end
|
47
40
|
|
48
|
-
|
41
|
+
response
|
42
|
+
end
|
49
43
|
|
50
|
-
|
51
|
-
|
52
|
-
end
|
44
|
+
private
|
45
|
+
attr_reader :env
|
53
46
|
|
54
|
-
|
55
|
-
|
47
|
+
def request
|
48
|
+
@request ||= self.class.request.new env
|
49
|
+
end
|
56
50
|
|
57
|
-
|
58
|
-
response
|
59
|
-
else
|
60
|
-
response.status = 404
|
51
|
+
def response
|
52
|
+
@response ||= self.class.response.new
|
61
53
|
end
|
62
54
|
|
63
|
-
|
64
|
-
|
55
|
+
def my
|
56
|
+
env.fetch :path_params, {}
|
57
|
+
end
|
65
58
|
end
|
66
59
|
end
|
data/lib/hobby/router.rb
CHANGED
@@ -1,29 +1,41 @@
|
|
1
1
|
module Hobby
|
2
2
|
class Router
|
3
|
-
require_relative 'router/route'
|
4
|
-
|
5
3
|
def initialize
|
6
|
-
@routes =
|
4
|
+
@routes = Routes.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def add_route verb, path, &route
|
8
|
+
path = nil if path.eql? '/'
|
9
|
+
@routes["#{verb}#{path}"] = route
|
7
10
|
end
|
8
11
|
|
9
|
-
def
|
10
|
-
@routes[
|
11
|
-
|
12
|
+
def route_for env
|
13
|
+
route, params = @routes["#{env['REQUEST_METHOD']}#{env['PATH_INFO']}"]
|
14
|
+
env[:path_params] = params if params
|
15
|
+
route
|
12
16
|
end
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
|
18
|
+
class Routes < Hash
|
19
|
+
def initialize
|
20
|
+
@patterns = {}
|
21
|
+
super { |hash, key| hash[key] = find key }
|
17
22
|
end
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
24
|
+
def []= key, route
|
25
|
+
if key.include? ?:
|
26
|
+
string = key.gsub(/(:\w+)/) { "(?<#{$1[1..-1]}>[^/?#]+)" }
|
27
|
+
@patterns[/^#{string}$/] = route
|
28
|
+
else
|
29
|
+
super and super "#{key}/", route
|
23
30
|
end
|
24
31
|
end
|
25
32
|
|
26
|
-
|
33
|
+
private
|
34
|
+
|
35
|
+
def find key
|
36
|
+
_, route = @patterns.find { |pattern, _| pattern.match key }
|
37
|
+
[route, $~.names.map(&:to_sym).zip($~.captures).to_h] if route
|
38
|
+
end
|
27
39
|
end
|
28
40
|
end
|
29
41
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Hobby::RSpec
|
2
|
+
module Router
|
3
|
+
extend self
|
4
|
+
SOME_ROUTE = ->{}
|
5
|
+
|
6
|
+
def env_for path, verb = 'GET'
|
7
|
+
{'REQUEST_METHOD' => verb, 'PATH_INFO' => path }
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_routes *routes
|
11
|
+
routes.each { |route| subject.add_route 'GET', route, &SOME_ROUTE }
|
12
|
+
end
|
13
|
+
|
14
|
+
extend RSpec::Matchers::DSL
|
15
|
+
|
16
|
+
define :find_route do |path, verb = 'GET'|
|
17
|
+
match do |subject|
|
18
|
+
env = env_for path, verb
|
19
|
+
route = subject.route_for env
|
20
|
+
|
21
|
+
params_are_ok = (@params ? (@params.to_a - env[:path_params].to_a).empty? : true)
|
22
|
+
|
23
|
+
(route == SOME_ROUTE) && params_are_ok
|
24
|
+
end
|
25
|
+
|
26
|
+
chain :and_set_params do |**params|
|
27
|
+
@params = params
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.included example_group
|
32
|
+
example_group.extend self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/hobby.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'rack'
|
2
2
|
require 'forwardable'
|
3
|
-
require 'include_constants'
|
4
3
|
|
5
4
|
module Hobby
|
6
5
|
Verbs = %w!DELETE GET HEAD OPTIONS PATCH POST PUT!
|
7
|
-
require 'hobby/app'
|
8
|
-
autoload :Router, 'hobby/router'
|
9
|
-
include_constants :Builder, :Request, :Response, from: Rack
|
10
6
|
end
|
7
|
+
|
8
|
+
require 'hobby/router'
|
9
|
+
require 'hobby/app'
|
data/spec/app_spec.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'rack/test'
|
3
|
+
|
4
|
+
$sources = {}
|
5
|
+
|
6
|
+
Dir['spec/apps/*.rb'].each do |file|
|
7
|
+
name = File.basename file, '.rb'
|
8
|
+
|
9
|
+
eval %!
|
10
|
+
class #{name}
|
11
|
+
class << self
|
12
|
+
attr_accessor :app
|
13
|
+
end
|
14
|
+
end
|
15
|
+
!
|
16
|
+
|
17
|
+
$sources[Object.const_get name] = IO.read file
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_app described_class
|
21
|
+
body = $sources[described_class]
|
22
|
+
|
23
|
+
eval %!
|
24
|
+
Class.new do
|
25
|
+
include Hobby::App
|
26
|
+
#{body}
|
27
|
+
end
|
28
|
+
!
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Hobby::App do
|
32
|
+
include Rack::Test::Methods
|
33
|
+
|
34
|
+
describe '.new' do
|
35
|
+
context 'an app with nested app(s)' do
|
36
|
+
let(:subject) { build_app(Nested).new }
|
37
|
+
it { should be_a Rack::URLMap }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'an app without nested app(s)' do
|
41
|
+
let(:subject) { build_app(Main).new }
|
42
|
+
it { should be_a Hobby::App }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#call' do
|
47
|
+
def app
|
48
|
+
build_app(Main).new
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when a route was found' do
|
52
|
+
it 'evaluates the route' do
|
53
|
+
get '/existent'
|
54
|
+
|
55
|
+
assert { last_response.ok? }
|
56
|
+
assert { last_response.body == 'existent' }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when a route was not found' do
|
61
|
+
it 'responds with 404 status code' do
|
62
|
+
get '/nonexistent/route'
|
63
|
+
|
64
|
+
assert { last_response.not_found? }
|
65
|
+
assert { last_response.body.empty? }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'gives a distinct object to each request' do
|
70
|
+
get '/first'
|
71
|
+
assert { last_response.body == 'first' }
|
72
|
+
|
73
|
+
get '/second'
|
74
|
+
assert { last_response.body != 'first' }
|
75
|
+
assert { last_response.body == 'second' }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe :integration do
|
80
|
+
before do
|
81
|
+
described_class.app = build_app described_class
|
82
|
+
end
|
83
|
+
|
84
|
+
def app
|
85
|
+
described_class.app.new
|
86
|
+
end
|
87
|
+
|
88
|
+
describe Map do
|
89
|
+
it 'mounts an application to the rack stack' do
|
90
|
+
get '/map'
|
91
|
+
assert { last_response.body == 'from map' }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe Use do
|
96
|
+
it 'adds a middleware to the rack stack' do
|
97
|
+
get '/use'
|
98
|
+
assert { last_response.body == 'from use' }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe WithoutPath do
|
103
|
+
it 'is accessible as /' do
|
104
|
+
get '/'
|
105
|
+
assert { last_response.body == 'root' }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe OneRouteRouter do
|
110
|
+
it 'returns for any route' do
|
111
|
+
get '/'
|
112
|
+
assert { last_response.body == 'for any route' }
|
113
|
+
|
114
|
+
get '/some-other-route'
|
115
|
+
assert { last_response.body == 'for any route' }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe Env do
|
120
|
+
it do
|
121
|
+
get '/query_string?1=2&3=4'
|
122
|
+
assert { last_response.body == '1=2&3=4' }
|
123
|
+
end
|
124
|
+
|
125
|
+
it do
|
126
|
+
get '/access_params?key=value'
|
127
|
+
assert { last_response.body == 'value' }
|
128
|
+
end
|
129
|
+
|
130
|
+
it do
|
131
|
+
get '/access_path_params_via_my'
|
132
|
+
assert { last_response.body == 'true' }
|
133
|
+
|
134
|
+
get '/access_path_params_via_env'
|
135
|
+
assert { last_response.body == 'true' }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe Nested do
|
140
|
+
it do
|
141
|
+
get '/nested'
|
142
|
+
assert { last_response.body == 'a:b:c' }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/spec/apps/Env.rb
ADDED
data/spec/apps/Main.rb
ADDED
data/spec/apps/Map.rb
ADDED
data/spec/apps/Nested.rb
ADDED
data/spec/apps/Use.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
middleware = Class.new do
|
2
|
+
def initialize(app = nil)
|
3
|
+
@app = app
|
4
|
+
end
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
request = Rack::Request.new(env)
|
8
|
+
@app.call(env) unless request.path_info == '/use'
|
9
|
+
[200, {}, 'from use']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
use middleware
|
14
|
+
|
15
|
+
get('/') { 'hello world' }
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'devtools/spec_helper'
|
2
|
+
|
3
|
+
require 'hobby'
|
4
|
+
require 'hobby/rspec/router'
|
5
|
+
|
6
|
+
require 'minitest'
|
7
|
+
require 'minitest-power_assert'
|
8
|
+
Minitest::Assertions.prepend Minitest::PowerAssert::Assertions
|
9
|
+
|
10
|
+
if defined? Mutant
|
11
|
+
class Mutant::Selector::Expression
|
12
|
+
def call _subject
|
13
|
+
integration.all_tests
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.expect_with :rspec, :minitest
|
20
|
+
config.include Hobby::RSpec::Router, type: :router
|
21
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Hobby::RSpec::Router do
|
4
|
+
before do
|
5
|
+
@Group = Class.new do
|
6
|
+
def subject
|
7
|
+
@router ||= Hobby::Router.new
|
8
|
+
end
|
9
|
+
end.include described_class
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'contains a few helpers in its singleton' do
|
13
|
+
[:env_for, :add_routes, :find_route].each do |name|
|
14
|
+
assert { @Group.respond_to? name }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'env_for' do
|
19
|
+
context 'two arguments passed' do
|
20
|
+
it 'returns an environment' do
|
21
|
+
env = { "REQUEST_METHOD" => 'POST', "PATH_INFO" => '/some_path' }
|
22
|
+
assert { env == (@Group.env_for '/some_path', 'POST') }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'one argument passed' do
|
27
|
+
it 'defaults to GET' do
|
28
|
+
env = { "REQUEST_METHOD" => 'GET', "PATH_INFO" => '/some_path' }
|
29
|
+
assert { env == (@Group.env_for '/some_path') }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'add_routes' do
|
35
|
+
it do
|
36
|
+
routes = %w! first second third!
|
37
|
+
group = @Group.new
|
38
|
+
|
39
|
+
group.add_routes *routes
|
40
|
+
|
41
|
+
routes.each do |route|
|
42
|
+
expect(group.subject.route_for group.env_for 'first').to be_truthy
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/spec/router_spec.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Hobby::Router, type: :router do
|
4
|
+
routes = %w!
|
5
|
+
/with.dot
|
6
|
+
/with-hyphen
|
7
|
+
/hello/:name
|
8
|
+
/say/:something/to/:someone
|
9
|
+
/route/:id.json
|
10
|
+
/existent/only/for/GET
|
11
|
+
!
|
12
|
+
|
13
|
+
before { add_routes *routes }
|
14
|
+
|
15
|
+
it { should find_route '/with.dot' }
|
16
|
+
it { should find_route '/with-hyphen' }
|
17
|
+
it { should find_route '/with.dot/' }
|
18
|
+
it { should find_route '/with-hyphen/' }
|
19
|
+
|
20
|
+
it { should find_route '/existent/only/for/GET' }
|
21
|
+
it { should_not find_route '/nonexistent' }
|
22
|
+
it { should_not find_route '/existent/only/for/GET', 'POST' }
|
23
|
+
|
24
|
+
it { should find_route('/hello/ololo').and_set_params(name: 'ololo') }
|
25
|
+
it { should find_route('/route/66.json').and_set_params(id: '66') }
|
26
|
+
it do
|
27
|
+
should find_route('/say/nothing/to/no_one').
|
28
|
+
and_set_params(something: 'nothing', someone: 'no_one')
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
it 'memoizes requests with params' do
|
33
|
+
subject.route_for env_for '/hello/ololo'
|
34
|
+
|
35
|
+
assert { subject.instance_variable_get(:@routes).include? 'GET/hello/ololo' }
|
36
|
+
end
|
37
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hobby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Anatoly Cherno
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -24,107 +24,9 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
|
28
|
-
name: include_constants
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: bundler
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.3'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '1.3'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: codeclimate-test-reporter
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rack-test
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rake
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: minitest
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: minitest-power_assert
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
|
-
description: A minimalistic microframework built on top of rack
|
27
|
+
description:
|
126
28
|
email:
|
127
|
-
-
|
29
|
+
- anatoly.cherno@gmail.com
|
128
30
|
executables: []
|
129
31
|
extensions: []
|
130
32
|
extra_rdoc_files: []
|
@@ -140,11 +42,19 @@ files:
|
|
140
42
|
- lib/hobby.rb
|
141
43
|
- lib/hobby/app.rb
|
142
44
|
- lib/hobby/router.rb
|
143
|
-
- lib/hobby/router
|
144
|
-
-
|
145
|
-
-
|
146
|
-
-
|
147
|
-
|
45
|
+
- lib/hobby/rspec/router.rb
|
46
|
+
- spec/app_spec.rb
|
47
|
+
- spec/apps/Env.rb
|
48
|
+
- spec/apps/Main.rb
|
49
|
+
- spec/apps/Map.rb
|
50
|
+
- spec/apps/Nested.rb
|
51
|
+
- spec/apps/OneRouteRouter.rb
|
52
|
+
- spec/apps/Use.rb
|
53
|
+
- spec/apps/WithoutPath.rb
|
54
|
+
- spec/helper.rb
|
55
|
+
- spec/router_group_spec.rb
|
56
|
+
- spec/router_spec.rb
|
57
|
+
homepage: https://github.com/ch1c0t/hobby
|
148
58
|
licenses:
|
149
59
|
- MIT
|
150
60
|
metadata: {}
|
@@ -164,12 +74,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
74
|
version: '0'
|
165
75
|
requirements: []
|
166
76
|
rubyforge_project:
|
167
|
-
rubygems_version: 2.
|
77
|
+
rubygems_version: 2.5.1
|
168
78
|
signing_key:
|
169
79
|
specification_version: 4
|
170
|
-
summary: A
|
80
|
+
summary: A minimal DSL over rack
|
171
81
|
test_files:
|
172
|
-
-
|
173
|
-
-
|
174
|
-
-
|
175
|
-
|
82
|
+
- spec/app_spec.rb
|
83
|
+
- spec/apps/Env.rb
|
84
|
+
- spec/apps/Main.rb
|
85
|
+
- spec/apps/Map.rb
|
86
|
+
- spec/apps/Nested.rb
|
87
|
+
- spec/apps/OneRouteRouter.rb
|
88
|
+
- spec/apps/Use.rb
|
89
|
+
- spec/apps/WithoutPath.rb
|
90
|
+
- spec/helper.rb
|
91
|
+
- spec/router_group_spec.rb
|
92
|
+
- spec/router_spec.rb
|
data/lib/hobby/router/route.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
class Hobby::Router
|
2
|
-
class Route
|
3
|
-
attr_reader :compiled_path, :extra_params, :path
|
4
|
-
def initialize(path, &block)
|
5
|
-
@path = path
|
6
|
-
@block = block
|
7
|
-
|
8
|
-
@extra_params = []
|
9
|
-
compiled_path = path.gsub(/:\w+/) do |match|
|
10
|
-
@extra_params << match.gsub(':', '').to_sym
|
11
|
-
'([^/?#]+)'
|
12
|
-
end
|
13
|
-
@compiled_path = /^#{compiled_path}$/
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_proc
|
17
|
-
@block
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/test/helper.rb
DELETED
data/test/test_app.rb
DELETED
@@ -1,189 +0,0 @@
|
|
1
|
-
require_relative 'helper'
|
2
|
-
|
3
|
-
module Minitest
|
4
|
-
class Test
|
5
|
-
include Rack::Test::Methods
|
6
|
-
|
7
|
-
def mock_app(&block)
|
8
|
-
@app = Class.new(Hobby::App, &block).new
|
9
|
-
end
|
10
|
-
|
11
|
-
def app
|
12
|
-
@app
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe Hobby::App do
|
18
|
-
describe :main_app do
|
19
|
-
before do
|
20
|
-
mock_app do
|
21
|
-
Hobby::Verbs.each do |verb|
|
22
|
-
class_eval "#{verb.downcase}('/') { '#{verb}' }"
|
23
|
-
class_eval "#{verb.downcase}('/route.json') { '#{verb} /route.json' }"
|
24
|
-
class_eval "#{verb.downcase}('/route/:id.json') { request.params[:id] }"
|
25
|
-
class_eval "#{verb.downcase}('/:name') { request.params[:name] }"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
Hobby::Verbs.each do |verb|
|
31
|
-
describe 'when the request matches a route' do
|
32
|
-
it "matches #{verb} ''" do
|
33
|
-
send verb.downcase, ''
|
34
|
-
assert last_response.ok?
|
35
|
-
assert_equal verb, last_response.body
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'matches #{verb} /' do
|
39
|
-
send verb.downcase, '/'
|
40
|
-
assert last_response.ok?
|
41
|
-
assert_equal verb, last_response.body
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'matches #{verb} /route.json' do
|
45
|
-
send verb.downcase, '/route.json'
|
46
|
-
assert last_response.ok?
|
47
|
-
assert_equal "#{verb} /route.json", last_response.body
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'matches #{verb} /route/:id.json' do
|
51
|
-
send verb.downcase, '/route/1.json'
|
52
|
-
assert last_response.ok?
|
53
|
-
assert_equal '1', last_response.body
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'matches #{verb} /:name' do
|
57
|
-
send verb.downcase, '/hobbit'
|
58
|
-
assert last_response.ok?
|
59
|
-
assert_equal 'hobbit', last_response.body
|
60
|
-
|
61
|
-
send verb.downcase, '/hello-hobbit'
|
62
|
-
assert last_response.ok?
|
63
|
-
assert_equal 'hello-hobbit', last_response.body
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe 'when the request not matches a route' do
|
68
|
-
it 'responds with 404 status code' do
|
69
|
-
send verb.downcase, '/not/found'
|
70
|
-
assert last_response.not_found?
|
71
|
-
assert_equal '', last_response.body
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
describe :map_app do
|
78
|
-
before do
|
79
|
-
mock_app do
|
80
|
-
map '/map' do
|
81
|
-
run Proc.new { |env| [200, {}, ['from map']] }
|
82
|
-
end
|
83
|
-
|
84
|
-
get('/') { 'hello world' }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'mounts an application to the rack stack' do
|
89
|
-
get '/map'
|
90
|
-
assert_equal 'from map', last_response.body
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
describe :use_app do
|
95
|
-
before do
|
96
|
-
mock_app do
|
97
|
-
middleware = Class.new do
|
98
|
-
def initialize(app = nil)
|
99
|
-
@app = app
|
100
|
-
end
|
101
|
-
|
102
|
-
def call(env)
|
103
|
-
request = Rack::Request.new(env)
|
104
|
-
@app.call(env) unless request.path_info == '/use'
|
105
|
-
[200, {}, 'from use']
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
use middleware
|
110
|
-
|
111
|
-
get('/') { 'hello world' }
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'adds a middleware to the rack stack' do
|
116
|
-
get '/use'
|
117
|
-
assert_equal 'from use', last_response.body
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
describe :halt_app do
|
122
|
-
before do
|
123
|
-
mock_app do
|
124
|
-
get '/halt' do
|
125
|
-
response.status = 501
|
126
|
-
halt response.finish
|
127
|
-
end
|
128
|
-
|
129
|
-
get '/halt_finished' do
|
130
|
-
halt [404, {}, ['Not found']]
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
it 'halts the execution with a response' do
|
136
|
-
get '/halt'
|
137
|
-
assert { last_response.status == 501 }
|
138
|
-
end
|
139
|
-
|
140
|
-
it 'halts the execution with a finished response' do
|
141
|
-
get '/halt_finished'
|
142
|
-
assert { last_response.status == 404 }
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
describe :router_app do
|
147
|
-
before do
|
148
|
-
mock_app do
|
149
|
-
router do
|
150
|
-
Class.new do
|
151
|
-
def add_route(*)
|
152
|
-
end
|
153
|
-
|
154
|
-
def route_for _request
|
155
|
-
Proc.new { 'for any route' }
|
156
|
-
end
|
157
|
-
end.new
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
it 'returns for any route' do
|
163
|
-
get '/'
|
164
|
-
assert { last_response.body == 'for any route' }
|
165
|
-
|
166
|
-
get '/some-other-route'
|
167
|
-
assert { last_response.body == 'for any route' }
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
describe :custom_members do
|
172
|
-
before do
|
173
|
-
mock_app do
|
174
|
-
builder { Rack::Builder.new }
|
175
|
-
Request = Rack::Request
|
176
|
-
Response = Rack::Response
|
177
|
-
|
178
|
-
get '/' do
|
179
|
-
'it works'
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
it 'works' do
|
185
|
-
get '/'
|
186
|
-
assert { last_response.body == 'it works' }
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
data/test/test_router.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
require_relative 'helper'
|
2
|
-
|
3
|
-
def request_to path, request_method: 'GET'
|
4
|
-
Hobby::Request.new Rack::MockRequest.env_for "http://example.com:8080/#{path}", method: request_method
|
5
|
-
end
|
6
|
-
|
7
|
-
describe Hobby::Router do
|
8
|
-
before do
|
9
|
-
@router = Hobby::Router.new
|
10
|
-
@route = -> { :wrapped }
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'works' do
|
14
|
-
@router.add_route 'GET', '/ololo', &@route
|
15
|
-
|
16
|
-
route = @router.route_for request_to 'ololo'
|
17
|
-
assert { route.to_proc.call == :wrapped }
|
18
|
-
|
19
|
-
route = @router.route_for request_to 'ololo2'
|
20
|
-
assert { route.nil? }
|
21
|
-
|
22
|
-
route = @router.route_for request_to 'ololo', request_method: 'POST'
|
23
|
-
assert { route.nil? }
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'with .' do
|
27
|
-
@router.add_route 'GET', '/route.json', &@route
|
28
|
-
|
29
|
-
route = @router.route_for request_to 'route.json'
|
30
|
-
assert { route.to_proc.call == :wrapped }
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'with -' do
|
34
|
-
@router.add_route 'GET', '/hello-world', &@route
|
35
|
-
|
36
|
-
route = @router.route_for request_to 'hello-world'
|
37
|
-
assert { route.to_proc.call == :wrapped }
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'with params' do
|
41
|
-
@router
|
42
|
-
.add_route 'GET', '/hello/:name' do :first end
|
43
|
-
.add_route 'GET', '/say/:something/to/:someone' do :second end
|
44
|
-
|
45
|
-
request = request_to 'hello/ololo'
|
46
|
-
route = @router.route_for request
|
47
|
-
assert { route.to_proc.call == :first }
|
48
|
-
assert { request.params[:name] == 'ololo'}
|
49
|
-
|
50
|
-
request = request_to 'say/nothing/to/no_one'
|
51
|
-
route = @router.route_for request
|
52
|
-
assert { route.to_proc.call == :second }
|
53
|
-
assert { request.params[:something] == 'nothing'}
|
54
|
-
assert { request.params[:someone] == 'no_one'}
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'with . and params' do
|
58
|
-
@router.add_route 'GET', '/route/:id.json', &@route
|
59
|
-
|
60
|
-
request = request_to 'route/42.json'
|
61
|
-
route = @router.route_for request
|
62
|
-
assert { route.to_proc.call == :wrapped }
|
63
|
-
assert { request.params[:id] == '42' }
|
64
|
-
end
|
65
|
-
end
|