gnarly 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/gnarly/application.rb +61 -6
- data/lib/gnarly/base.rb +9 -5
- data/lib/gnarly/environment.rb +21 -0
- data/lib/gnarly/haml.rb +40 -0
- data/lib/gnarly/provides.rb +5 -15
- data/lib/gnarly/request.rb +10 -9
- data/lib/gnarly/status.rb +22 -0
- data/lib/gnarly/system.rb +35 -0
- data/lib/gnarly/understands.rb +21 -0
- data/lib/gnarly/url.rb +111 -14
- data/lib/gnarly.rb +1 -0
- metadata +7 -3
data/lib/gnarly/application.rb
CHANGED
@@ -1,28 +1,81 @@
|
|
1
|
-
require 'gnarly/provides.rb'
|
2
1
|
require 'gnarly/url.rb'
|
2
|
+
require 'gnarly/environment.rb'
|
3
3
|
|
4
4
|
module Gnarly
|
5
5
|
|
6
6
|
class Application
|
7
|
-
include
|
7
|
+
include Status
|
8
8
|
|
9
9
|
def initialize()
|
10
10
|
@urls = []
|
11
|
+
@environment = Environment.new self
|
12
|
+
end
|
13
|
+
|
14
|
+
def init(&block)
|
15
|
+
@environment.instance_exec &block
|
11
16
|
end
|
12
17
|
|
13
18
|
def url(url_mask, &block)
|
14
19
|
url = Url.new(url_mask)
|
15
|
-
url.instance_eval &block
|
20
|
+
url.instance_eval &block if block
|
16
21
|
@urls << url
|
22
|
+
url
|
23
|
+
end
|
24
|
+
|
25
|
+
def helper(helper)
|
26
|
+
(@helpers ||= []) << helper
|
17
27
|
end
|
18
28
|
|
19
|
-
def
|
20
|
-
|
29
|
+
def get(*args, &block)
|
30
|
+
m = args.shift
|
31
|
+
u = url m
|
32
|
+
u.get *args, &block
|
33
|
+
end
|
34
|
+
|
35
|
+
def put(*args, &block)
|
36
|
+
m = args.shift
|
37
|
+
u = url m
|
38
|
+
u.put *args, &block
|
39
|
+
end
|
40
|
+
|
41
|
+
def post(*args, &block)
|
42
|
+
m = args.shift
|
43
|
+
u = url m
|
44
|
+
u.post *args, &block
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(*args, &block)
|
48
|
+
m = args.shift
|
49
|
+
u = url m
|
50
|
+
u.delete *args, &block
|
51
|
+
end
|
52
|
+
|
53
|
+
def call(state)
|
54
|
+
environment = @environment.dup
|
55
|
+
environment.extend *@helpers if @helpers
|
56
|
+
environment.state state
|
57
|
+
request = environment.request
|
21
58
|
path = request.path
|
22
59
|
if url = @urls.detect { |u| u.match? path }
|
23
60
|
@responder = url.responder request.method
|
24
61
|
if @responder
|
25
|
-
|
62
|
+
helpers = url.helpers
|
63
|
+
environment.extend *helpers if helpers
|
64
|
+
if before = url.before
|
65
|
+
environment.instance_exec *url.parameters, &before
|
66
|
+
end
|
67
|
+
keys = url.keys request.method
|
68
|
+
response = environment.instance_exec *url.parameters(*keys), &@responder
|
69
|
+
if after = url.after
|
70
|
+
environment.instance_exec *url.parameters, &after
|
71
|
+
end
|
72
|
+
unless response.is_a? Array and response.size == 3
|
73
|
+
name, line = @responder.source_location
|
74
|
+
msg = "Bad return type for block in #{name} line #{line}"
|
75
|
+
internal_server_error msg
|
76
|
+
else
|
77
|
+
response
|
78
|
+
end
|
26
79
|
else
|
27
80
|
method_not_allowed url.allow
|
28
81
|
end
|
@@ -31,6 +84,8 @@ module Gnarly
|
|
31
84
|
end
|
32
85
|
end
|
33
86
|
|
87
|
+
private
|
88
|
+
|
34
89
|
end
|
35
90
|
|
36
91
|
def self.application(&block)
|
data/lib/gnarly/base.rb
CHANGED
@@ -4,16 +4,20 @@ module Gnarly
|
|
4
4
|
|
5
5
|
module Base
|
6
6
|
|
7
|
-
def
|
8
|
-
if
|
9
|
-
@
|
7
|
+
def state(state=nil)
|
8
|
+
if state
|
9
|
+
@state = state
|
10
10
|
@request = nil
|
11
11
|
end
|
12
|
-
@
|
12
|
+
@state
|
13
13
|
end
|
14
14
|
|
15
15
|
def request()
|
16
|
-
@request ||= Request.new @
|
16
|
+
@request ||= Request.new @state
|
17
|
+
end
|
18
|
+
|
19
|
+
def logger()
|
20
|
+
@state["rack.logger"]
|
17
21
|
end
|
18
22
|
|
19
23
|
def body_charset(body)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'gnarly/provides.rb'
|
2
|
+
require 'gnarly/understands.rb'
|
3
|
+
|
4
|
+
module Gnarly
|
5
|
+
|
6
|
+
class Environment
|
7
|
+
include Provides
|
8
|
+
include Understands
|
9
|
+
|
10
|
+
def initialize(app)
|
11
|
+
@application = app
|
12
|
+
push_default_content_type "text/plain"
|
13
|
+
end
|
14
|
+
|
15
|
+
def application()
|
16
|
+
@application
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/gnarly/haml.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'haml'
|
2
|
+
|
3
|
+
module Gnarly
|
4
|
+
|
5
|
+
module Haml
|
6
|
+
|
7
|
+
def haml(name, &block)
|
8
|
+
root = application.haml_root
|
9
|
+
template = File.read "#{root}/#{name}.haml"
|
10
|
+
engine = ::Haml::Engine.new template
|
11
|
+
body = engine.render self
|
12
|
+
if block
|
13
|
+
push_default_content_type "text/html"
|
14
|
+
result = block.call body
|
15
|
+
pop_default_content_type
|
16
|
+
result
|
17
|
+
else
|
18
|
+
body
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class Application
|
25
|
+
|
26
|
+
def haml_root(haml_root=nil)
|
27
|
+
if haml_root
|
28
|
+
@haml_root = haml_root
|
29
|
+
else
|
30
|
+
@haml_root ||= "haml"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class Environment
|
37
|
+
include Haml
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/lib/gnarly/provides.rb
CHANGED
@@ -11,25 +11,15 @@ module Gnarly
|
|
11
11
|
def provides(*mimes, &block)
|
12
12
|
mime = MIMEParse.best_match mimes, request.accept
|
13
13
|
if mime
|
14
|
+
push_default_content_type mime
|
14
15
|
response = block.call mime if block
|
15
|
-
|
16
|
-
|
17
|
-
unless headers.key? "Content-type"
|
18
|
-
charset = body_charset(body)
|
19
|
-
headers["Content-type"] =
|
20
|
-
charset ? "#{mime}; charset=#{charset}" : mime
|
21
|
-
end
|
22
|
-
response
|
23
|
-
else
|
24
|
-
name, line = @responder.source_location
|
25
|
-
msg = "Bad return type for block in #{filename} line #{line}"
|
26
|
-
internal_server_error msg
|
27
|
-
end
|
16
|
+
pop_default_content_type
|
17
|
+
response
|
28
18
|
else
|
29
19
|
not_acceptable
|
30
20
|
end
|
31
21
|
end
|
32
|
-
|
22
|
+
|
33
23
|
end
|
34
|
-
|
24
|
+
|
35
25
|
end
|
data/lib/gnarly/request.rb
CHANGED
@@ -3,22 +3,23 @@ require 'rack/utils'
|
|
3
3
|
module Gnarly
|
4
4
|
|
5
5
|
class Request
|
6
|
+
attr_reader :state
|
6
7
|
|
7
|
-
def initialize(
|
8
|
-
@
|
8
|
+
def initialize(state)
|
9
|
+
@state = state
|
9
10
|
parse_content_type
|
10
11
|
end
|
11
12
|
|
12
13
|
def path()
|
13
|
-
@path ||= @
|
14
|
+
@path ||= @state["PATH_INFO"]
|
14
15
|
end
|
15
16
|
|
16
17
|
def method()
|
17
|
-
@method ||= @
|
18
|
+
@method ||= @state["REQUEST_METHOD"].downcase.to_sym
|
18
19
|
end
|
19
20
|
|
20
21
|
def accept()
|
21
|
-
@accept ||= @
|
22
|
+
@accept ||= @state["HTTP_ACCEPT"]
|
22
23
|
end
|
23
24
|
|
24
25
|
def content_type()
|
@@ -31,7 +32,7 @@ module Gnarly
|
|
31
32
|
|
32
33
|
def body()
|
33
34
|
unless @body
|
34
|
-
input = @
|
35
|
+
input = @state["rack.input"]
|
35
36
|
@body = input.read
|
36
37
|
input.rewind
|
37
38
|
@body.force_encoding charset if charset
|
@@ -40,14 +41,14 @@ module Gnarly
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def params()
|
43
|
-
@params ||= ::Rack::Utils.parse_query(@
|
44
|
+
@params ||= ::Rack::Utils.parse_query(@state["QUERY_STRING"])
|
44
45
|
end
|
45
46
|
|
46
47
|
private
|
47
48
|
|
48
49
|
def parse_content_type()
|
49
|
-
if header = @
|
50
|
-
header =~ /^([^;\s]+)(\s*;\s*charset=(\S+)\s*)
|
50
|
+
if header = @state["CONTENT_TYPE"]
|
51
|
+
header =~ /^([^;\s]+)(\s*;\s*charset=(\S+)\s*)?$/
|
51
52
|
@content_type = $1
|
52
53
|
@charset = $3
|
53
54
|
end
|
data/lib/gnarly/status.rb
CHANGED
@@ -3,11 +3,29 @@ module Gnarly
|
|
3
3
|
|
4
4
|
module Status
|
5
5
|
|
6
|
+
def push_default_content_type(mime)
|
7
|
+
(@default_content_types ||= []).push mime
|
8
|
+
end
|
9
|
+
|
10
|
+
def pop_default_content_type()
|
11
|
+
@default_content_types.pop if @default_content_types
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_content_type()
|
15
|
+
@default_content_types.last if @default_content_types
|
16
|
+
end
|
17
|
+
|
6
18
|
def ok(body, mime=nil)
|
19
|
+
mime = default_content_type unless mime
|
7
20
|
headers = mime ? {"Content-type" => mime} : {}
|
8
21
|
[200, headers, [body]]
|
9
22
|
end
|
10
23
|
|
24
|
+
def created(location)
|
25
|
+
headers = {"Location" => location, "Content-type" => "text/plain"}
|
26
|
+
[201, headers, [""]]
|
27
|
+
end
|
28
|
+
|
11
29
|
def not_found(body="Not Found", mime="text/plain")
|
12
30
|
[404, {"Content-type" => mime}, [body]]
|
13
31
|
end
|
@@ -25,6 +43,10 @@ module Gnarly
|
|
25
43
|
[body]]
|
26
44
|
end
|
27
45
|
|
46
|
+
def unsupported_media_type(body="Unsupported Media Type", mime="text/plain")
|
47
|
+
[415, {"Content-type" => mime}, [body]]
|
48
|
+
end
|
49
|
+
|
28
50
|
def internal_server_error(body="Internal Server Error", mime="text/plain")
|
29
51
|
[500, {"Content-type" => mime}, [body]]
|
30
52
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
module Gnarly
|
3
|
+
|
4
|
+
class System
|
5
|
+
|
6
|
+
def initialize(*apps)
|
7
|
+
@apps = apps
|
8
|
+
end
|
9
|
+
|
10
|
+
def +(app)
|
11
|
+
@apps << app
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
response = nil
|
17
|
+
@apps.find do |app|
|
18
|
+
response = app.call(env)
|
19
|
+
status = response.first
|
20
|
+
status != 404 and status < 500
|
21
|
+
end
|
22
|
+
response
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class Application
|
28
|
+
|
29
|
+
def +(other)
|
30
|
+
System.new self, other
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'gnarly/base.rb'
|
2
|
+
require 'gnarly/status.rb'
|
3
|
+
|
4
|
+
module Gnarly
|
5
|
+
|
6
|
+
module Understands
|
7
|
+
include Base
|
8
|
+
include Status
|
9
|
+
|
10
|
+
def understands(*mimes, &block)
|
11
|
+
mime = MIMEParse.best_match [request.content_type], mimes.join(",")
|
12
|
+
if mime
|
13
|
+
block.call mime if block
|
14
|
+
else
|
15
|
+
unsupported_media_type
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/gnarly/url.rb
CHANGED
@@ -4,50 +4,147 @@ module Gnarly
|
|
4
4
|
class Url
|
5
5
|
|
6
6
|
def initialize(url_mask)
|
7
|
-
|
7
|
+
case url_mask
|
8
|
+
when String
|
9
|
+
@url_mask = make_url_mask url_mask
|
10
|
+
when Regexp
|
11
|
+
@url_mask = url_mask
|
12
|
+
end
|
8
13
|
@responders = {}
|
14
|
+
@keys = {}
|
9
15
|
@allow = []
|
10
16
|
end
|
11
17
|
|
12
18
|
def match?(path)
|
13
|
-
@match = @url_mask.match path
|
19
|
+
@match = @url_mask.match path if @url_mask
|
14
20
|
@match != nil
|
15
21
|
end
|
16
22
|
|
17
|
-
def parameters()
|
18
|
-
|
19
|
-
|
20
|
-
|
23
|
+
def parameters(*keys)
|
24
|
+
if keys.empty?
|
25
|
+
a = @match.to_a
|
26
|
+
a.shift
|
27
|
+
if @types
|
28
|
+
(0..a.size - 1).collect do |i|
|
29
|
+
case @types[i]
|
30
|
+
when :integer
|
31
|
+
a[i].to_i
|
32
|
+
else
|
33
|
+
a[i]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
else
|
37
|
+
a
|
38
|
+
end
|
39
|
+
else
|
40
|
+
if @type_map
|
41
|
+
keys.collect do |key|
|
42
|
+
case @type_map[key.to_s]
|
43
|
+
when :integer
|
44
|
+
@match[key].to_i
|
45
|
+
else
|
46
|
+
@match[key]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
else
|
50
|
+
keys.collect do |key|
|
51
|
+
@match[key]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
21
55
|
end
|
22
56
|
|
23
57
|
def responder(method)
|
24
58
|
@responders[method]
|
25
59
|
end
|
26
60
|
|
61
|
+
def keys(method)
|
62
|
+
@keys[method]
|
63
|
+
end
|
64
|
+
|
27
65
|
def allow()
|
28
66
|
@allow.join " "
|
29
67
|
end
|
30
68
|
|
31
|
-
def
|
32
|
-
@
|
69
|
+
def helper(helper)
|
70
|
+
(@helpers ||= []) << helper
|
71
|
+
end
|
72
|
+
|
73
|
+
def helpers()
|
74
|
+
@helpers
|
75
|
+
end
|
76
|
+
|
77
|
+
def get(*keys, &block)
|
78
|
+
@responders[:get] = block
|
79
|
+
@keys[:get] = keys
|
33
80
|
@allow << "GET"
|
34
81
|
end
|
35
82
|
|
36
|
-
def put(&block)
|
37
|
-
@responders[
|
83
|
+
def put(*keys, &block)
|
84
|
+
@responders[:put] = block
|
85
|
+
@keys[:put] = keys
|
38
86
|
@allow << "PUT"
|
39
87
|
end
|
40
88
|
|
41
|
-
def post(&block)
|
42
|
-
@responders[
|
89
|
+
def post(*keys, &block)
|
90
|
+
@responders[:post] = block
|
91
|
+
@keys[:post] = keys
|
43
92
|
@allow << "POST"
|
44
93
|
end
|
45
94
|
|
46
|
-
def delete(&block)
|
47
|
-
@responders[
|
95
|
+
def delete(*keys, &block)
|
96
|
+
@responders[:delete] = block
|
97
|
+
@keys[:delete] = keys
|
48
98
|
@allow << "DELETE"
|
49
99
|
end
|
50
100
|
|
101
|
+
def before(&block)
|
102
|
+
if block
|
103
|
+
@before = block
|
104
|
+
else
|
105
|
+
@before
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def after(&block)
|
110
|
+
if block
|
111
|
+
@after = block
|
112
|
+
else
|
113
|
+
@after
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def make_url_mask(template)
|
118
|
+
types = []
|
119
|
+
type_map = {}
|
120
|
+
has_types = false
|
121
|
+
s = template.gsub(/\{([^\}]*)\}/) do
|
122
|
+
name, type = $1.split ":"
|
123
|
+
t = nil
|
124
|
+
p = case type
|
125
|
+
when "integer", "index"
|
126
|
+
has_types = true
|
127
|
+
t = :integer
|
128
|
+
types << t
|
129
|
+
'\d+'
|
130
|
+
else
|
131
|
+
types << nil
|
132
|
+
'[^/]+'
|
133
|
+
end
|
134
|
+
nc = ""
|
135
|
+
if name and name != ""
|
136
|
+
nc = "?<#{name}>"
|
137
|
+
type_map[name] = t
|
138
|
+
end
|
139
|
+
"(#{nc}#{p})"
|
140
|
+
end
|
141
|
+
if has_types
|
142
|
+
@types = types
|
143
|
+
@type_map = type_map
|
144
|
+
end
|
145
|
+
Regexp.new "^#{s}$"
|
146
|
+
end
|
147
|
+
|
51
148
|
end
|
52
149
|
|
53
150
|
end
|
data/lib/gnarly.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Kim Dalsgaard
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-03-08 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -29,10 +29,14 @@ extra_rdoc_files: []
|
|
29
29
|
files:
|
30
30
|
- lib/gnarly/application.rb
|
31
31
|
- lib/gnarly/base.rb
|
32
|
+
- lib/gnarly/environment.rb
|
33
|
+
- lib/gnarly/haml.rb
|
32
34
|
- lib/gnarly/mimeparse.rb
|
33
35
|
- lib/gnarly/provides.rb
|
34
36
|
- lib/gnarly/request.rb
|
35
37
|
- lib/gnarly/status.rb
|
38
|
+
- lib/gnarly/system.rb
|
39
|
+
- lib/gnarly/understands.rb
|
36
40
|
- lib/gnarly/url.rb
|
37
41
|
- lib/gnarly/uuid.rb
|
38
42
|
- lib/gnarly.rb
|