mojito 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +20 -0
- data/README.textile +52 -0
- data/VERSION +1 -0
- data/lib/mojito.rb +150 -0
- data/lib/mojito/helpers.rb +7 -0
- data/lib/mojito/helpers/exception_handling.rb +84 -0
- data/lib/mojito/matchers.rb +16 -0
- data/lib/mojito/matchers/methods.rb +33 -0
- data/lib/mojito/matchers/path.rb +35 -0
- data/lib/mojito/matchers/virtual_host.rb +22 -0
- data/lib/mojito/rendering.rb +15 -0
- data/lib/mojito/rendering/content.rb +13 -0
- data/lib/mojito/rendering/content_types.rb +20 -0
- data/lib/mojito/rendering/delegation.rb +13 -0
- data/lib/mojito/rendering/status_codes.rb +39 -0
- data/lib/mojito/utils/status_codes.rb +64 -0
- data/spec/mojito/matchers_spec.rb +89 -0
- data/spec/mojito/rendering_spec.rb +14 -0
- data/spec/mojito_spec.rb +34 -0
- metadata +143 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Max Trense
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
h1. mojito - Next level ruby webframework
|
2
|
+
|
3
|
+
p. Mojito is a lean and simple webframework.
|
4
|
+
|
5
|
+
p. As the name implies mojito derives from cuba (https://github.com/soveran/cuba). Many thanks to Michel and the cuba sources for giving me some major insights on web-application-simplicity!
|
6
|
+
|
7
|
+
h2. Installing
|
8
|
+
|
9
|
+
bc. $ gem install mojito
|
10
|
+
|
11
|
+
h2. Your first application
|
12
|
+
|
13
|
+
p. The easiest way to start with mojito is to use a plain config.ru script to develop and configure your application.
|
14
|
+
|
15
|
+
bc.. $ cat config.ru
|
16
|
+
require 'mojito'
|
17
|
+
|
18
|
+
FirstApp = Mojito.application do
|
19
|
+
on GET(), PATH('/hello/:name') do |name|
|
20
|
+
write "Hello #{name}! How are you?"
|
21
|
+
content_type :plain
|
22
|
+
ok!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
run FirstApp
|
27
|
+
|
28
|
+
p. This installs a handler for GET requests to a path of /hello/ followed by a name (actually this notation catches all but a slash '/'). You can start this application by running @rackup@ from the same directory:
|
29
|
+
|
30
|
+
bc. $ rackup
|
31
|
+
|
32
|
+
p. This starts a http-server on port 9292. Now try to call your handler through curl:
|
33
|
+
|
34
|
+
bc. $ curl http://localhost:9292/hello/Fred
|
35
|
+
Hello Fred! How are you?
|
36
|
+
|
37
|
+
p. Please be aware that although this way is easy to start with it tends to messy very quick if your project becomes more complex, so please read on...
|
38
|
+
|
39
|
+
h2. Contributing to mojito
|
40
|
+
|
41
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
42
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
43
|
+
* Fork the project.
|
44
|
+
* Start a feature/bugfix branch.
|
45
|
+
* Commit and push until you are happy with your contribution.
|
46
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
47
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
48
|
+
|
49
|
+
h2. Copyright
|
50
|
+
|
51
|
+
p. Copyright (c) 2012 Max Trense. See LICENSE.txt for further details.
|
52
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/mojito.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rack'
|
3
|
+
require 'slick/logging'
|
4
|
+
|
5
|
+
module Mojito
|
6
|
+
require 'mojito/utils/status_codes'
|
7
|
+
require 'mojito/helpers'
|
8
|
+
require 'mojito/rendering'
|
9
|
+
require 'mojito/matchers'
|
10
|
+
|
11
|
+
R = Rendering
|
12
|
+
M = Matchers
|
13
|
+
H = Helpers
|
14
|
+
|
15
|
+
def self.included(type)
|
16
|
+
type.instance_exec do
|
17
|
+
ALL_HELPERS.reverse.each do |mod|
|
18
|
+
include mod
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.base_application(*helpers, &block)
|
24
|
+
Class.new.tap do |cl|
|
25
|
+
cl.instance_exec do
|
26
|
+
include Mojito::Base
|
27
|
+
helpers.reverse.each do |helper|
|
28
|
+
include helper
|
29
|
+
extend helper::ClassMethods if helper.const_defined? :ClassMethods
|
30
|
+
end
|
31
|
+
end
|
32
|
+
cl.routes &block if block
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.application(*helpers, &block)
|
37
|
+
Class.new.tap do |cl|
|
38
|
+
cl.instance_exec do
|
39
|
+
include Mojito
|
40
|
+
helpers.reverse.each do |helper|
|
41
|
+
include helper
|
42
|
+
extend helper::ClassMethods if helper.const_defined? :ClassMethods
|
43
|
+
end
|
44
|
+
end
|
45
|
+
cl.routes &block if block
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module Base
|
50
|
+
|
51
|
+
def self.included(type)
|
52
|
+
type.extend ClassMethods
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(env)
|
56
|
+
@__env = env
|
57
|
+
end
|
58
|
+
|
59
|
+
def env
|
60
|
+
@__env
|
61
|
+
end
|
62
|
+
|
63
|
+
def request
|
64
|
+
@__request ||= Rack::Request.new(env)
|
65
|
+
end
|
66
|
+
|
67
|
+
def response
|
68
|
+
@__response ||= Rack::Response.new
|
69
|
+
end
|
70
|
+
|
71
|
+
def captures
|
72
|
+
@__captures ||= []
|
73
|
+
end
|
74
|
+
|
75
|
+
def locals
|
76
|
+
@__locals ||= {}
|
77
|
+
end
|
78
|
+
|
79
|
+
def script_name
|
80
|
+
env['SCRIPT_NAME']
|
81
|
+
end
|
82
|
+
|
83
|
+
def path_info
|
84
|
+
env['PATH_INFO']
|
85
|
+
end
|
86
|
+
|
87
|
+
def halt!(resp = response)
|
88
|
+
throw :halt, case resp
|
89
|
+
when Rack::Response
|
90
|
+
resp.finish
|
91
|
+
when Array
|
92
|
+
resp
|
93
|
+
when Symbol, Integer
|
94
|
+
response.status = STATUS[resp].code
|
95
|
+
response.finish
|
96
|
+
else
|
97
|
+
[500, { 'Content-Type' => 'application/octet-stream' }, []]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def dispatch
|
102
|
+
instance_exec &self.class.routes if self.class.routes
|
103
|
+
Rack::Response.new [], 404, 'Content-Type' => 'application/octet-stream'
|
104
|
+
end
|
105
|
+
|
106
|
+
def on(*matchers, &block)
|
107
|
+
env_backup = env.dup
|
108
|
+
param_size = captures.length
|
109
|
+
return unless matchers.all? {|m| __match?(m) }
|
110
|
+
params = captures[param_size..-1][0..block.arity].collect {|p| Rack::Utils.unescape(p) }
|
111
|
+
instance_exec *params, &block
|
112
|
+
ensure
|
113
|
+
@__env = env_backup
|
114
|
+
end
|
115
|
+
|
116
|
+
def __match?(matcher)
|
117
|
+
case matcher
|
118
|
+
when String, Regexp
|
119
|
+
instance_exec &Matchers::PATH(matcher)
|
120
|
+
when Proc
|
121
|
+
instance_exec &matcher
|
122
|
+
else
|
123
|
+
matcher
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
module ClassMethods
|
128
|
+
|
129
|
+
def call(env)
|
130
|
+
catch :halt do
|
131
|
+
new(env).dispatch
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def routes(&block)
|
136
|
+
@__routing = block if block
|
137
|
+
@__routing
|
138
|
+
end
|
139
|
+
|
140
|
+
def mock_request
|
141
|
+
Rack::MockRequest.new self
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
ALL_HELPERS = [Mojito::Matchers, Mojito::Rendering, Mojito::Helpers::ExceptionHandling, Mojito::Base]
|
149
|
+
|
150
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito
|
4
|
+
|
5
|
+
class MojitoException < Exception
|
6
|
+
|
7
|
+
def initialize(status = 500, message = STATUS[status].message)
|
8
|
+
super(message)
|
9
|
+
@status = Mojito::STATUS[status].code
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :status
|
13
|
+
|
14
|
+
def to_response
|
15
|
+
Rack::Response.new [], status, 'Content-Type' => 'application/octet-stream'
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
module Helpers
|
21
|
+
|
22
|
+
module ExceptionHandling
|
23
|
+
|
24
|
+
def self.included(type)
|
25
|
+
type.extend ClassMethods
|
26
|
+
type.instance_exec do
|
27
|
+
old_dispatch = instance_method(:dispatch)
|
28
|
+
define_method :dispatch do
|
29
|
+
begin
|
30
|
+
old_dispatch.bind(self).call
|
31
|
+
rescue Exception => e
|
32
|
+
__handle_error e
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def raise(exception)
|
39
|
+
backtrace = caller[1..-1]
|
40
|
+
Kernel.raise case exception
|
41
|
+
when Symbol, Integer
|
42
|
+
Mojito::MojitoException.new(exception)
|
43
|
+
when Exception
|
44
|
+
exception
|
45
|
+
else
|
46
|
+
RuntimeError.new(exception)
|
47
|
+
end.tap {|e| e.set_backtrace backtrace }
|
48
|
+
end
|
49
|
+
|
50
|
+
def __handle_error(exception)
|
51
|
+
if handler = case exception
|
52
|
+
when MojitoException
|
53
|
+
self.class.error_handlers[exception.status]
|
54
|
+
when Exception
|
55
|
+
self.class.error_handlers[exception.class]
|
56
|
+
end
|
57
|
+
instance_exec &handler
|
58
|
+
end
|
59
|
+
puts "Exception: #{exception.inspect}"
|
60
|
+
raise exception
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
|
65
|
+
def on_error(type, &block)
|
66
|
+
case type
|
67
|
+
when Symbol, Integer
|
68
|
+
error_handlers[Mojito::STATUS[type].code] = block
|
69
|
+
when Class
|
70
|
+
error_handlers[type] = block
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def error_handlers
|
75
|
+
@__error_handlers ||= {}
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Matchers
|
4
|
+
|
5
|
+
require 'mojito/matchers/methods'
|
6
|
+
require 'mojito/matchers/path'
|
7
|
+
require 'mojito/matchers/virtual_host'
|
8
|
+
|
9
|
+
include Methods
|
10
|
+
include Path
|
11
|
+
include VirtualHost
|
12
|
+
|
13
|
+
extend Path
|
14
|
+
extend VirtualHost
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Matchers
|
4
|
+
|
5
|
+
module Methods
|
6
|
+
|
7
|
+
def METHOD(method)
|
8
|
+
proc { request.request_method == method.to_s.upcase }
|
9
|
+
end
|
10
|
+
|
11
|
+
def GET
|
12
|
+
METHOD(:get)
|
13
|
+
end
|
14
|
+
|
15
|
+
def HEAD
|
16
|
+
METHOD(:head)
|
17
|
+
end
|
18
|
+
|
19
|
+
def POST
|
20
|
+
METHOD(:post)
|
21
|
+
end
|
22
|
+
|
23
|
+
def PUT
|
24
|
+
METHOD(:put)
|
25
|
+
end
|
26
|
+
|
27
|
+
def DELETE
|
28
|
+
METHOD(:delete)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Matchers
|
4
|
+
|
5
|
+
module Path
|
6
|
+
|
7
|
+
def PATH(pattern)
|
8
|
+
consume_path = proc do |pattern|
|
9
|
+
if match = path_info.match(%r<\A/#{pattern}(?=/|\z)>)
|
10
|
+
env['SCRIPT_NAME'] = match.to_s
|
11
|
+
env['PATH_INFO'] = match.post_match
|
12
|
+
locals.update match.names.inject({}) {|hash, name| hash[name.to_sym] = match[name] ; hash }
|
13
|
+
captures.push *match.captures
|
14
|
+
true
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
proc do
|
20
|
+
if p = case pattern
|
21
|
+
when String
|
22
|
+
pattern.gsub(%r{/?:\?\w+}) {|name| "(?:/(?<#{name[2..-1]}>[^/]+))?" }.gsub(%r{:\w+}) {|name| "(?<#{name[1..-1]}>[^/]+)" }
|
23
|
+
when Regexp
|
24
|
+
pattern
|
25
|
+
end
|
26
|
+
instance_exec p, &consume_path
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Matchers
|
4
|
+
|
5
|
+
module VirtualHost
|
6
|
+
|
7
|
+
def HOST(pattern)
|
8
|
+
proc do
|
9
|
+
case pattern
|
10
|
+
when String
|
11
|
+
/#{pattern.gsub('**', '[^:]+').gsub('*', '[^:.]+')}/ === request.host_with_port
|
12
|
+
when Array
|
13
|
+
pattern.any? {|p| /#{p.gsub('**', '[^:]+').gsub('*', '[^:.]+')}/ === request.host_with_port }
|
14
|
+
when Regexp
|
15
|
+
pattern === request.host_with_port
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Rendering
|
4
|
+
|
5
|
+
require 'mojito/rendering/content'
|
6
|
+
require 'mojito/rendering/content_types'
|
7
|
+
require 'mojito/rendering/delegation'
|
8
|
+
require 'mojito/rendering/status_codes'
|
9
|
+
|
10
|
+
include Content
|
11
|
+
include ContentTypes
|
12
|
+
include Delegation
|
13
|
+
include StatusCodes
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Rendering
|
4
|
+
|
5
|
+
module ContentTypes
|
6
|
+
require 'mime/types'
|
7
|
+
|
8
|
+
def content_type(type = :html, charset = 'UTF-8')
|
9
|
+
t = case type
|
10
|
+
when :plain
|
11
|
+
MIME::Types.type_for 'txt'
|
12
|
+
else
|
13
|
+
MIME::Types.type_for type.to_s
|
14
|
+
end.first
|
15
|
+
response['Content-Type'] = t.to_s + (t.ascii? ? "; charset=#{charset}" : '')
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Rendering
|
4
|
+
|
5
|
+
module StatusCodes
|
6
|
+
|
7
|
+
def ok!
|
8
|
+
response.status = 200
|
9
|
+
halt!
|
10
|
+
end
|
11
|
+
|
12
|
+
def not_found!
|
13
|
+
response.status = 404
|
14
|
+
halt!
|
15
|
+
end
|
16
|
+
|
17
|
+
def internal_server_error!
|
18
|
+
response.status = 500
|
19
|
+
halt!
|
20
|
+
end
|
21
|
+
|
22
|
+
def unavailable!
|
23
|
+
response.status = 503
|
24
|
+
halt!
|
25
|
+
end
|
26
|
+
|
27
|
+
def redirect(target, status = 302)
|
28
|
+
response['Location'] = target
|
29
|
+
response.status = status
|
30
|
+
end
|
31
|
+
|
32
|
+
def redirect!(target, status = 302)
|
33
|
+
redirect target, status
|
34
|
+
halt!
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito
|
4
|
+
|
5
|
+
HttpStatusCode = Struct.new :code, :symbol, :message
|
6
|
+
STATUS = {}
|
7
|
+
|
8
|
+
[
|
9
|
+
HttpStatusCode.new(100, :continue, 'Continue'),
|
10
|
+
HttpStatusCode.new(101, :switching_protocols, 'Switching Protocols'),
|
11
|
+
HttpStatusCode.new(102, :processing, 'Processing'),
|
12
|
+
HttpStatusCode.new(103, :checkpoint, 'Checkpoint'),
|
13
|
+
HttpStatusCode.new(122, :request_uri_too_long, 'Request-URI too long'),
|
14
|
+
HttpStatusCode.new(200, :ok, 'OK'),
|
15
|
+
HttpStatusCode.new(201, :created, 'Created'),
|
16
|
+
HttpStatusCode.new(202, :accepted, 'Accepted'),
|
17
|
+
HttpStatusCode.new(203, :non_authoritative_information, 'Non-Authoritative Information'),
|
18
|
+
HttpStatusCode.new(204, :no_content, 'No Content'),
|
19
|
+
HttpStatusCode.new(205, :reset_content, 'Reset Content'),
|
20
|
+
HttpStatusCode.new(206, :partial_content, 'Partial Content'),
|
21
|
+
HttpStatusCode.new(207, :multi_status, 'Multi-Status'),
|
22
|
+
HttpStatusCode.new(208, :already_reported, 'Already Reported'),
|
23
|
+
HttpStatusCode.new(226, :im_used, 'IM Used'),
|
24
|
+
HttpStatusCode.new(300, :multiple_choices, 'Multiple Choices'),
|
25
|
+
HttpStatusCode.new(301, :moved_permanently, 'Moved Permanently'),
|
26
|
+
HttpStatusCode.new(302, :found, 'Found'),
|
27
|
+
HttpStatusCode.new(303, :see_other, 'See Other'),
|
28
|
+
HttpStatusCode.new(304, :not_modified, 'Not Modified'),
|
29
|
+
HttpStatusCode.new(305, :use_proxy, 'Use Proxy'),
|
30
|
+
HttpStatusCode.new(306, :switch_proxy, 'Switch Proxy'),
|
31
|
+
HttpStatusCode.new(307, :temporary_redirect, 'Temporary Redirect'),
|
32
|
+
HttpStatusCode.new(308, :resume_incomplete, 'Resume Incomplete'),
|
33
|
+
HttpStatusCode.new(400, :bad_request, 'Bad Request'),
|
34
|
+
HttpStatusCode.new(401, :unauthorized, 'Unauthorized'),
|
35
|
+
HttpStatusCode.new(402, :payment_required, 'Payment Required'),
|
36
|
+
HttpStatusCode.new(403, :forbidden, 'Forbidden'),
|
37
|
+
HttpStatusCode.new(404, :not_found, 'Not Found'),
|
38
|
+
HttpStatusCode.new(405, :method_not_allowed, 'Method Not Allowed'),
|
39
|
+
HttpStatusCode.new(406, :not_acceptable, 'Not Acceptable'),
|
40
|
+
HttpStatusCode.new(407, :proxy_authentication_required, 'Proxy Authentication Required'),
|
41
|
+
HttpStatusCode.new(408, :request_timeout, 'Request Timeout'),
|
42
|
+
HttpStatusCode.new(409, :conflict, 'Conflict'),
|
43
|
+
HttpStatusCode.new(410, :gone, 'Gone'),
|
44
|
+
HttpStatusCode.new(411, :length_required, 'Length Required'),
|
45
|
+
HttpStatusCode.new(412, :precondition_failed, 'Precondition Failed'),
|
46
|
+
HttpStatusCode.new(413, :request_entity_too_large, 'Request Entity Too Large'),
|
47
|
+
HttpStatusCode.new(414, :request_uri_too_long, 'Request-URI Too Long'),
|
48
|
+
HttpStatusCode.new(415, :unsupported_media_type, 'Unsupported Media Type'),
|
49
|
+
HttpStatusCode.new(416, :request_range_not_satisfiable, 'Request Range Not Satisfiable'),
|
50
|
+
HttpStatusCode.new(417, :expectation_failed, 'Expectation Failed'),
|
51
|
+
HttpStatusCode.new(418, :im_a_teapot, 'I\'m a teapot'),
|
52
|
+
HttpStatusCode.new(419, :enhance_your_calm, 'Enhance Your Calm'),
|
53
|
+
HttpStatusCode.new(500, :internal_server_error, 'Internal Server Error'),
|
54
|
+
HttpStatusCode.new(501, :not_implemented, 'Not Implemented'),
|
55
|
+
HttpStatusCode.new(502, :bad_gateway, 'Bad Gateway'),
|
56
|
+
HttpStatusCode.new(503, :service_unavailable, 'Service Unavailable'),
|
57
|
+
HttpStatusCode.new(504, :gateway_timeout, 'Gateway Timeout'),
|
58
|
+
HttpStatusCode.new(505, :http_version_not_supported, 'HTTP Version Not Supported'),
|
59
|
+
].each do |sc|
|
60
|
+
STATUS[sc.code] = sc
|
61
|
+
STATUS[sc.symbol] = sc
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'mojito'
|
3
|
+
|
4
|
+
describe Mojito::Matchers::Methods do
|
5
|
+
|
6
|
+
subject do
|
7
|
+
Mojito.application Mojito::Matchers::Methods do
|
8
|
+
on GET() do write 'get' ; halt! end
|
9
|
+
on POST() do write 'post' ; halt! end
|
10
|
+
on HEAD() do write 'head' ; halt! end
|
11
|
+
on PUT() do write 'put' ; halt! end
|
12
|
+
on DELETE() do write 'delete' ; halt! end
|
13
|
+
on METHOD(:options) do write 'options' ; halt! end
|
14
|
+
end.mock_request
|
15
|
+
end
|
16
|
+
|
17
|
+
it { subject.get('/').body.should == 'get' }
|
18
|
+
it { subject.post('/').body.should == 'post' }
|
19
|
+
it { subject.head('/').body.should == 'head' }
|
20
|
+
it { subject.put('/').body.should == 'put' }
|
21
|
+
it { subject.delete('/').body.should == 'delete' }
|
22
|
+
it { subject.request('OPTIONS', '/').body.should == 'options' }
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Mojito::Matchers::Path do
|
27
|
+
|
28
|
+
subject { Mojito.application.new Rack::MockRequest.env_for('http://localhost/hello/world/rest') }
|
29
|
+
|
30
|
+
it do
|
31
|
+
subject.send(:__match?, Mojito::M::PATH('hello'))
|
32
|
+
subject.captures.should be_empty
|
33
|
+
subject.locals.should be_empty
|
34
|
+
subject.path_info.should == '/world/rest'
|
35
|
+
end
|
36
|
+
|
37
|
+
it do
|
38
|
+
subject.send(:__match?, 'hello')
|
39
|
+
subject.captures.should be_empty
|
40
|
+
subject.locals.should be_empty
|
41
|
+
subject.path_info.should == '/world/rest'
|
42
|
+
end
|
43
|
+
|
44
|
+
it do
|
45
|
+
subject.send(:__match?, Mojito::M::PATH('hello/:name'))
|
46
|
+
subject.captures.should == ['world']
|
47
|
+
subject.locals.should == { :name => 'world' }
|
48
|
+
subject.path_info.should == '/rest'
|
49
|
+
end
|
50
|
+
|
51
|
+
it do
|
52
|
+
subject.send(:__match?, 'hello/:name')
|
53
|
+
subject.captures.should == ['world']
|
54
|
+
subject.locals.should == { :name => 'world' }
|
55
|
+
subject.path_info.should == '/rest'
|
56
|
+
end
|
57
|
+
|
58
|
+
it do
|
59
|
+
subject.send(:__match?, Mojito::M::PATH(%r{hello/(?<name>[^/]+)}))
|
60
|
+
subject.captures.should == ['world']
|
61
|
+
subject.locals.should == { :name => 'world' }
|
62
|
+
subject.path_info.should == '/rest'
|
63
|
+
end
|
64
|
+
|
65
|
+
it do
|
66
|
+
subject.send(:__match?, %r{hello/(?<name>[^/]+)})
|
67
|
+
subject.captures.should == ['world']
|
68
|
+
subject.locals.should == { :name => 'world' }
|
69
|
+
subject.path_info.should == '/rest'
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
describe Mojito::Matchers::VirtualHost do
|
75
|
+
|
76
|
+
subject do
|
77
|
+
Mojito.application Mojito::Matchers::VirtualHost do
|
78
|
+
on HOST('localhost') do write 'localhost' ; halt! end
|
79
|
+
on HOST('test:8080') do write 'test' ; halt! end
|
80
|
+
end.mock_request
|
81
|
+
end
|
82
|
+
|
83
|
+
it { subject.get('http://localhost:4444/').body.should == 'localhost' }
|
84
|
+
it { subject.get('http://localhost/').body.should == 'localhost' }
|
85
|
+
it { subject.get('http://localhost:80/').body.should == 'localhost' }
|
86
|
+
it { subject.get('http://test:8080/').body.should == 'test' }
|
87
|
+
it { subject.get('http://test/').status.should == 404 }
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'mojito'
|
3
|
+
|
4
|
+
describe Mojito::Rendering::Content do
|
5
|
+
|
6
|
+
subject do
|
7
|
+
Mojito.base_application Mojito::Rendering::Content do
|
8
|
+
on do write 'test content' ; halt! end
|
9
|
+
end.mock_request
|
10
|
+
end
|
11
|
+
|
12
|
+
it { subject.get('/').body.should == 'test content' }
|
13
|
+
|
14
|
+
end
|
data/spec/mojito_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'mojito'
|
3
|
+
|
4
|
+
describe Mojito do
|
5
|
+
|
6
|
+
context do
|
7
|
+
subject { Mojito.application {} }
|
8
|
+
it { subject.ancestors.should include(Mojito::Base) }
|
9
|
+
it { subject.should respond_to(:call) }
|
10
|
+
end
|
11
|
+
|
12
|
+
context do
|
13
|
+
subject { Mojito.application(Mojito::Matchers::Path).new Rack::MockRequest.env_for('http://localhost/hello/world/rest') }
|
14
|
+
it { subject.env.should_not be_nil }
|
15
|
+
it { subject.request.should be_kind_of(Rack::Request) }
|
16
|
+
it { subject.captures.should be_empty }
|
17
|
+
it { subject.locals.should be_empty }
|
18
|
+
it do
|
19
|
+
subject.send(:__match?, proc { request.host == 'localhost' })
|
20
|
+
subject.captures.should be_empty
|
21
|
+
subject.locals.should be_empty
|
22
|
+
subject.path_info.should == '/hello/world/rest'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context do
|
27
|
+
subject do
|
28
|
+
Mojito.application do
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mojito
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Max Trense
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: &6514840 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.4.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *6514840
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mime-types
|
27
|
+
requirement: &6514180 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.18'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *6514180
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &6513520 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.8.0
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *6513520
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rdoc
|
49
|
+
requirement: &7041900 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.12'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *7041900
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: bundler
|
60
|
+
requirement: &7040840 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 1.0.0
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *7040840
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: jeweler
|
71
|
+
requirement: &7039960 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 1.8.3
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *7039960
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: rcov
|
82
|
+
requirement: &7038500 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *7038500
|
91
|
+
description: A simple yet powerful webframework largely inspired by Rum and Cuba
|
92
|
+
email: dev@trense.info
|
93
|
+
executables: []
|
94
|
+
extensions: []
|
95
|
+
extra_rdoc_files:
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.textile
|
98
|
+
files:
|
99
|
+
- LICENSE.txt
|
100
|
+
- VERSION
|
101
|
+
- lib/mojito.rb
|
102
|
+
- lib/mojito/helpers.rb
|
103
|
+
- lib/mojito/helpers/exception_handling.rb
|
104
|
+
- lib/mojito/matchers.rb
|
105
|
+
- lib/mojito/matchers/methods.rb
|
106
|
+
- lib/mojito/matchers/path.rb
|
107
|
+
- lib/mojito/matchers/virtual_host.rb
|
108
|
+
- lib/mojito/rendering.rb
|
109
|
+
- lib/mojito/rendering/content.rb
|
110
|
+
- lib/mojito/rendering/content_types.rb
|
111
|
+
- lib/mojito/rendering/delegation.rb
|
112
|
+
- lib/mojito/rendering/status_codes.rb
|
113
|
+
- lib/mojito/utils/status_codes.rb
|
114
|
+
- spec/mojito/matchers_spec.rb
|
115
|
+
- spec/mojito/rendering_spec.rb
|
116
|
+
- spec/mojito_spec.rb
|
117
|
+
- README.textile
|
118
|
+
homepage: http://github.com/mtrense/mojito
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ! '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 1.8.17
|
140
|
+
signing_key:
|
141
|
+
specification_version: 3
|
142
|
+
summary: A simple yet powerful webframework largely inspired by Rum and Cuba
|
143
|
+
test_files: []
|