mojito 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +49 -6
- data/VERSION +1 -1
- data/lib/mojito.rb +5 -104
- data/lib/mojito/base.rb +100 -0
- data/lib/mojito/helpers.rb +1 -0
- data/lib/mojito/helpers/exception_handling.rb +1 -1
- data/lib/mojito/helpers/shortcuts.rb +31 -0
- data/lib/mojito/matchers/path.rb +3 -3
- data/lib/mojito/rendering.rb +2 -0
- data/lib/mojito/rendering/delegation.rb +2 -2
- data/lib/mojito/rendering/file.rb +53 -0
- data/lib/mojito/rendering/status_codes.rb +1 -2
- data/lib/mojito/request_extensions.rb +31 -0
- data/spec/mojito/helpers_spec.rb +11 -0
- data/spec/mojito/matchers_spec.rb +36 -12
- data/spec/mojito/rendering_spec.rb +75 -0
- data/spec/mojito/request_extensions_spec.rb +19 -0
- data/spec/mojito_spec.rb +3 -0
- metadata +45 -17
data/README.textile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
h1. mojito - Next level
|
1
|
+
h1. mojito - Next level Ruby webframework
|
2
2
|
|
3
3
|
p. Mojito is a lean and simple webframework.
|
4
4
|
|
5
|
-
p. As the name implies mojito
|
5
|
+
p. As the name implies mojito is Cuba-born (https://github.com/soveran/cuba). Many thanks to Michel and his cuba project for giving me some major insights on web-application-simplicity!
|
6
6
|
|
7
7
|
h2. Installing
|
8
8
|
|
@@ -10,7 +10,7 @@ bc. $ gem install mojito
|
|
10
10
|
|
11
11
|
h2. Your first application
|
12
12
|
|
13
|
-
p. The easiest way to start with mojito is to use a plain config.ru script to develop and configure your application.
|
13
|
+
p. The easiest way to start with mojito is to use a plain @config.ru@ script to develop and configure your application.
|
14
14
|
|
15
15
|
bc.. $ cat config.ru
|
16
16
|
require 'mojito'
|
@@ -23,9 +23,9 @@ FirstApp = Mojito.application do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
run FirstApp
|
26
|
+
run FirstApp.to_app
|
27
27
|
|
28
|
-
p. This installs a handler for GET requests to a path of /hello/ followed by a name (actually this notation catches
|
28
|
+
p. This installs a handler for GET requests to a path of /hello/ followed by a name (actually this notation catches everything but a slash '/'). You can start this application by running @rackup@ from the same directory:
|
29
29
|
|
30
30
|
bc. $ rackup
|
31
31
|
|
@@ -34,7 +34,50 @@ p. This starts a http-server on port 9292. Now try to call your handler through
|
|
34
34
|
bc. $ curl http://localhost:9292/hello/Fred
|
35
35
|
Hello Fred! How are you?
|
36
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...
|
37
|
+
p. Please be aware that although this way is easy to start with it tends to get messy very quick if your project becomes more complex, so please read on...
|
38
|
+
|
39
|
+
h2. Structure of a real-world application
|
40
|
+
|
41
|
+
p. The above call to @Mojito.application@ actually created a class and included most of Mojitos foundation methods, so that you could start writing your application code without wondering what features you want to include in your application. As a tribute to simplicity (one of the most important I think), another helper method @Mojito.base_application@ can be used to create a blank application class, which only includes modules that are strictly necessary. Instead of using those methods you can also write out all of the generated classes code (here @Mojito@ is included, which in turn includes @Mojito::Base@, the basic module which is used by @Mojito.base_application@):
|
42
|
+
|
43
|
+
bc.. class FirstApp
|
44
|
+
include Mojito
|
45
|
+
routes do
|
46
|
+
on GET(), PATH('/hello/:name') do |name|
|
47
|
+
...
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
p. which is not only perfectly legal but also favoured, when you need any special behaviour on your application class.
|
53
|
+
|
54
|
+
p. Although you can actually use any structure you want (including a single-file application), some of them are more useful than others. My preferred way of developing Mojito applications is to reuse most of the concepts found in product development with rubygems and git.
|
55
|
+
|
56
|
+
h2. Behind the scenes
|
57
|
+
|
58
|
+
p. Understanding how Mojito dispatches requests and selects handlers, which in turn use common functionality to create the response is vital to an efficient use of Mojito as web application framework. First of all Mojito applications (classes which include @Mojito::Base@ or @Mojito@) are simply Ruby classes, which are instantiated with the Rack-environment hash for each request. This is automatically triggered by a rack-enabled webserver through the call of @run ApplicationClass@, which tells rack to call @call(env)@ on that class. The Mojito implementation will then instantiate the Application (the instance is called a _controller_ from now on) and call @dispatch@ on that controller. The dispatch method then executes any block given to the @routes@-method in the class definition. As in Cuba the routes-definition is executed once for each request.
|
59
|
+
|
60
|
+
h3. Matching
|
61
|
+
|
62
|
+
p. A matcher is simply a Proc, instantiated with matcher-specific configuration, which returns whether or not a given request matches its criteria. The request-method matcher for example is implemented as follows:
|
63
|
+
|
64
|
+
bc.. def METHOD(method)
|
65
|
+
proc { request.request_method == method.to_s.upcase }
|
66
|
+
end
|
67
|
+
|
68
|
+
p. This method configures a Proc to return true when the requests method matches the given parameter. The returned Proc is evaluated in the context of the current controller. This is also where the @request@ method comes from. Like @request@, a matcher can access any method from the controller, within which it is executed. The naming convention is to name all matchers uppercase.
|
69
|
+
|
70
|
+
h3. Rendering
|
71
|
+
|
72
|
+
h3. Helpers
|
73
|
+
|
74
|
+
h2. Deployment/Hosting
|
75
|
+
|
76
|
+
p. tbd
|
77
|
+
|
78
|
+
h2. Writing extensions
|
79
|
+
|
80
|
+
p. tbd
|
38
81
|
|
39
82
|
h2. Contributing to mojito
|
40
83
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/lib/mojito.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'rack'
|
3
|
-
require '
|
3
|
+
require 'extlib'
|
4
4
|
|
5
5
|
module Mojito
|
6
|
-
require 'mojito/
|
6
|
+
require 'mojito/request_extensions'
|
7
7
|
require 'mojito/helpers'
|
8
|
+
require 'mojito/base'
|
9
|
+
require 'mojito/utils/status_codes'
|
8
10
|
require 'mojito/rendering'
|
9
11
|
require 'mojito/matchers'
|
10
12
|
|
@@ -26,7 +28,6 @@ module Mojito
|
|
26
28
|
include Mojito::Base
|
27
29
|
helpers.reverse.each do |helper|
|
28
30
|
include helper
|
29
|
-
extend helper::ClassMethods if helper.const_defined? :ClassMethods
|
30
31
|
end
|
31
32
|
end
|
32
33
|
cl.routes &block if block
|
@@ -39,112 +40,12 @@ module Mojito
|
|
39
40
|
include Mojito
|
40
41
|
helpers.reverse.each do |helper|
|
41
42
|
include helper
|
42
|
-
extend helper::ClassMethods if helper.const_defined? :ClassMethods
|
43
43
|
end
|
44
44
|
end
|
45
45
|
cl.routes &block if block
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
|
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]
|
49
|
+
ALL_HELPERS = [Mojito::Matchers, Mojito::Rendering, Mojito::Helpers::ExceptionHandling, Mojito::Helpers::Shortcuts, Mojito::Base]
|
149
50
|
|
150
51
|
end
|
data/lib/mojito/base.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito
|
4
|
+
|
5
|
+
module Base
|
6
|
+
|
7
|
+
def self.included(type)
|
8
|
+
type.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(env)
|
12
|
+
@__env = env
|
13
|
+
end
|
14
|
+
|
15
|
+
def env
|
16
|
+
@__env
|
17
|
+
end
|
18
|
+
|
19
|
+
def request
|
20
|
+
@__request ||= Rack::Request.new(env)
|
21
|
+
end
|
22
|
+
|
23
|
+
def response
|
24
|
+
@__response ||= Rack::Response.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def halt!(resp = response)
|
28
|
+
throw :halt, case resp
|
29
|
+
when Rack::Response
|
30
|
+
resp.finish
|
31
|
+
when Array
|
32
|
+
resp
|
33
|
+
when Symbol, Integer
|
34
|
+
response.status = STATUS[resp].code
|
35
|
+
response.finish
|
36
|
+
else
|
37
|
+
[500, { 'Content-Type' => 'text/plain', 'Content-Length' => '0' }, []]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Dispatches the current request to the matching routes.
|
43
|
+
def dispatch
|
44
|
+
instance_exec &self.class.routes if self.class.routes
|
45
|
+
Rack::Response.new [], 404, 'Content-Type' => 'application/octet-stream'
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Defines a route which is matched when all given matchers evaluate to +true+.
|
50
|
+
def on(*matchers, &block)
|
51
|
+
env_backup = env.dup
|
52
|
+
param_size = request.captures.length
|
53
|
+
return unless matchers.all? {|m| __match?(m) }
|
54
|
+
params = request.captures[param_size..-1][0..block.arity].collect {|p| Rack::Utils.unescape(p) }
|
55
|
+
instance_exec *params, &block
|
56
|
+
ensure
|
57
|
+
@__env = env_backup
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Evaluates a single matcher, returning whether it matched or not. Please be aware that matchers (most prominently
|
62
|
+
# the PATH matcher) may alter the current request, however a matcher is only allowed to do that when it matches.
|
63
|
+
def __match?(matcher)
|
64
|
+
case matcher
|
65
|
+
when String, Regexp
|
66
|
+
instance_exec &Matchers::PATH(matcher)
|
67
|
+
when Proc
|
68
|
+
instance_exec &matcher
|
69
|
+
else
|
70
|
+
matcher
|
71
|
+
end
|
72
|
+
end
|
73
|
+
private :__match?
|
74
|
+
|
75
|
+
module ClassMethods
|
76
|
+
|
77
|
+
def call(env)
|
78
|
+
catch :halt do
|
79
|
+
new(env).dispatch
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def routes(&block)
|
84
|
+
@__routing = block if block
|
85
|
+
@__routing
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_app
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
def mock_request
|
93
|
+
Rack::MockRequest.new self
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
data/lib/mojito/helpers.rb
CHANGED
@@ -22,7 +22,6 @@ module Mojito
|
|
22
22
|
module ExceptionHandling
|
23
23
|
|
24
24
|
def self.included(type)
|
25
|
-
type.extend ClassMethods
|
26
25
|
type.instance_exec do
|
27
26
|
old_dispatch = instance_method(:dispatch)
|
28
27
|
define_method :dispatch do
|
@@ -33,6 +32,7 @@ module Mojito
|
|
33
32
|
end
|
34
33
|
end
|
35
34
|
end
|
35
|
+
type.extend ClassMethods
|
36
36
|
end
|
37
37
|
|
38
38
|
def raise(exception)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Helpers::Shortcuts
|
4
|
+
|
5
|
+
def captures
|
6
|
+
request.captures
|
7
|
+
end
|
8
|
+
|
9
|
+
def locals
|
10
|
+
request.locals
|
11
|
+
end
|
12
|
+
|
13
|
+
def script_name
|
14
|
+
request.script_name
|
15
|
+
end
|
16
|
+
|
17
|
+
def path_info
|
18
|
+
request.path_info
|
19
|
+
end
|
20
|
+
|
21
|
+
def session
|
22
|
+
request.session
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/mojito/matchers/path.rb
CHANGED
@@ -6,11 +6,11 @@ module Mojito::Matchers
|
|
6
6
|
|
7
7
|
def PATH(pattern)
|
8
8
|
consume_path = proc do |pattern|
|
9
|
-
if match =
|
9
|
+
if match = env['PATH_INFO'].match(%r<\A/#{pattern}(?=/|\z)>)
|
10
10
|
env['SCRIPT_NAME'] = match.to_s
|
11
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
|
12
|
+
request.locals.update match.names.inject({}) {|hash, name| hash[name.to_sym] = match[name] ; hash }
|
13
|
+
request.captures.push *match.captures
|
14
14
|
true
|
15
15
|
else
|
16
16
|
false
|
data/lib/mojito/rendering.rb
CHANGED
@@ -5,11 +5,13 @@ module Mojito::Rendering
|
|
5
5
|
require 'mojito/rendering/content'
|
6
6
|
require 'mojito/rendering/content_types'
|
7
7
|
require 'mojito/rendering/delegation'
|
8
|
+
require 'mojito/rendering/file'
|
8
9
|
require 'mojito/rendering/status_codes'
|
9
10
|
|
10
11
|
include Content
|
11
12
|
include ContentTypes
|
12
13
|
include Delegation
|
14
|
+
include File
|
13
15
|
include StatusCodes
|
14
16
|
|
15
17
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mojito::Rendering
|
4
|
+
|
5
|
+
module File
|
6
|
+
require 'pathname'
|
7
|
+
require 'mime/types'
|
8
|
+
require 'time'
|
9
|
+
|
10
|
+
def file!(filename)
|
11
|
+
path = Pathname === filename ? filename : Pathname.new(filename.to_s)
|
12
|
+
restrict_path! path if respond_to? :restrict_path!
|
13
|
+
if path.readable? and path.file?
|
14
|
+
body = FileResponse.new path
|
15
|
+
halt! [response.status, response.headers.merge(body.compute_headers), body]
|
16
|
+
else
|
17
|
+
not_found!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class FileResponse
|
22
|
+
|
23
|
+
def initialize(pathname)
|
24
|
+
@pathname = pathname
|
25
|
+
end
|
26
|
+
|
27
|
+
def each(&block)
|
28
|
+
@pathname.open do |f|
|
29
|
+
yield f.read
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def compute_headers
|
34
|
+
{ 'Content-Type' => mime_type.to_s, 'Content-Length' => size.to_s, 'Last-Modified' => @pathname.mtime.rfc2822 }
|
35
|
+
end
|
36
|
+
|
37
|
+
def mime_type
|
38
|
+
MIME::Types.type_for(@pathname.to_s).first || MIME::Types['application/octet-stream'].first
|
39
|
+
end
|
40
|
+
|
41
|
+
def size
|
42
|
+
@pathname.size
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_path
|
46
|
+
@pathname.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class ::Rack::Request
|
4
|
+
|
5
|
+
def captures
|
6
|
+
@env['mojito/captures'] ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def locals
|
10
|
+
@env['mojito/locals'] ||= Mash.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def script_name
|
14
|
+
@env['SCRIPT_NAME']
|
15
|
+
end
|
16
|
+
|
17
|
+
def path_info
|
18
|
+
@env['PATH_INFO']
|
19
|
+
end
|
20
|
+
|
21
|
+
get_method = instance_method(:GET)
|
22
|
+
define_method :GET do
|
23
|
+
get_method.bind(self).call.to_mash
|
24
|
+
end
|
25
|
+
|
26
|
+
post_method = instance_method(:POST)
|
27
|
+
define_method :POST do
|
28
|
+
post_method.bind(self).call.to_mash
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
require 'simplecov' and SimpleCov.start do
|
3
|
+
add_filter "spec/"
|
4
|
+
end
|
2
5
|
require 'mojito'
|
3
6
|
|
4
7
|
describe Mojito::Matchers::Methods do
|
@@ -43,30 +46,51 @@ describe Mojito::Matchers::Path do
|
|
43
46
|
|
44
47
|
it do
|
45
48
|
subject.send(:__match?, Mojito::M::PATH('hello/:name'))
|
46
|
-
subject.captures.should == ['world']
|
47
|
-
subject.locals.should == {
|
48
|
-
subject.path_info.should == '/rest'
|
49
|
+
subject.request.captures.should == ['world']
|
50
|
+
subject.request.locals.should == { 'name' => 'world' }
|
51
|
+
subject.request.path_info.should == '/rest'
|
49
52
|
end
|
50
53
|
|
51
54
|
it do
|
52
55
|
subject.send(:__match?, 'hello/:name')
|
53
|
-
subject.captures.should == ['world']
|
54
|
-
subject.locals.should == {
|
55
|
-
subject.path_info.should == '/rest'
|
56
|
+
subject.request.captures.should == ['world']
|
57
|
+
subject.request.locals.should == { 'name' => 'world' }
|
58
|
+
subject.request.path_info.should == '/rest'
|
56
59
|
end
|
57
60
|
|
58
61
|
it do
|
59
62
|
subject.send(:__match?, Mojito::M::PATH(%r{hello/(?<name>[^/]+)}))
|
60
|
-
subject.captures.should == ['world']
|
61
|
-
subject.locals.should == {
|
62
|
-
subject.path_info.should == '/rest'
|
63
|
+
subject.request.captures.should == ['world']
|
64
|
+
subject.request.locals.should == { 'name' => 'world' }
|
65
|
+
subject.request.path_info.should == '/rest'
|
63
66
|
end
|
64
67
|
|
65
68
|
it do
|
66
69
|
subject.send(:__match?, %r{hello/(?<name>[^/]+)})
|
67
|
-
subject.captures.should == ['world']
|
68
|
-
subject.locals.should == {
|
69
|
-
subject.path_info.should == '/rest'
|
70
|
+
subject.request.captures.should == ['world']
|
71
|
+
subject.request.locals.should == { 'name' => 'world' }
|
72
|
+
subject.request.path_info.should == '/rest'
|
73
|
+
end
|
74
|
+
|
75
|
+
context do
|
76
|
+
subject do
|
77
|
+
Mojito.base_application Mojito::M::Path, Mojito::R::Content do
|
78
|
+
on PATH('hello/:name') do
|
79
|
+
on PATH('another/:name') do
|
80
|
+
write request.locals[:name]
|
81
|
+
halt!
|
82
|
+
end
|
83
|
+
write request.locals[:name]
|
84
|
+
halt!
|
85
|
+
end
|
86
|
+
end.mock_request
|
87
|
+
end
|
88
|
+
|
89
|
+
it { subject.get('/hello/Fred').status.should == 200 }
|
90
|
+
it { subject.get('/hello/Fred').body.should == 'Fred' }
|
91
|
+
it { subject.get('/hello/Fred/another/Barney').status.should == 200 }
|
92
|
+
it { subject.get('/hello/Fred/another/Barney').body.should == 'Barney' }
|
93
|
+
|
70
94
|
end
|
71
95
|
|
72
96
|
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
require 'simplecov' and SimpleCov.start do
|
3
|
+
add_filter "spec/"
|
4
|
+
end
|
2
5
|
require 'mojito'
|
3
6
|
|
4
7
|
describe Mojito::Rendering::Content do
|
@@ -12,3 +15,75 @@ describe Mojito::Rendering::Content do
|
|
12
15
|
it { subject.get('/').body.should == 'test content' }
|
13
16
|
|
14
17
|
end
|
18
|
+
|
19
|
+
describe Mojito::Rendering::StatusCodes do
|
20
|
+
|
21
|
+
subject do
|
22
|
+
Mojito.base_application Mojito::Matchers::Path, Mojito::Rendering::StatusCodes do
|
23
|
+
on 'ok' do ok! end
|
24
|
+
on 'not_found' do not_found! end
|
25
|
+
on 'internal_server_error' do internal_server_error! end
|
26
|
+
on 'unavailable' do unavailable! end
|
27
|
+
on 'redirect' do redirect! '/test' end
|
28
|
+
end.mock_request
|
29
|
+
end
|
30
|
+
|
31
|
+
it { subject.get('/ok').status.should == 200 }
|
32
|
+
it { subject.get('/not_found').status.should == 404 }
|
33
|
+
it { subject.get('/internal_server_error').status.should == 500 }
|
34
|
+
it { subject.get('/unavailable').status.should == 503 }
|
35
|
+
it { subject.get('/redirect').status.should == 302 }
|
36
|
+
it { subject.get('/redirect').headers['Location'].should == '/test' }
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe Mojito::Rendering::Delegation do
|
41
|
+
|
42
|
+
subject do
|
43
|
+
sub_app = Mojito.base_application Mojito::R::Content, Mojito::R::Delegation do
|
44
|
+
on true do write 'sub-application' ; halt! end
|
45
|
+
end
|
46
|
+
Mojito.base_application Mojito::Rendering::Content, Mojito::Rendering::Delegation do
|
47
|
+
on true do run! sub_app end
|
48
|
+
end.mock_request
|
49
|
+
end
|
50
|
+
|
51
|
+
it { subject.get('/').status.should == 200 }
|
52
|
+
it { subject.get('/').body.should == 'sub-application' }
|
53
|
+
|
54
|
+
context do
|
55
|
+
|
56
|
+
subject do
|
57
|
+
sub_app = Mojito.base_application Mojito::R::Content, Mojito::R::Delegation, Mojito::H::Shortcuts do
|
58
|
+
on true do write("#{path_info} #{captures.first}") ; halt! end
|
59
|
+
end
|
60
|
+
Mojito.base_application Mojito::M::Path, Mojito::R::Content, Mojito::R::Delegation do
|
61
|
+
on PATH('hello/:name') do run! sub_app end
|
62
|
+
end.mock_request
|
63
|
+
end
|
64
|
+
|
65
|
+
it { subject.get('/hello/world/rest').status.should == 200 }
|
66
|
+
it { subject.get('/hello/world/rest').body.should == '/rest world' }
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
describe Mojito::Rendering::File do
|
73
|
+
|
74
|
+
subject do
|
75
|
+
Mojito.base_application Mojito::Rendering::File do
|
76
|
+
file! __FILE__
|
77
|
+
end.mock_request
|
78
|
+
end
|
79
|
+
|
80
|
+
it { subject.get('/').status.should == 200 }
|
81
|
+
it { subject.get('/').headers.should include('Content-Type') }
|
82
|
+
it { subject.get('/').headers['Content-Type'].should == 'application/x-ruby' }
|
83
|
+
it { subject.get('/').headers.should include('Content-Length') }
|
84
|
+
it { subject.get('/').headers['Content-Length'].should == ::File.size(__FILE__).to_s }
|
85
|
+
it { subject.get('/').headers.should include('Last-Modified') }
|
86
|
+
it { subject.get('/').headers['Last-Modified'].should == ::File.mtime(__FILE__).rfc2822 }
|
87
|
+
it { subject.get('/').body.should == ::File.read(__FILE__) }
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'simplecov' and SimpleCov.start do
|
3
|
+
add_filter "spec/"
|
4
|
+
end
|
5
|
+
require 'mojito'
|
6
|
+
|
7
|
+
describe ::Rack::Request do
|
8
|
+
|
9
|
+
subject do
|
10
|
+
Mojito.base_application Mojito::Rendering::Content do
|
11
|
+
write request.GET[:hello]
|
12
|
+
halt!
|
13
|
+
end.mock_request
|
14
|
+
end
|
15
|
+
|
16
|
+
it { subject.get('/?hello=world').status.should == 200 }
|
17
|
+
it { subject.get('/?hello=world').body.should == 'world' }
|
18
|
+
|
19
|
+
end
|
data/spec/mojito_spec.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mojito
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
16
|
-
requirement: &
|
16
|
+
requirement: &20131580 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.4.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *20131580
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: mime-types
|
27
|
-
requirement: &
|
27
|
+
requirement: &20130100 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,32 @@ dependencies:
|
|
32
32
|
version: '1.18'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *20130100
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: tilt
|
38
|
+
requirement: &20129340 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.3.3
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *20129340
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: extlib
|
49
|
+
requirement: &20128340 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.9.15
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *20128340
|
36
58
|
- !ruby/object:Gem::Dependency
|
37
59
|
name: rspec
|
38
|
-
requirement: &
|
60
|
+
requirement: &20152720 !ruby/object:Gem::Requirement
|
39
61
|
none: false
|
40
62
|
requirements:
|
41
63
|
- - ~>
|
@@ -43,10 +65,10 @@ dependencies:
|
|
43
65
|
version: 2.8.0
|
44
66
|
type: :development
|
45
67
|
prerelease: false
|
46
|
-
version_requirements: *
|
68
|
+
version_requirements: *20152720
|
47
69
|
- !ruby/object:Gem::Dependency
|
48
70
|
name: rdoc
|
49
|
-
requirement: &
|
71
|
+
requirement: &20151740 !ruby/object:Gem::Requirement
|
50
72
|
none: false
|
51
73
|
requirements:
|
52
74
|
- - ~>
|
@@ -54,10 +76,10 @@ dependencies:
|
|
54
76
|
version: '3.12'
|
55
77
|
type: :development
|
56
78
|
prerelease: false
|
57
|
-
version_requirements: *
|
79
|
+
version_requirements: *20151740
|
58
80
|
- !ruby/object:Gem::Dependency
|
59
81
|
name: bundler
|
60
|
-
requirement: &
|
82
|
+
requirement: &20150340 !ruby/object:Gem::Requirement
|
61
83
|
none: false
|
62
84
|
requirements:
|
63
85
|
- - ~>
|
@@ -65,10 +87,10 @@ dependencies:
|
|
65
87
|
version: 1.0.0
|
66
88
|
type: :development
|
67
89
|
prerelease: false
|
68
|
-
version_requirements: *
|
90
|
+
version_requirements: *20150340
|
69
91
|
- !ruby/object:Gem::Dependency
|
70
92
|
name: jeweler
|
71
|
-
requirement: &
|
93
|
+
requirement: &20149300 !ruby/object:Gem::Requirement
|
72
94
|
none: false
|
73
95
|
requirements:
|
74
96
|
- - ~>
|
@@ -76,10 +98,10 @@ dependencies:
|
|
76
98
|
version: 1.8.3
|
77
99
|
type: :development
|
78
100
|
prerelease: false
|
79
|
-
version_requirements: *
|
101
|
+
version_requirements: *20149300
|
80
102
|
- !ruby/object:Gem::Dependency
|
81
103
|
name: rcov
|
82
|
-
requirement: &
|
104
|
+
requirement: &20148580 !ruby/object:Gem::Requirement
|
83
105
|
none: false
|
84
106
|
requirements:
|
85
107
|
- - ! '>='
|
@@ -87,7 +109,7 @@ dependencies:
|
|
87
109
|
version: '0'
|
88
110
|
type: :development
|
89
111
|
prerelease: false
|
90
|
-
version_requirements: *
|
112
|
+
version_requirements: *20148580
|
91
113
|
description: A simple yet powerful webframework largely inspired by Rum and Cuba
|
92
114
|
email: dev@trense.info
|
93
115
|
executables: []
|
@@ -99,8 +121,10 @@ files:
|
|
99
121
|
- LICENSE.txt
|
100
122
|
- VERSION
|
101
123
|
- lib/mojito.rb
|
124
|
+
- lib/mojito/base.rb
|
102
125
|
- lib/mojito/helpers.rb
|
103
126
|
- lib/mojito/helpers/exception_handling.rb
|
127
|
+
- lib/mojito/helpers/shortcuts.rb
|
104
128
|
- lib/mojito/matchers.rb
|
105
129
|
- lib/mojito/matchers/methods.rb
|
106
130
|
- lib/mojito/matchers/path.rb
|
@@ -109,10 +133,14 @@ files:
|
|
109
133
|
- lib/mojito/rendering/content.rb
|
110
134
|
- lib/mojito/rendering/content_types.rb
|
111
135
|
- lib/mojito/rendering/delegation.rb
|
136
|
+
- lib/mojito/rendering/file.rb
|
112
137
|
- lib/mojito/rendering/status_codes.rb
|
138
|
+
- lib/mojito/request_extensions.rb
|
113
139
|
- lib/mojito/utils/status_codes.rb
|
140
|
+
- spec/mojito/helpers_spec.rb
|
114
141
|
- spec/mojito/matchers_spec.rb
|
115
142
|
- spec/mojito/rendering_spec.rb
|
143
|
+
- spec/mojito/request_extensions_spec.rb
|
116
144
|
- spec/mojito_spec.rb
|
117
145
|
- README.textile
|
118
146
|
homepage: http://github.com/mtrense/mojito
|
@@ -127,7 +155,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
127
155
|
requirements:
|
128
156
|
- - ! '>='
|
129
157
|
- !ruby/object:Gem::Version
|
130
|
-
version:
|
158
|
+
version: 1.9.0
|
131
159
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
160
|
none: false
|
133
161
|
requirements:
|