hobby 0.0.11 → 0.1.3
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 +5 -5
- data/.travis.yml +1 -0
- data/Gemfile +2 -2
- data/hobby.gemspec +2 -2
- data/lib/hobby.rb +13 -36
- data/lib/hobby/helpers.rb +50 -0
- data/lib/hobby/router.rb +30 -0
- data/lib/hobby/router/builder.rb +25 -0
- data/readme.adoc +26 -36
- data/spec/app_spec.rb +119 -0
- data/spec/apps/Map.rb +3 -3
- data/spec/apps/MapInsideInitialize.rb +13 -0
- data/spec/apps/Nested.rb +11 -13
- data/spec/apps/OneRouteRouter.rb +8 -0
- data/spec/apps/ScriptName.rb +12 -0
- data/spec/apps/Status.rb +4 -0
- data/spec/apps/UnshareableRouterMaps.rb +29 -0
- data/spec/apps/UnshareableRouterUses.rb +16 -0
- data/spec/apps/UseInsideInitialize.rb +7 -0
- data/spec/helper.rb +5 -5
- data/spec/mutant_patches.rb +27 -0
- data/spec/router_matchers.rb +0 -4
- metadata +20 -7
- data/CHANGELOG.md +0 -53
- data/Rakefile +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b95d5ad065324e903deb97971e990916ec1011e2353f50d0ab4c30f69607cecc
|
|
4
|
+
data.tar.gz: 0e2a747860c4d153fc418fbd1b1ed599847e419f80db6c31690c02b2097ea146
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dbff581d827ea9eadd548378730262c9f4e0b9941cc25954c62455e90032698e6364e82dfc2e25ab7ab79efb919a3b2f2bfa34580cd9f853376b993821e5eedf
|
|
7
|
+
data.tar.gz: c030f1a411b5db33c080eb0b68c722ac9c362d5212fc3f60b88f61df007f47baa53a73e0e3b5d2e4e920331415095cec74f69cf5225fe3f4a73b80bc3496cdc3
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/hobby.gemspec
CHANGED
|
@@ -4,10 +4,10 @@ $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.
|
|
7
|
+
spec.version = '0.1.3'
|
|
8
8
|
spec.authors = ['Anatoly Chernow']
|
|
9
9
|
spec.email = ['chertoly@gmail.com']
|
|
10
|
-
spec.summary = %q{A
|
|
10
|
+
spec.summary = %q{A professional way to create Rack applications.}
|
|
11
11
|
spec.homepage = 'https://github.com/ch1c0t/hobby'
|
|
12
12
|
spec.license = 'MIT'
|
|
13
13
|
|
data/lib/hobby.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'rack'
|
|
|
2
2
|
require 'forwardable'
|
|
3
3
|
|
|
4
4
|
require 'hobby/router'
|
|
5
|
+
require 'hobby/helpers'
|
|
5
6
|
|
|
6
7
|
module Hobby
|
|
7
8
|
App = Hobby # to stay compatible with old code
|
|
@@ -9,20 +10,19 @@ module Hobby
|
|
|
9
10
|
|
|
10
11
|
def self.included app
|
|
11
12
|
app.extend Singleton
|
|
12
|
-
app.
|
|
13
|
+
app.router = Router.new
|
|
13
14
|
app.request, app.response = Rack::Request, Rack::Response
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
module Singleton
|
|
17
|
-
attr_accessor :
|
|
18
|
+
attr_accessor :router, :request, :response
|
|
18
19
|
|
|
19
20
|
def new (*)
|
|
20
|
-
|
|
21
|
-
builder.to_app
|
|
21
|
+
super.router.to_rack_app
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
extend Forwardable
|
|
25
|
-
delegate [:map, :use] => :
|
|
25
|
+
delegate [:map, :use] => :router
|
|
26
26
|
|
|
27
27
|
VERBS.each do |verb|
|
|
28
28
|
define_method verb.downcase do |path = '/', &action|
|
|
@@ -31,6 +31,8 @@ module Hobby
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
include Helpers
|
|
35
|
+
|
|
34
36
|
def call env
|
|
35
37
|
dup.handle env
|
|
36
38
|
end
|
|
@@ -38,40 +40,15 @@ module Hobby
|
|
|
38
40
|
protected
|
|
39
41
|
def handle env
|
|
40
42
|
catch :halt do
|
|
41
|
-
@route =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
response.write body
|
|
45
|
-
|
|
46
|
-
response
|
|
43
|
+
@route = router.route_for (@env = env)
|
|
44
|
+
fill_body
|
|
45
|
+
response.to_a
|
|
47
46
|
end
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
private
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@request ||= self.class.request.new env
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def response
|
|
58
|
-
@response ||= self.class.response.new
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def my
|
|
62
|
-
route.params
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def halt
|
|
66
|
-
throw :halt, response
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def not_found
|
|
70
|
-
response.status = 404
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def content_type type
|
|
74
|
-
mime_type = Rack::Mime::MIME_TYPES.fetch ".#{type}"
|
|
75
|
-
response.add_header 'Content-Type', mime_type
|
|
50
|
+
def fill_body
|
|
51
|
+
body = route ? (instance_exec &route) : not_found
|
|
52
|
+
response.write body
|
|
76
53
|
end
|
|
77
54
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Hobby
|
|
2
|
+
module Helpers
|
|
3
|
+
extend Forwardable
|
|
4
|
+
|
|
5
|
+
attr_reader :env, :route
|
|
6
|
+
|
|
7
|
+
def router
|
|
8
|
+
@router ||= begin
|
|
9
|
+
router = self.class.router.clone
|
|
10
|
+
router.app = self
|
|
11
|
+
router
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
delegate [:map, :use] => :router
|
|
16
|
+
|
|
17
|
+
def request
|
|
18
|
+
@request ||= self.class.request.new env
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def response
|
|
22
|
+
@response ||= self.class.response.new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def my
|
|
26
|
+
route.params
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def halt
|
|
30
|
+
throw :halt, response.to_a
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def not_found
|
|
34
|
+
response.status = 404
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def content_type type
|
|
38
|
+
mime_type = Rack::Mime::MIME_TYPES.fetch ".#{type}"
|
|
39
|
+
response.add_header 'Content-Type', mime_type
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def status status
|
|
43
|
+
response.status = status
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def script_name
|
|
47
|
+
env.fetch 'SCRIPT_NAME'
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/hobby/router.rb
CHANGED
|
@@ -2,6 +2,11 @@ module Hobby
|
|
|
2
2
|
class Router
|
|
3
3
|
def initialize
|
|
4
4
|
@routes = Routes.new
|
|
5
|
+
@uses, @maps = [], []
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def initialize_copy _router
|
|
9
|
+
@uses, @maps = @uses.dup, @maps.dup
|
|
5
10
|
end
|
|
6
11
|
|
|
7
12
|
def add_route verb, path, &action
|
|
@@ -17,8 +22,33 @@ module Hobby
|
|
|
17
22
|
route, params = @routes["#{env['REQUEST_METHOD']}#{env['PATH_INFO']}"]
|
|
18
23
|
params ? route.with_params(params) : route
|
|
19
24
|
end
|
|
25
|
+
|
|
26
|
+
def use *all
|
|
27
|
+
@uses << all
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def map path, app = nil, &block
|
|
31
|
+
@maps << Builder::Map.new(path, app, &block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attr_accessor :app
|
|
35
|
+
|
|
36
|
+
def to_rack_app
|
|
37
|
+
builder = create_builder
|
|
38
|
+
builder.run app
|
|
39
|
+
builder.to_app
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
def create_builder
|
|
44
|
+
builder = Builder.new
|
|
45
|
+
@uses.each { |all| builder.add_use *all }
|
|
46
|
+
@maps.each { |map| builder.add_map map }
|
|
47
|
+
builder
|
|
48
|
+
end
|
|
20
49
|
end
|
|
21
50
|
end
|
|
22
51
|
|
|
52
|
+
require 'hobby/router/builder'
|
|
23
53
|
require 'hobby/router/routes'
|
|
24
54
|
require 'hobby/router/route'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Hobby
|
|
2
|
+
class Router
|
|
3
|
+
class Builder < Rack::Builder
|
|
4
|
+
# To work around
|
|
5
|
+
# https://github.com/mbj/mutant#the-crash--stuck-problem-mri
|
|
6
|
+
alias add_use use
|
|
7
|
+
alias add_map2 map
|
|
8
|
+
|
|
9
|
+
def add_map map
|
|
10
|
+
if map.app
|
|
11
|
+
add_map2 map.path do run map.app end
|
|
12
|
+
else
|
|
13
|
+
add_map2 map.path, &map.block
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Map
|
|
18
|
+
attr_reader :path, :app, :block
|
|
19
|
+
def initialize path, app, &block
|
|
20
|
+
@path, @app, @block = path, app, block
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/readme.adoc
CHANGED
|
@@ -27,8 +27,16 @@ $ gem install hobby
|
|
|
27
27
|
[[introduction]]
|
|
28
28
|
== Introduction
|
|
29
29
|
|
|
30
|
-
Hobby
|
|
31
|
-
|
|
30
|
+
Hobby provides a Ruby DSL to create web applications. It is well suited both for standalone and inside-Rails use.
|
|
31
|
+
|
|
32
|
+
The DSL looks a lot like Sinatra, but Hobby applications are
|
|
33
|
+
|
|
34
|
+
* more reusable
|
|
35
|
+
* more reliable
|
|
36
|
+
+
|
|
37
|
+
The API and the codebase are much smaller and fully covered with specs. 100% mutation coverage is constantly maintained.
|
|
38
|
+
* https://github.com/luislavena/bench-micro#requestssec[more performant]
|
|
39
|
+
* https://github.com/luislavena/bench-micro#memory-allocationrequest[consume less resources]
|
|
32
40
|
|
|
33
41
|
To create a Hobby application, you create a class and include `Hobby` in it.
|
|
34
42
|
For example:
|
|
@@ -40,9 +48,9 @@ require 'hobby'
|
|
|
40
48
|
class C
|
|
41
49
|
include Hobby
|
|
42
50
|
|
|
43
|
-
get
|
|
51
|
+
get "/hello" do
|
|
44
52
|
"Hello, world."
|
|
45
|
-
|
|
53
|
+
end
|
|
46
54
|
end
|
|
47
55
|
----
|
|
48
56
|
|
|
@@ -74,9 +82,9 @@ class C
|
|
|
74
82
|
@name = name
|
|
75
83
|
end
|
|
76
84
|
|
|
77
|
-
get
|
|
85
|
+
get "/hello" do
|
|
78
86
|
"Hello, #{@name}."
|
|
79
|
-
|
|
87
|
+
end
|
|
80
88
|
end
|
|
81
89
|
----
|
|
82
90
|
|
|
@@ -95,9 +103,9 @@ class C
|
|
|
95
103
|
@name.upcase
|
|
96
104
|
end
|
|
97
105
|
|
|
98
|
-
get
|
|
106
|
+
get "/hello" do
|
|
99
107
|
"Hello, #{name}."
|
|
100
|
-
|
|
108
|
+
end
|
|
101
109
|
end
|
|
102
110
|
----
|
|
103
111
|
|
|
@@ -133,29 +141,12 @@ For common HTTP verbs, Hobby provides the route definers(methods named according
|
|
|
133
141
|
class App
|
|
134
142
|
include Hobby
|
|
135
143
|
|
|
136
|
-
get '
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
put '/' do
|
|
145
|
-
# ...
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
patch '/' do
|
|
149
|
-
# ...
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
delete '/' do
|
|
153
|
-
# ...
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
options '/' do
|
|
157
|
-
# ...
|
|
158
|
-
end
|
|
144
|
+
get { 'Some string.' }
|
|
145
|
+
post { 'Some string.' }
|
|
146
|
+
put { 'Some string.' }
|
|
147
|
+
patch { 'Some string.' }
|
|
148
|
+
delete { 'Some string.' }
|
|
149
|
+
# TODO: find a good example for `options`
|
|
159
150
|
end
|
|
160
151
|
----
|
|
161
152
|
|
|
@@ -285,8 +276,8 @@ to '/anatoly' and '/patricio' routes:
|
|
|
285
276
|
class App
|
|
286
277
|
include Hobby
|
|
287
278
|
|
|
288
|
-
map
|
|
289
|
-
map
|
|
279
|
+
map '/anatoly', C.new('Anatoly')
|
|
280
|
+
map '/patricio', C.new('Patricio')
|
|
290
281
|
|
|
291
282
|
get '/' do
|
|
292
283
|
'Mapping app.'
|
|
@@ -327,7 +318,6 @@ Many components of an application can be customized or replaced.
|
|
|
327
318
|
class App
|
|
328
319
|
include Hobby
|
|
329
320
|
|
|
330
|
-
self.builder = custom_builder
|
|
331
321
|
self.router = custom_router
|
|
332
322
|
self.request = custom_request
|
|
333
323
|
self.response = custom_response
|
|
@@ -346,9 +336,9 @@ To run the specs:
|
|
|
346
336
|
bundle exec rspec
|
|
347
337
|
----
|
|
348
338
|
|
|
349
|
-
To perform
|
|
339
|
+
To perform mutation analysis:
|
|
350
340
|
|
|
351
341
|
[source,bash]
|
|
352
342
|
----
|
|
353
|
-
bundle exec mutant --use rspec 'Hobby*'
|
|
343
|
+
bundle exec mutant --use rspec 'Hobby*' --include lib --require hobby
|
|
354
344
|
----
|
data/spec/app_spec.rb
CHANGED
|
@@ -75,6 +75,17 @@ describe Hobby::App do
|
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
describe '#router' do
|
|
79
|
+
it 'gives a distinct router to each app' do
|
|
80
|
+
app = Class.new { include Hobby }
|
|
81
|
+
|
|
82
|
+
first_instance = app.new
|
|
83
|
+
second_instance = app.new
|
|
84
|
+
|
|
85
|
+
assert { not first_instance.router.equal? second_instance.router }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
78
89
|
describe :integration do
|
|
79
90
|
before do
|
|
80
91
|
described_class.app = build_app described_class
|
|
@@ -89,6 +100,51 @@ describe Hobby::App do
|
|
|
89
100
|
get '/map'
|
|
90
101
|
assert { last_response.body == 'from map' }
|
|
91
102
|
end
|
|
103
|
+
|
|
104
|
+
it 'mounts an application to the rack stack with old deprecated syntax' do
|
|
105
|
+
get '/deprecated_map'
|
|
106
|
+
assert { last_response.body == 'from deprecated map' }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
describe MapInsideInitialize do
|
|
111
|
+
describe 'without any passed arguments' do
|
|
112
|
+
def app
|
|
113
|
+
described_class.app.new
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it do
|
|
117
|
+
get '/'
|
|
118
|
+
assert { last_response.body == 'hello world' }
|
|
119
|
+
get '/first_map'
|
|
120
|
+
assert { last_response.body == 'first mapapp' }
|
|
121
|
+
get '/second_map'
|
|
122
|
+
assert { last_response.body == 'second mapapp' }
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe 'with passed routes' do
|
|
127
|
+
def app
|
|
128
|
+
some_app = Class.new {
|
|
129
|
+
include Hobby
|
|
130
|
+
get { 'Some string.' }
|
|
131
|
+
}
|
|
132
|
+
routes = { '/third_map' => some_app.new }
|
|
133
|
+
described_class.app.new routes
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it do
|
|
137
|
+
get '/'
|
|
138
|
+
assert { last_response.body == 'hello world' }
|
|
139
|
+
get '/first_map'
|
|
140
|
+
assert { last_response.body == 'first mapapp' }
|
|
141
|
+
get '/second_map'
|
|
142
|
+
assert { last_response.body == 'second mapapp' }
|
|
143
|
+
|
|
144
|
+
get '/third_map'
|
|
145
|
+
assert { last_response.body == 'Some string.' }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
92
148
|
end
|
|
93
149
|
|
|
94
150
|
describe Use do
|
|
@@ -98,6 +154,13 @@ describe Hobby::App do
|
|
|
98
154
|
end
|
|
99
155
|
end
|
|
100
156
|
|
|
157
|
+
describe UseInsideInitialize do
|
|
158
|
+
it do
|
|
159
|
+
get '/'
|
|
160
|
+
assert { last_response.content_type == 'application/json' }
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
101
164
|
describe WithoutPath do
|
|
102
165
|
it 'is accessible as /' do
|
|
103
166
|
get '/'
|
|
@@ -163,5 +226,61 @@ describe Hobby::App do
|
|
|
163
226
|
assert { last_response.headers['Content-Type'] == 'application/javascript' }
|
|
164
227
|
end
|
|
165
228
|
end
|
|
229
|
+
|
|
230
|
+
describe Status do
|
|
231
|
+
it do
|
|
232
|
+
get '/'
|
|
233
|
+
assert { last_response.status == 201 }
|
|
234
|
+
assert { last_response.body == 'Created.' }
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
describe UnshareableRouterMaps do
|
|
239
|
+
it do
|
|
240
|
+
get '/1/first'
|
|
241
|
+
assert { last_response.body == 'The name is A.' }
|
|
242
|
+
get '/1/second'
|
|
243
|
+
assert { last_response.body == 'The name is B.' }
|
|
244
|
+
get '/1/third'
|
|
245
|
+
assert { last_response.body == '404' }
|
|
246
|
+
|
|
247
|
+
get '/2/first'
|
|
248
|
+
assert { last_response.body == 'The name is A.' }
|
|
249
|
+
get '/2/second'
|
|
250
|
+
assert { last_response.body == 'The name is B.' }
|
|
251
|
+
get '/2/third'
|
|
252
|
+
assert { last_response.body == 'The name is C.' }
|
|
253
|
+
|
|
254
|
+
get '/3/first'
|
|
255
|
+
assert { last_response.body == 'The name is A.' }
|
|
256
|
+
get '/3/second'
|
|
257
|
+
assert { last_response.body == 'The name is B.' }
|
|
258
|
+
get '/3/third'
|
|
259
|
+
assert { last_response.body == '404' }
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
describe UnshareableRouterUses do
|
|
264
|
+
it do
|
|
265
|
+
get '/1'
|
|
266
|
+
assert { last_response.content_type == 'application/html' }
|
|
267
|
+
|
|
268
|
+
get '/2'
|
|
269
|
+
assert { last_response.content_type == 'application/json' }
|
|
270
|
+
|
|
271
|
+
get '/3'
|
|
272
|
+
assert { last_response.content_type == 'application/html' }
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
describe ScriptName do
|
|
277
|
+
it do
|
|
278
|
+
get '/'
|
|
279
|
+
assert { last_response.body == '' }
|
|
280
|
+
|
|
281
|
+
get '/some/path'
|
|
282
|
+
assert { last_response.body == '/some/path' }
|
|
283
|
+
end
|
|
284
|
+
end
|
|
166
285
|
end
|
|
167
286
|
end
|
data/spec/apps/Map.rb
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
first_app = Proc.new { |env| [200, {}, ['first mapapp']] }
|
|
2
|
+
second_app = Proc.new { |env| [200, {}, ['second mapapp']] }
|
|
3
|
+
|
|
4
|
+
map '/first_map', first_app
|
|
5
|
+
map '/second_map', second_app
|
|
6
|
+
|
|
7
|
+
get('/') { 'hello world' }
|
|
8
|
+
|
|
9
|
+
def initialize hash = {}
|
|
10
|
+
hash.each do |route, app|
|
|
11
|
+
map route, app
|
|
12
|
+
end
|
|
13
|
+
end
|
data/spec/apps/Nested.rb
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
include Hobby::App
|
|
1
|
+
nested_app = Class.new do
|
|
2
|
+
include Hobby::App
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
get do
|
|
12
|
-
"#{@a}:#{@b}:#{@c}"
|
|
13
|
-
end
|
|
4
|
+
def initialize first, second
|
|
5
|
+
@a = first
|
|
6
|
+
@b = second
|
|
7
|
+
@c = yield
|
|
14
8
|
end
|
|
15
9
|
|
|
16
|
-
|
|
10
|
+
get do
|
|
11
|
+
"#{@a}:#{@b}:#{@c}"
|
|
12
|
+
end
|
|
17
13
|
end
|
|
14
|
+
|
|
15
|
+
map '/nested', nested_app.new(:a, :b) { :c }
|
data/spec/apps/OneRouteRouter.rb
CHANGED
data/spec/apps/Status.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
mapped_app = Class.new do
|
|
2
|
+
include Hobby
|
|
3
|
+
|
|
4
|
+
def initialize name
|
|
5
|
+
@name = name
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
get { "The name is #{@name}." }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
mapping_app = Class.new do
|
|
12
|
+
include Hobby
|
|
13
|
+
|
|
14
|
+
map '/first', mapped_app.new('A')
|
|
15
|
+
map '/second', mapped_app.new('B')
|
|
16
|
+
|
|
17
|
+
def initialize routes = {}
|
|
18
|
+
routes.each do |route, app|
|
|
19
|
+
map route, app
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
map '/1', mapping_app.new
|
|
25
|
+
|
|
26
|
+
routes = { '/third' => mapped_app.new('C') }
|
|
27
|
+
map '/2', mapping_app.new(routes)
|
|
28
|
+
|
|
29
|
+
map '/3', mapping_app.new
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
app = Class.new do
|
|
4
|
+
include Hobby
|
|
5
|
+
get { 'hello world'.to_json }
|
|
6
|
+
|
|
7
|
+
use Rack::ContentType, 'application/html'
|
|
8
|
+
|
|
9
|
+
def initialize middleware_with_arguments = nil
|
|
10
|
+
use *middleware_with_arguments if middleware_with_arguments
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
map '/1', app.new
|
|
15
|
+
map '/2', app.new([Rack::ContentType, 'application/json'])
|
|
16
|
+
map '/3', app.new
|
data/spec/helper.rb
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
require 'devtools/spec_helper'
|
|
2
2
|
|
|
3
3
|
require 'hobby'
|
|
4
|
+
require_relative 'mutant_patches' if defined? Mutant
|
|
4
5
|
|
|
5
6
|
require 'minitest'
|
|
6
7
|
require 'minitest-power_assert'
|
|
7
8
|
Minitest::Assertions.prepend Minitest::PowerAssert::Assertions
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
integration.all_tests
|
|
13
|
-
end
|
|
10
|
+
module EnvFor
|
|
11
|
+
def env_for path, verb = 'GET'
|
|
12
|
+
{'REQUEST_METHOD' => verb, 'PATH_INFO' => path }
|
|
14
13
|
end
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
RSpec.configure do |config|
|
|
18
17
|
config.expect_with :rspec, :minitest
|
|
18
|
+
config.include EnvFor
|
|
19
19
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Run all the specs for any subject.
|
|
2
|
+
class Mutant::Selector::Expression
|
|
3
|
+
def call _subject
|
|
4
|
+
integration.all_tests
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Do not silence stdout and stderr of the running mutation.
|
|
9
|
+
class Mutant::Isolation::Fork
|
|
10
|
+
def result
|
|
11
|
+
yield
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Print the source of the current mutation.
|
|
16
|
+
class Mutant::Loader
|
|
17
|
+
def call
|
|
18
|
+
source = Unparser.unparse node
|
|
19
|
+
|
|
20
|
+
puts <<~S
|
|
21
|
+
Current mutation:
|
|
22
|
+
#{source}
|
|
23
|
+
S
|
|
24
|
+
|
|
25
|
+
kernel.eval source, binding, subject.source_path.to_s, subject.source_line
|
|
26
|
+
end
|
|
27
|
+
end
|
data/spec/router_matchers.rb
CHANGED
|
@@ -2,10 +2,6 @@ module RouterMatchers
|
|
|
2
2
|
extend self
|
|
3
3
|
SOME_ROUTE = ->{:some_route}
|
|
4
4
|
|
|
5
|
-
def env_for path, verb = 'GET'
|
|
6
|
-
{'REQUEST_METHOD' => verb, 'PATH_INFO' => path }
|
|
7
|
-
end
|
|
8
|
-
|
|
9
5
|
def add_routes *routes
|
|
10
6
|
routes.each { |route| subject.add_route 'GET', route, &SOME_ROUTE }
|
|
11
7
|
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.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anatoly Chernow
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-03-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -33,13 +33,13 @@ extra_rdoc_files: []
|
|
|
33
33
|
files:
|
|
34
34
|
- ".gitignore"
|
|
35
35
|
- ".travis.yml"
|
|
36
|
-
- CHANGELOG.md
|
|
37
36
|
- Gemfile
|
|
38
37
|
- LICENSE
|
|
39
|
-
- Rakefile
|
|
40
38
|
- hobby.gemspec
|
|
41
39
|
- lib/hobby.rb
|
|
40
|
+
- lib/hobby/helpers.rb
|
|
42
41
|
- lib/hobby/router.rb
|
|
42
|
+
- lib/hobby/router/builder.rb
|
|
43
43
|
- lib/hobby/router/route.rb
|
|
44
44
|
- lib/hobby/router/routes.rb
|
|
45
45
|
- readme.adoc
|
|
@@ -50,11 +50,18 @@ files:
|
|
|
50
50
|
- spec/apps/Halting.rb
|
|
51
51
|
- spec/apps/Main.rb
|
|
52
52
|
- spec/apps/Map.rb
|
|
53
|
+
- spec/apps/MapInsideInitialize.rb
|
|
53
54
|
- spec/apps/Nested.rb
|
|
54
55
|
- spec/apps/OneRouteRouter.rb
|
|
56
|
+
- spec/apps/ScriptName.rb
|
|
57
|
+
- spec/apps/Status.rb
|
|
58
|
+
- spec/apps/UnshareableRouterMaps.rb
|
|
59
|
+
- spec/apps/UnshareableRouterUses.rb
|
|
55
60
|
- spec/apps/Use.rb
|
|
61
|
+
- spec/apps/UseInsideInitialize.rb
|
|
56
62
|
- spec/apps/WithoutPath.rb
|
|
57
63
|
- spec/helper.rb
|
|
64
|
+
- spec/mutant_patches.rb
|
|
58
65
|
- spec/router_matchers.rb
|
|
59
66
|
- spec/router_spec.rb
|
|
60
67
|
homepage: https://github.com/ch1c0t/hobby
|
|
@@ -76,11 +83,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
76
83
|
- !ruby/object:Gem::Version
|
|
77
84
|
version: '0'
|
|
78
85
|
requirements: []
|
|
79
|
-
|
|
80
|
-
rubygems_version: 2.6.11
|
|
86
|
+
rubygems_version: 3.0.3
|
|
81
87
|
signing_key:
|
|
82
88
|
specification_version: 4
|
|
83
|
-
summary: A
|
|
89
|
+
summary: A professional way to create Rack applications.
|
|
84
90
|
test_files:
|
|
85
91
|
- spec/app_spec.rb
|
|
86
92
|
- spec/apps/ContentType.rb
|
|
@@ -89,10 +95,17 @@ test_files:
|
|
|
89
95
|
- spec/apps/Halting.rb
|
|
90
96
|
- spec/apps/Main.rb
|
|
91
97
|
- spec/apps/Map.rb
|
|
98
|
+
- spec/apps/MapInsideInitialize.rb
|
|
92
99
|
- spec/apps/Nested.rb
|
|
93
100
|
- spec/apps/OneRouteRouter.rb
|
|
101
|
+
- spec/apps/ScriptName.rb
|
|
102
|
+
- spec/apps/Status.rb
|
|
103
|
+
- spec/apps/UnshareableRouterMaps.rb
|
|
104
|
+
- spec/apps/UnshareableRouterUses.rb
|
|
94
105
|
- spec/apps/Use.rb
|
|
106
|
+
- spec/apps/UseInsideInitialize.rb
|
|
95
107
|
- spec/apps/WithoutPath.rb
|
|
96
108
|
- spec/helper.rb
|
|
109
|
+
- spec/mutant_patches.rb
|
|
97
110
|
- spec/router_matchers.rb
|
|
98
111
|
- spec/router_spec.rb
|
data/CHANGELOG.md
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# 0.6.0
|
|
2
|
-
|
|
3
|
-
* Change the implementation of `Hobbit::Base#halt`. This new implementation is
|
|
4
|
-
more rack compliant.
|
|
5
|
-
* Test hobbit with [oktobertest](https://github.com/patriciomacadden/oktobertest)
|
|
6
|
-
instead of minitest (Because reasons!).
|
|
7
|
-
|
|
8
|
-
# 0.5.1 (Unreleased)
|
|
9
|
-
|
|
10
|
-
* A class is an object too, so allow to `run` classes.
|
|
11
|
-
* Add `Hobbit::Request`, which sets the path info to `/` if its empty (instead
|
|
12
|
-
of doing that on the call method).
|
|
13
|
-
|
|
14
|
-
# 0.5.0
|
|
15
|
-
|
|
16
|
-
* Refactor `Hobbit::Base#halt`. It now sets the status, merges the headers and
|
|
17
|
-
writes the body (using `Hobbit::Response#write`) when given a fixnum, a hash or
|
|
18
|
-
a string.
|
|
19
|
-
* `Hobbit::Response` headers and body are not accessors anymore. This is
|
|
20
|
-
because when you set the body directly, the `Content-Length` is not calculated
|
|
21
|
-
(it's calculated on `#write`).
|
|
22
|
-
|
|
23
|
-
# 0.4.4
|
|
24
|
-
|
|
25
|
-
* Refactor `Hobbit::Response`.
|
|
26
|
-
|
|
27
|
-
# 0.4.3
|
|
28
|
-
|
|
29
|
-
* Calculate the `Content-Length` of a `Hobbit::Response` using `#bytesize`
|
|
30
|
-
instead of `#size`.
|
|
31
|
-
|
|
32
|
-
# 0.4.2
|
|
33
|
-
|
|
34
|
-
* Add `Hobbit::Response#redirect`, that was missing since `Hobbit::Response`
|
|
35
|
-
isn't a `Rack::Response` subclass.
|
|
36
|
-
|
|
37
|
-
# 0.4.1
|
|
38
|
-
|
|
39
|
-
* `Hobbit::Response` now returns the `Content-Length` header as a string.
|
|
40
|
-
|
|
41
|
-
# 0.4.0
|
|
42
|
-
|
|
43
|
-
* Add halt method.
|
|
44
|
-
|
|
45
|
-
# 0.3.1
|
|
46
|
-
|
|
47
|
-
* Remove unused `attr_accessor` (`:length`) from `Hobbit::Response`.
|
|
48
|
-
|
|
49
|
-
# 0.3.0
|
|
50
|
-
|
|
51
|
-
* `Hobbit::Response` is no longer a subclass of `Rack::Response`.
|
|
52
|
-
* Forward `#map` and `#use` methods to `Rack::Builder` instead of define these
|
|
53
|
-
methods.
|
data/Rakefile
DELETED