hobby 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8c721c77a7b912fa113e730de3ff58cdf8866e06
4
+ data.tar.gz: 4590a7005e8597a277cae934955b30b0a981b1b4
5
+ SHA512:
6
+ metadata.gz: 2bb9aa2ee633d3d06c85a7e41dd1a1b59bb4d870c48de2b351846b7e38566e1f17731bb94894bcec50e917e1fbcfab51d7784d6175ae89db37476918373d3403
7
+ data.tar.gz: efa3483ccf3ea59cc6e6e16dd4280713eaef4e7a04b1ac1cd66c07e42344cd42efb6a52ddabab7cf4ed2fee032926264fb6f7224f8750e555976309d29665364
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ ---
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.2
5
+ - ruby-head
6
+ addons:
7
+ code_climate:
8
+ repo_token:
9
+ secure: "bogVqPmJ2KlI2XK3Ln9Ki8vG08WTvKbKfvYm4ErD1A+2t3vg61utFKxJS8R3P5QKuDDIIX98eQC7r+gPeGEYwT+tDDFMnBsGQZcaeXJCYZQ3qLN36GpnPmpeZ3tTZwop5aT1AmMCF0IllDWYCjcWzeEZan23Eqt5YOyzTGAnr4g="
data/CHANGELOG.md ADDED
@@ -0,0 +1,53 @@
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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Patricio Mac Adden
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,378 @@
1
+ # Hobbit [![Build Status](http://img.shields.io/travis/patriciomacadden/hobbit.svg)](https://travis-ci.org/patriciomacadden/hobbit) [![Code Climate](http://img.shields.io/codeclimate/github/patriciomacadden/hobbit.svg)](https://codeclimate.com/github/patriciomacadden/hobbit) [![Code Climate Coverage](http://img.shields.io/codeclimate/coverage/github/patriciomacadden/hobbit.svg)](https://codeclimate.com/github/patriciomacadden/hobbit) [![Dependency Status](http://img.shields.io/gemnasium/patriciomacadden/hobbit.svg)](https://gemnasium.com/patriciomacadden/hobbit) [![Gem Version](http://img.shields.io/gem/v/hobbit.svg)](http://badge.fury.io/rb/hobbit)
2
+
3
+ A minimalistic microframework built on top of [Rack](http://rack.github.io/).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'hobbit'
11
+ # or this if you want to use hobbit master
12
+ # gem 'hobbit', github: 'patriciomacadden/hobbit'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ $ gem install hobbit
25
+ ```
26
+
27
+ ## Features
28
+
29
+ * DSL inspired by [Sinatra](http://www.sinatrarb.com/).
30
+ * [Speed](https://github.com/luislavena/bench-micro).
31
+ * Extensible with standard ruby classes and modules, with no extra logic. See
32
+ [hobbit-contrib](https://github.com/patriciomacadden/hobbit-contrib).
33
+ * Zero configuration.
34
+
35
+ ## Philosophy
36
+
37
+ * [Don't repeat yourself](http://en.wikipedia.org/wiki/Don't_repeat_yourself)
38
+ * Encourages the understanding and use of [Rack](http://rack.github.io/) and
39
+ its extensions instead of providing such functionality.
40
+
41
+ ## Usage
42
+
43
+ Hobbit applications are just instances of classes that inherits from
44
+ `Hobbit::Base`, which complies the
45
+ [Rack SPEC](http://rubydoc.info/github/rack/rack/master/file/SPEC).
46
+
47
+ ### Hello World example
48
+
49
+ Create a file called `app.rb`:
50
+
51
+ ```ruby
52
+ require 'hobbit'
53
+
54
+ class App < Hobbit::Base
55
+ get '/' do
56
+ 'Hello World!'
57
+ end
58
+ end
59
+ ```
60
+
61
+ Create a `config.ru` file:
62
+
63
+ ```ruby
64
+ require './app'
65
+
66
+ run App.new # or just `run App`
67
+ ```
68
+
69
+ Run it with `rackup`:
70
+
71
+ ```bash
72
+ $ rackup
73
+ ```
74
+
75
+ View your app at [http://localhost:9292](http://localhost:9292).
76
+
77
+ ### Routes
78
+
79
+ Every route is composed of a verb, a path (optional) and a block. When an
80
+ incoming request matches a route, the block is executed and a response is sent
81
+ back to the client. The return value of the block will be the `body` of the
82
+ response. The `headers` and `status code` of the response will be calculated by
83
+ `Hobbit::Response`, but you could modify it anyway you want it.
84
+
85
+ See an example:
86
+
87
+ ```ruby
88
+ class App < Hobbit::Base
89
+ get '/' do
90
+ # ...
91
+ end
92
+
93
+ post '/' do
94
+ # ...
95
+ end
96
+
97
+ put '/' do
98
+ # ...
99
+ end
100
+
101
+ patch '/' do
102
+ # ...
103
+ end
104
+
105
+ delete '/' do
106
+ # ...
107
+ end
108
+
109
+ options '/' do
110
+ # ...
111
+ end
112
+ end
113
+ ```
114
+
115
+ When a route gets called you have this methods available:
116
+
117
+ * `env`: The Rack environment.
118
+ * `request`: a `Hobbit::Request` instance.
119
+ * `response`: a `Hobbit::Response` instance.
120
+
121
+ And any other method defined in your application.
122
+
123
+ #### Available methods
124
+
125
+ * `delete`
126
+ * `get`
127
+ * `head`
128
+ * `options`
129
+ * `patch`
130
+ * `post`
131
+ * `put`
132
+
133
+ **Note**: Since most browsers don't support methods other than **GET** and
134
+ **POST** you must use the `Rack::MethodOverride` middleware. (See
135
+ [Rack::MethodOverride](https://github.com/rack/rack/blob/master/lib/rack/methodoverride.rb)).
136
+
137
+ #### Routes with parameters
138
+
139
+ Besides the standard `GET` and `POST` parameters, you can have routes with
140
+ parameters:
141
+
142
+ ```ruby
143
+ require 'hobbit'
144
+
145
+ class App < Hobbit::Base
146
+ # matches both /hi/hobbit and /hi/patricio
147
+ get '/hi/:name' do
148
+ # request.params is filled with the route paramters, like this:
149
+ "Hello #{request.params[:name]}"
150
+ end
151
+ end
152
+ ```
153
+
154
+ #### Redirecting
155
+
156
+ If you look at Hobbit implementation, you may notice that there is no
157
+ `redirect` method (or similar). This is because such functionality is provided
158
+ by [Rack::Response](https://github.com/rack/rack/blob/master/lib/rack/response.rb)
159
+ and for now we [don't wan't to repeat ourselves](http://en.wikipedia.org/wiki/Don't_repeat_yourself)
160
+ (obviously you can create an extension!). So, if you want to redirect to
161
+ another route, do it like this:
162
+
163
+ ```ruby
164
+ require 'hobbit'
165
+
166
+ class App < Hobbit::Base
167
+ get '/' do
168
+ response.redirect '/hi'
169
+ end
170
+
171
+ get '/hi' do
172
+ 'Hello World!'
173
+ end
174
+ end
175
+ ```
176
+
177
+ #### Halting
178
+
179
+ To immediately stop a request within route you can use `halt`.
180
+
181
+ ```ruby
182
+ require 'hobbit'
183
+
184
+ class App < Hobbit::Base
185
+ use Rack::Session::Cookie, secret: SecureRandom.hex(64)
186
+
187
+ def session
188
+ env['rack.session']
189
+ end
190
+
191
+ get '/' do
192
+ response.status = 401
193
+ halt response.finish
194
+ end
195
+ end
196
+ ```
197
+
198
+ ### Built on top of rack
199
+
200
+ Each Hobbit application is a Rack stack (See this
201
+ [blog post](http://m.onkey.org/ruby-on-rack-2-the-builder) for more
202
+ information).
203
+
204
+ #### Mapping applications
205
+
206
+ You can mount any Rack application to the stack by using the `map` class
207
+ method:
208
+
209
+ ```ruby
210
+ require 'hobbit'
211
+
212
+ class InnerApp < Hobbit::Base
213
+ # gets called when path_info = '/inner'
214
+ get do
215
+ 'Hello InnerApp!'
216
+ end
217
+ end
218
+
219
+ class App < Hobbit::Base
220
+ map('/inner') { run InnerApp.new }
221
+
222
+ get '/' do
223
+ 'Hello App!'
224
+ end
225
+ end
226
+ ```
227
+
228
+ #### Using middleware
229
+
230
+ You can add any Rack middleware to the stack by using the `use` class method:
231
+
232
+ ```ruby
233
+ require 'hobbit'
234
+
235
+ class App < Hobbit::Base
236
+ use Rack::Session::Cookie, secret: SecureRandom.hex(64)
237
+ use Rack::ShowExceptions
238
+
239
+ def session
240
+ env['rack.session']
241
+ end
242
+
243
+ get '/' do
244
+ session[:name] = 'hobbit'
245
+ end
246
+
247
+ # more routes...
248
+ end
249
+
250
+ run App.new
251
+ ```
252
+
253
+ ### Security
254
+
255
+ By default, Hobbit (nor Rack) comes without any protection against web
256
+ attacks. The use of [rack-protection](https://github.com/rkh/rack-protection)
257
+ is highly recommended:
258
+
259
+ ```ruby
260
+ require 'hobbit'
261
+ require 'rack/protection'
262
+ require 'securerandom'
263
+
264
+ class App < Hobbit::Base
265
+ use Rack::Session::Cookie, secret: SecureRandom.hex(64)
266
+ use Rack::Protection
267
+
268
+ get '/' do
269
+ 'Hello World!'
270
+ end
271
+ end
272
+ ```
273
+
274
+ See the [rack-protection](https://github.com/rkh/rack-protection)
275
+ documentation for futher information.
276
+
277
+ ### Testing
278
+
279
+ [rack-test](https://github.com/brynary/rack-test) is highly recommended. See
280
+ an example:
281
+
282
+ In `app.rb`:
283
+
284
+ ```ruby
285
+ require 'hobbit'
286
+
287
+ class App < Hobbit::Base
288
+ get '/' do
289
+ 'Hello World!'
290
+ end
291
+ end
292
+ ```
293
+
294
+ In `app_spec.rb`:
295
+
296
+ ```ruby
297
+ require 'minitest/autorun'
298
+ # imagine that app.rb and app_spec.rb are stored in the same directory
299
+ require 'app'
300
+
301
+ describe App do
302
+ include Rack::Test::Methods
303
+
304
+ def app
305
+ App.new
306
+ end
307
+
308
+ describe 'GET /' do
309
+ it 'must be ok' do
310
+ get '/'
311
+ last_response.must_be :ok?
312
+ last_response.body.must_match /Hello World!/
313
+ end
314
+ end
315
+ end
316
+ ```
317
+
318
+ See the [rack-test](https://github.com/brynary/rack-test) documentation
319
+ for futher information.
320
+
321
+ ### Extensions
322
+
323
+ You can extend Hobbit by creating standard ruby modules. See an example:
324
+
325
+ ```ruby
326
+ module MyExtension
327
+ def do_something
328
+ # do something
329
+ end
330
+ end
331
+
332
+ class App < Hobbit::Base
333
+ include MyExtension
334
+
335
+ get '/' do
336
+ do_something
337
+ 'Hello World!'
338
+ end
339
+ end
340
+ ```
341
+
342
+ #### Hobbit::Contrib
343
+
344
+ [hobbit-contrib](https://github.com/patriciomacadden/hobbit-contrib) is a ruby
345
+ gem that comes with a lot of hobbit extensions, such as:
346
+
347
+ * `Hobbit::Render`: provides basic template rendering.
348
+ * `Hobbit::Session`: provides helper methods for handling user sessions.
349
+ * `Hobbit::Environment`: provides helper methods for handling application
350
+ environments.
351
+ * `Hobbit::Filter`: provides helper class methods for handling Sinatra-like
352
+ filters.
353
+ * `Hobbit::ErrorHandling`: provides helper class methods for handling
354
+ Sinatra-like error handling.
355
+
356
+ ... And many more!
357
+
358
+ ## Community
359
+
360
+ * [Wiki](https://github.com/patriciomacadden/hobbit/wiki): Guides, how-tos and recipes
361
+ * IRC: [#hobbitrb](irc://chat.freenode.net/#hobbitrb) on [http://freenode.net](http://freenode.net)
362
+
363
+ ## Presentations
364
+
365
+ * Building web applications in Ruby, by [Krzysztof Wawer](https://github.com/wafcio)
366
+ ([english](https://speakerdeck.com/wafcio/hobbit-english), [polish](https://speakerdeck.com/wafcio/hobbit))
367
+
368
+ ## Contributing
369
+
370
+ 1. Fork it
371
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
372
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
373
+ 4. Push to the branch (`git push origin my-new-feature`)
374
+ 5. Create new Pull Request
375
+
376
+ ## License
377
+
378
+ See the [LICENSE](https://github.com/patriciomacadden/hobbit/blob/master/LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new
data/hobby.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'hobby'
7
+ spec.version = '0.0.0'
8
+ spec.authors = ['Patricio Mac Adden']
9
+ spec.email = ['patriciomacadden@gmail.com']
10
+ spec.description = %q{A minimalistic microframework built on top of rack}
11
+ spec.summary = %q{A minimalistic microframework built on top of rack}
12
+ spec.homepage = 'https://github.com/patriciomacadden/hobbit'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ 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
+ end
data/lib/hobby/app.rb ADDED
@@ -0,0 +1,66 @@
1
+ module Hobby
2
+ class App
3
+ class << self
4
+ def members
5
+ @members ||= {}
6
+ end
7
+
8
+ [:builder, :router].each do |member|
9
+ define_method member do |&custom_member|
10
+ if custom_member
11
+ members[member] = custom_member.call
12
+ else
13
+ members[member] ||= Hobby.const_get(member.capitalize).new
14
+ end
15
+ end
16
+ end
17
+
18
+ Verbs.each do |verb|
19
+ define_method verb.downcase do |path, &route|
20
+ router.add_route verb, path, &route
21
+ end
22
+ 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
+ end
33
+
34
+ attr_reader :env, :request, :response
35
+
36
+ def call env
37
+ dup.handle env
38
+ end
39
+
40
+ def handle env
41
+ @env = env
42
+ @request = Request.new @env
43
+ @response = Response.new
44
+
45
+ catch(:halt) { route_eval }
46
+ end
47
+
48
+ private
49
+
50
+ def halt response
51
+ throw :halt, response
52
+ end
53
+
54
+ def route_eval
55
+ route = self.class.router.route_for request
56
+
57
+ if route
58
+ response.write instance_eval &route
59
+ else
60
+ response.status = 404
61
+ end
62
+
63
+ response.finish
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,20 @@
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
@@ -0,0 +1,29 @@
1
+ module Hobby
2
+ class Router
3
+ require_relative 'router/route'
4
+
5
+ def initialize
6
+ @routes = Hash.new { |hash, key| hash[key] = [] }
7
+ end
8
+
9
+ def add_route(verb, path, &block)
10
+ @routes[verb] << Route.new(path, &block)
11
+ self
12
+ end
13
+
14
+ def route_for(request)
15
+ route = @routes[request.request_method].detect do |route|
16
+ route.compiled_path =~ request.path_info
17
+ end
18
+
19
+ if route
20
+ $~.captures.each_with_index do |value, index|
21
+ param = route.extra_params[index]
22
+ request.params[param] = value
23
+ end
24
+ end
25
+
26
+ route
27
+ end
28
+ end
29
+ end
data/lib/hobby.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rack'
2
+ require 'forwardable'
3
+ require 'include_constants'
4
+
5
+ module Hobby
6
+ 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
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'hobby'
2
+ require 'rack/test'
3
+
4
+ require 'minitest-power_assert'
5
+ require 'minitest/autorun'
data/test/test_app.rb ADDED
@@ -0,0 +1,189 @@
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
@@ -0,0 +1,65 @@
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
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hobby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Patricio Mac Adden
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
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
126
+ email:
127
+ - patriciomacadden@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".travis.yml"
134
+ - CHANGELOG.md
135
+ - Gemfile
136
+ - LICENSE
137
+ - README.md
138
+ - Rakefile
139
+ - hobby.gemspec
140
+ - lib/hobby.rb
141
+ - lib/hobby/app.rb
142
+ - lib/hobby/router.rb
143
+ - lib/hobby/router/route.rb
144
+ - test/helper.rb
145
+ - test/test_app.rb
146
+ - test/test_router.rb
147
+ homepage: https://github.com/patriciomacadden/hobbit
148
+ licenses:
149
+ - MIT
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 2.4.5
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: A minimalistic microframework built on top of rack
171
+ test_files:
172
+ - test/helper.rb
173
+ - test/test_app.rb
174
+ - test/test_router.rb
175
+ has_rdoc: