wunderbar 0.10.0 → 0.10.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +0 -9
- data/lib/wunderbar/builder.rb +53 -16
- data/lib/wunderbar/cgi-methods.rb +30 -58
- data/lib/wunderbar/environment.rb +25 -26
- data/lib/wunderbar/html-methods.rb +35 -27
- data/lib/wunderbar/job-control.rb +4 -2
- data/lib/wunderbar/rack.rb +40 -0
- data/lib/wunderbar/server.rb +53 -84
- data/lib/wunderbar/sinatra.rb +161 -0
- data/lib/wunderbar/version.rb +1 -1
- data/wunderbar.gemspec +3 -3
- metadata +6 -5
- data/lib/wunderbar/template.rb +0 -91
data/README.md
CHANGED
@@ -182,8 +182,6 @@ number of other convenience methods are defined:
|
|
182
182
|
* `_.post?` -- was this invoked via HTTP POST?
|
183
183
|
* `_.system` -- invokes a shell command, captures stdin, stdout, and stderr
|
184
184
|
* `_.submit` -- runs command (or block) as a deamon process
|
185
|
-
* `_.SELF` -- Request URI
|
186
|
-
* `_.SELF?` -- Request URI with '?' appended (avoids spoiling the cache)
|
187
185
|
|
188
186
|
Access to all of the builder _defined_ methods (typically these end in an esclamation mark) and all of the Wunderbar module methods can be accessed in this way. Examples:
|
189
187
|
|
@@ -262,9 +260,6 @@ output stream, which provides access to other useful methods, for example:
|
|
262
260
|
|
263
261
|
Globals provided
|
264
262
|
---
|
265
|
-
* `$cgi` - Common Gateway Interface
|
266
|
-
* `$params` - Access to parameters (read-only OpenStruct like interface)
|
267
|
-
* `$env` - Access to environment variables (read-only OpenStruct like interface)
|
268
263
|
* `$USER` - Host user id
|
269
264
|
* `$HOME` - Home directory
|
270
265
|
* `$SERVER` - Server name
|
@@ -292,10 +287,6 @@ HTML methods
|
|
292
287
|
Note that adding an exclamation mark to the end of the tag name disables this
|
293
288
|
behavior.
|
294
289
|
|
295
|
-
OpenStruct methods (for $params and $env)
|
296
|
-
---
|
297
|
-
* `untaint_if_match`: untaints value if it matches a regular expression
|
298
|
-
|
299
290
|
Builder extensions
|
300
291
|
---
|
301
292
|
* `indented_text!`: matches text indentation to markup
|
data/lib/wunderbar/builder.rb
CHANGED
@@ -188,17 +188,25 @@ module Wunderbar
|
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
191
|
-
class
|
192
|
-
def
|
191
|
+
class BuilderBase
|
192
|
+
def set_variables_from_params
|
193
|
+
@_scope.params.each do |key,value|
|
194
|
+
value = value.first if Array === value
|
195
|
+
value.gsub! "\r\n", "\n" if String === value
|
196
|
+
instance_variable_set "@#{key}", value if key =~ /^\w+$/
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class TextBuilder < BuilderBase
|
202
|
+
def initialize(scope)
|
193
203
|
require 'stringio'
|
194
204
|
@_target = StringIO.new
|
205
|
+
@_scope = scope
|
195
206
|
end
|
196
207
|
|
197
|
-
def encode(
|
198
|
-
|
199
|
-
instance_variable_set "@#{key}", value.first if key =~ /^\w+$/
|
200
|
-
end
|
201
|
-
|
208
|
+
def encode(&block)
|
209
|
+
set_variables_from_params
|
202
210
|
self.instance_eval(&block)
|
203
211
|
@_target.string
|
204
212
|
end
|
@@ -221,22 +229,35 @@ module Wunderbar
|
|
221
229
|
end
|
222
230
|
end
|
223
231
|
|
232
|
+
def _exception(*args)
|
233
|
+
exception = args.first
|
234
|
+
if exception.respond_to? :backtrace
|
235
|
+
Wunderbar.error exception.inspect
|
236
|
+
@_target.puts unless size == 0
|
237
|
+
@_target.puts exception.inspect
|
238
|
+
exception.backtrace.each do |frame|
|
239
|
+
next if CALLERS_TO_IGNORE.any? {|re| frame =~ re}
|
240
|
+
Wunderbar.warn " #{frame}"
|
241
|
+
@_target.puts " #{frame}"
|
242
|
+
end
|
243
|
+
else
|
244
|
+
super
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
224
248
|
def target!
|
225
249
|
@_target.string
|
226
250
|
end
|
227
251
|
end
|
228
252
|
|
229
|
-
class JsonBuilder
|
230
|
-
def initialize(scope
|
253
|
+
class JsonBuilder < BuilderBase
|
254
|
+
def initialize(scope)
|
231
255
|
@_scope = scope
|
232
256
|
@_target = {}
|
233
257
|
end
|
234
258
|
|
235
|
-
def encode(
|
236
|
-
|
237
|
-
instance_variable_set "@#{key}", value.first if key =~ /^\w+$/
|
238
|
-
end
|
239
|
-
|
259
|
+
def encode(&block)
|
260
|
+
set_variables_from_params
|
240
261
|
self.instance_eval(&block)
|
241
262
|
@_target
|
242
263
|
end
|
@@ -258,13 +279,13 @@ module Wunderbar
|
|
258
279
|
|
259
280
|
if args.length == 0
|
260
281
|
return self unless block
|
261
|
-
result = JsonBuilder.new.encode(&block)
|
282
|
+
result = JsonBuilder.new(@_scope).encode(&block)
|
262
283
|
elsif args.length == 1
|
263
284
|
result = args.first
|
264
285
|
|
265
286
|
if block
|
266
287
|
if Symbol === result or String === result
|
267
|
-
result = {result.to_s => JsonBuilder.new.encode(&block)}
|
288
|
+
result = {result.to_s => JsonBuilder.new(@_scope).encode(&block)}
|
268
289
|
else
|
269
290
|
result = result.map {|n| @_target = {}; block.call(n); @_target}
|
270
291
|
end
|
@@ -310,6 +331,22 @@ module Wunderbar
|
|
310
331
|
@_target = object
|
311
332
|
end
|
312
333
|
|
334
|
+
def _exception(*args)
|
335
|
+
exception = args.first
|
336
|
+
if exception.respond_to? :backtrace
|
337
|
+
Wunderbar.error exception.inspect
|
338
|
+
super(exception.inspect)
|
339
|
+
@_target['backtrace'] = []
|
340
|
+
exception.backtrace.each do |frame|
|
341
|
+
next if CALLERS_TO_IGNORE.any? {|re| frame =~ re}
|
342
|
+
Wunderbar.warn " #{frame}"
|
343
|
+
@_target['backtrace'] << frame
|
344
|
+
end
|
345
|
+
else
|
346
|
+
super
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
313
350
|
def target!
|
314
351
|
begin
|
315
352
|
JSON.pretty_generate(@_target)+ "\n"
|
@@ -1,77 +1,59 @@
|
|
1
1
|
module Wunderbar
|
2
|
-
|
3
2
|
module CGI
|
4
3
|
|
5
|
-
HIDE_FRAME = [ %r{/(wunderbar|webrick)/},
|
6
|
-
%r{/gems/.*/(builder|rack|sinatra)/} ]
|
7
|
-
|
8
4
|
# produce json
|
9
|
-
def self.json(&block)
|
5
|
+
def self.json(scope, &block)
|
10
6
|
headers = { 'type' => 'application/json', 'Cache-Control' => 'no-cache' }
|
11
|
-
builder = JsonBuilder.new
|
12
|
-
output = builder.encode(
|
7
|
+
builder = JsonBuilder.new(scope)
|
8
|
+
output = builder.encode(&block)
|
13
9
|
headers['status'] = "404 Not Found" if output == {}
|
14
10
|
rescue Exception => exception
|
15
|
-
Wunderbar.error exception.inspect
|
16
11
|
headers['status'] = "500 Internal Server Error"
|
17
|
-
|
18
|
-
|
19
|
-
next if HIDE_FRAME.any? {|re| frame =~ re}
|
20
|
-
Wunderbar.warn " #{frame}"
|
21
|
-
backtrace << frame
|
22
|
-
end
|
23
|
-
builder = JsonBuilder.new
|
24
|
-
builder._exception exception.inspect
|
25
|
-
builder._backtrace backtrace
|
12
|
+
builder._! Hash.new
|
13
|
+
builder._exception exception
|
26
14
|
ensure
|
27
|
-
out?(headers) { builder.target! }
|
15
|
+
out?(scope, headers) { builder.target! }
|
28
16
|
end
|
29
17
|
|
30
18
|
# produce text
|
31
|
-
def self.text &block
|
19
|
+
def self.text(scope, &block)
|
32
20
|
headers = {'type' => 'text/plain', 'charset' => 'UTF-8'}
|
33
|
-
builder = TextBuilder.new
|
34
|
-
output = builder.encode(
|
21
|
+
builder = TextBuilder.new(scope)
|
22
|
+
output = builder.encode(&block)
|
35
23
|
headers['status'] = "404 Not Found" if output == ''
|
36
24
|
rescue Exception => exception
|
37
|
-
Wunderbar.error exception.inspect
|
38
25
|
headers['status'] = "500 Internal Server Error"
|
39
|
-
builder.
|
40
|
-
builder.puts exception.inspect
|
41
|
-
exception.backtrace.each do |frame|
|
42
|
-
next if HIDE_FRAME.any? {|re| frame =~ re}
|
43
|
-
Wunderbar.warn " #{frame}"
|
44
|
-
builder.puts " #{frame}"
|
45
|
-
end
|
26
|
+
builder._exception exception
|
46
27
|
ensure
|
47
|
-
out?(headers) { builder.target! }
|
28
|
+
out?(scope, headers) { builder.target! }
|
48
29
|
end
|
49
30
|
|
50
31
|
# Conditionally provide output, based on ETAG
|
51
|
-
def self.out?(headers, &block)
|
32
|
+
def self.out?(scope, headers, &block)
|
52
33
|
content = block.call
|
53
34
|
require 'digest/md5'
|
54
35
|
etag = Digest::MD5.hexdigest(content)
|
55
36
|
|
56
|
-
if
|
37
|
+
if scope.env['HTTP_IF_NONE_MATCH'] == etag.inspect
|
57
38
|
headers['Date'] = ::CGI.rfc1123_date(Time.now)
|
58
|
-
|
39
|
+
scope.out headers.merge('status' => '304 Not Modified') do
|
59
40
|
''
|
60
41
|
end
|
61
42
|
else
|
62
|
-
|
43
|
+
scope.out headers.merge('Etag' => etag.inspect) do
|
63
44
|
content
|
64
45
|
end
|
65
46
|
end
|
66
|
-
rescue
|
47
|
+
rescue Exception => exception
|
48
|
+
Wunderbar.fatal exception.inspect
|
67
49
|
end
|
68
50
|
|
69
51
|
# produce html/xhtml
|
70
|
-
def self.html(*args, &block)
|
52
|
+
def self.html(scope, *args, &block)
|
71
53
|
headers = { 'type' => 'text/html', 'charset' => 'UTF-8' }
|
72
54
|
headers['type'] = 'application/xhtml+xml' if @xhtml
|
73
55
|
|
74
|
-
x = HtmlMarkup.new
|
56
|
+
x = HtmlMarkup.new(scope)
|
75
57
|
|
76
58
|
begin
|
77
59
|
if @xhtml
|
@@ -88,28 +70,18 @@ module Wunderbar
|
|
88
70
|
end
|
89
71
|
_body do
|
90
72
|
_h1 'Internal Server Error'
|
91
|
-
|
92
|
-
Wunderbar.error text
|
93
|
-
exception.backtrace.each do |frame|
|
94
|
-
next if HIDE_FRAME.any? {|re| frame =~ re}
|
95
|
-
Wunderbar.warn " #{frame}"
|
96
|
-
text += "\n #{frame}"
|
97
|
-
end
|
98
|
-
|
99
|
-
_pre text
|
73
|
+
_exception exception
|
100
74
|
end
|
101
75
|
end
|
102
76
|
end
|
103
77
|
|
104
|
-
out?(headers) { output }
|
78
|
+
out?(scope, headers) { output }
|
105
79
|
end
|
106
80
|
|
107
|
-
def self.call(
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
accept = $env.HTTP_ACCEPT.to_s
|
112
|
-
request_uri = $env.REQUEST_URI.to_s
|
81
|
+
def self.call(scope)
|
82
|
+
env = scope.env
|
83
|
+
accept = env['HTTP_ACCEPT'].to_s
|
84
|
+
request_uri = env['REQUEST_URI'].to_s
|
113
85
|
|
114
86
|
# implied request types
|
115
87
|
xhr_json = Wunderbar::Options::XHR_JSON || (accept =~ /json/)
|
@@ -118,8 +90,8 @@ module Wunderbar
|
|
118
90
|
@xhtml = (accept =~ /xhtml/ or accept == '')
|
119
91
|
|
120
92
|
# overrides via the uri query parameter
|
121
|
-
xhr_json
|
122
|
-
text
|
93
|
+
xhr_json ||= (request_uri =~ /\?json$/)
|
94
|
+
text ||= (request_uri =~ /\?text$/)
|
123
95
|
|
124
96
|
# overrides via the command line
|
125
97
|
xhtml_override = ARGV.include?('--xhtml')
|
@@ -142,17 +114,17 @@ module Wunderbar
|
|
142
114
|
@xhtml = false if html_override
|
143
115
|
end
|
144
116
|
|
145
|
-
self.html(*args, &block)
|
117
|
+
self.html(scope, *args, &block)
|
146
118
|
return
|
147
119
|
end
|
148
120
|
when :json
|
149
121
|
if xhr_json
|
150
|
-
self.json(*args, &block)
|
122
|
+
self.json(scope, *args, &block)
|
151
123
|
return
|
152
124
|
end
|
153
125
|
when :text
|
154
126
|
if text
|
155
|
-
self.text(*args, &block)
|
127
|
+
self.text(scope, *args, &block)
|
156
128
|
return
|
157
129
|
end
|
158
130
|
end
|
@@ -11,38 +11,16 @@ module Wunderbar
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def self.SELF?
|
20
|
-
if SELF '?'
|
21
|
-
self.self
|
22
|
-
else
|
23
|
-
SELF + "?" # avoids spoiling the cache
|
14
|
+
class Scope
|
15
|
+
attr_accessor :env
|
16
|
+
def initialize(env)
|
17
|
+
@env = env
|
24
18
|
end
|
25
19
|
end
|
26
|
-
|
27
|
-
# was this invoked via HTTP POST?
|
28
|
-
def self.post?
|
29
|
-
$env.REQUEST_METHOD.to_s.upcase == 'POST'
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# environment objects
|
34
|
-
$env = {}
|
35
|
-
def $env.method_missing(name)
|
36
|
-
delete name.to_s if ENV[name.to_s] != self[name.to_s]
|
37
|
-
if ENV[name.to_s] and not has_key?(name.to_s)
|
38
|
-
self[name.to_s]=ENV[name.to_s].dup.extend(Wunderbar::Untaint)
|
39
|
-
end
|
40
|
-
self[name.to_s]
|
41
20
|
end
|
42
21
|
|
43
22
|
require 'socket'
|
44
23
|
$SERVER = ENV['HTTP_HOST'] || Socket::gethostname
|
45
|
-
$HOME = ENV['HOME'] ||= Dir.home() rescue nil
|
46
24
|
|
47
25
|
# set encoding to UTF-8
|
48
26
|
ENV['LANG'] ||= "en_US.UTF-8"
|
@@ -52,3 +30,24 @@ if defined? Encoding
|
|
52
30
|
else
|
53
31
|
$KCODE = 'U'
|
54
32
|
end
|
33
|
+
|
34
|
+
# Add methods to the 'main' object
|
35
|
+
if self.to_s == 'main'
|
36
|
+
class << self
|
37
|
+
def _html(*args, &block)
|
38
|
+
Wunderbar.html(*args, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def _xhtml(*args, &block)
|
42
|
+
Wunderbar.xhtml(*args, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def _json(*args, &block)
|
46
|
+
Wunderbar.json(*args, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def _text(*args, &block)
|
50
|
+
Wunderbar.text(*args, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# Wrapper class that understands HTML
|
2
|
-
class HtmlMarkup
|
2
|
+
class HtmlMarkup < Wunderbar::BuilderBase
|
3
3
|
VOID = %w(
|
4
4
|
area base br col command embed hr img input keygen
|
5
5
|
link meta param source track wbr
|
6
6
|
)
|
7
7
|
|
8
|
-
def initialize(scope
|
8
|
+
def initialize(scope)
|
9
9
|
@_scope = scope
|
10
10
|
@x = Wunderbar::XmlMarkup.new :scope => scope, :indent => 2, :target => []
|
11
11
|
@xthml = false
|
@@ -24,13 +24,8 @@ class HtmlMarkup
|
|
24
24
|
@x.text! "\xEF\xBB\xBF"
|
25
25
|
@x.declare! :DOCTYPE, :html
|
26
26
|
@x.tag! :html, *args do
|
27
|
-
|
28
|
-
|
29
|
-
value = value.first if Array === value
|
30
|
-
instance_variable_set "@#{key}", value if key =~ /^\w+$/
|
31
|
-
end
|
32
|
-
end
|
33
|
-
instance_exec(@x, &block)
|
27
|
+
set_variables_from_params
|
28
|
+
instance_eval(&block)
|
34
29
|
end
|
35
30
|
@x.target!.join
|
36
31
|
end
|
@@ -101,28 +96,13 @@ class HtmlMarkup
|
|
101
96
|
end
|
102
97
|
elsif flag == '?'
|
103
98
|
# capture exceptions, produce filtered tracebacks
|
104
|
-
options = (Hash === args.last)? args.last : {}
|
105
|
-
traceback_class = options.delete(:traceback_class)
|
106
|
-
traceback_style = options.delete(:traceback_style)
|
107
|
-
traceback_style ||= 'background-color:#ff0; margin: 1em 0; ' +
|
108
|
-
'padding: 1em; border: 4px solid red; border-radius: 1em'
|
109
99
|
@x.tag!(name, *args) do
|
110
100
|
begin
|
111
101
|
block.call
|
112
102
|
rescue ::Exception => exception
|
113
|
-
|
114
|
-
|
115
|
-
exception
|
116
|
-
next if Wunderbar::CGI::HIDE_FRAME.any? {|re| frame =~ re}
|
117
|
-
Wunderbar.warn " #{frame}"
|
118
|
-
text += "\n #{frame}"
|
119
|
-
end
|
120
|
-
|
121
|
-
if traceback_class
|
122
|
-
@x.tag! :pre, text, :class=>traceback_class
|
123
|
-
else
|
124
|
-
@x.tag! :pre, text, :style=>traceback_style
|
125
|
-
end
|
103
|
+
options = (Hash === args.last)? args.last : {}
|
104
|
+
options[:log_level] = 'warn'
|
105
|
+
_exception exception, options
|
126
106
|
end
|
127
107
|
end
|
128
108
|
else
|
@@ -130,6 +110,34 @@ class HtmlMarkup
|
|
130
110
|
end
|
131
111
|
end
|
132
112
|
|
113
|
+
def _exception(*args)
|
114
|
+
exception = args.first
|
115
|
+
if exception.respond_to? :backtrace
|
116
|
+
options = (Hash === args.last)? args.last : {}
|
117
|
+
traceback_class = options.delete(:traceback_class)
|
118
|
+
traceback_style = options.delete(:traceback_style)
|
119
|
+
traceback_style ||= 'background-color:#ff0; margin: 1em 0; ' +
|
120
|
+
'padding: 1em; border: 4px solid red; border-radius: 1em'
|
121
|
+
|
122
|
+
text = exception.inspect
|
123
|
+
log_level = options.delete(:log_level) || :error
|
124
|
+
Wunderbar.send log_level, text
|
125
|
+
exception.backtrace.each do |frame|
|
126
|
+
next if Wunderbar::CALLERS_TO_IGNORE.any? {|re| frame =~ re}
|
127
|
+
Wunderbar.send log_level, " #{frame}"
|
128
|
+
text += "\n #{frame}"
|
129
|
+
end
|
130
|
+
|
131
|
+
if traceback_class
|
132
|
+
@x.tag! :pre, text, :class=>traceback_class
|
133
|
+
else
|
134
|
+
@x.tag! :pre, text, :style=>traceback_style
|
135
|
+
end
|
136
|
+
else
|
137
|
+
super
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
133
141
|
def _head(*args, &block)
|
134
142
|
@x.tag!('head', *args) do
|
135
143
|
@x.tag! :meta, :charset => 'utf-8'
|
@@ -16,8 +16,10 @@ module Wunderbar
|
|
16
16
|
STDERR.reopen STDOUT
|
17
17
|
|
18
18
|
# clear environment of cgi cruft
|
19
|
-
|
20
|
-
|
19
|
+
require 'cgi'
|
20
|
+
ENV.delete_if {|key,value| key =~ /^HTTP_/}
|
21
|
+
CGI::QueryExtension.public_instance_methods.each do |method|
|
22
|
+
ENV.delete method.to_s.upcase
|
21
23
|
end
|
22
24
|
|
23
25
|
# setup environment
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Wunderbar
|
2
|
+
class RackApp
|
3
|
+
# entry point for Rack
|
4
|
+
def call(env)
|
5
|
+
@_env = env
|
6
|
+
@_request = Rack::Request.new(env)
|
7
|
+
@_response = Rack::Response.new
|
8
|
+
Wunderbar.logger = @_request.logger
|
9
|
+
Wunderbar::CGI.call(self)
|
10
|
+
@_response.finish
|
11
|
+
end
|
12
|
+
|
13
|
+
# redirect the output produced
|
14
|
+
def out(headers,&block)
|
15
|
+
status = headers.delete('status')
|
16
|
+
@_response.status = status if status
|
17
|
+
|
18
|
+
headers = Wunderbar::CGI.headers(headers)
|
19
|
+
headers.each {|key, value| @_response[key] = value}
|
20
|
+
|
21
|
+
@_response.write block.call unless @_request.head?
|
22
|
+
end
|
23
|
+
|
24
|
+
def env
|
25
|
+
@_env
|
26
|
+
end
|
27
|
+
|
28
|
+
def params
|
29
|
+
@_request.params
|
30
|
+
end
|
31
|
+
|
32
|
+
def request
|
33
|
+
@_request
|
34
|
+
end
|
35
|
+
|
36
|
+
def response
|
37
|
+
@_response
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/wunderbar/server.rb
CHANGED
@@ -1,112 +1,81 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
2
|
+
# http://rubydoc.info/gems/sinatra/Sinatra/Application
|
3
|
+
# http://www.ruby-doc.org/stdlib-1.9.3/libdoc/cgi/rdoc/CGI.html#public-class-method-details
|
4
|
+
|
5
|
+
module Wunderbar
|
5
6
|
|
6
|
-
|
7
|
+
CALLERS_TO_IGNORE = [
|
8
|
+
%r{/(wunderbar|webrick)/},
|
9
|
+
%r{<internal:},
|
10
|
+
%r{/gems/.*/lib/(builder|rack|sinatra|tilt)/}
|
11
|
+
]
|
7
12
|
|
13
|
+
end
|
14
|
+
|
15
|
+
at_exit do
|
8
16
|
port = ARGV.find {|arg| arg =~ /--port=(.*)/}
|
9
17
|
if port and ARGV.delete(port)
|
10
18
|
port = $1.to_i
|
11
19
|
|
12
|
-
# entry point for Rack
|
13
|
-
def $cgi.call(env)
|
14
|
-
@request = Rack::Request.new(env)
|
15
|
-
@response = Rack::Response.new
|
16
|
-
$env = OpenStruct.new(env)
|
17
|
-
$params = @request.params
|
18
|
-
|
19
|
-
Wunderbar::CGI.call(env)
|
20
|
-
@response.finish
|
21
|
-
end
|
22
|
-
|
23
|
-
# redirect the output produced
|
24
|
-
def $cgi.out(headers,&block)
|
25
|
-
status = headers.delete('status')
|
26
|
-
@response.status = status if status
|
27
|
-
|
28
|
-
headers = Wunderbar::CGI.headers(headers)
|
29
|
-
headers.each { |key, value| @response[key] = value }
|
30
|
-
|
31
|
-
@response.write block.call unless @request.head?
|
32
|
-
end
|
33
|
-
|
34
20
|
# Evaluate optional data from the script (after __END__)
|
35
21
|
eval Wunderbar.data if Object.const_defined? :DATA
|
36
22
|
|
23
|
+
# Allow optional environment override
|
24
|
+
environment = ARGV.find {|arg| arg =~ /--environment=(.*)/}
|
25
|
+
ENV['RACK_ENV'] = environment if environment and ARGV.delete(environment)
|
26
|
+
|
37
27
|
# start the server
|
38
28
|
require 'rack'
|
39
|
-
require 'rack
|
40
|
-
app
|
41
|
-
|
29
|
+
require 'wunderbar/rack'
|
30
|
+
Rack::Server.start :app => Wunderbar::RackApp.new, :Port => port,
|
31
|
+
:environment => (ENV['RACK_ENV'] || 'development')
|
42
32
|
|
43
33
|
elsif defined? Sinatra
|
44
34
|
|
45
|
-
require 'wunderbar/
|
46
|
-
Tilt.register '_html', Wunderbar::Template::Html
|
47
|
-
Tilt.register '_xhtml', Wunderbar::Template::Xhtml
|
48
|
-
Tilt.register '_json', Wunderbar::Template::Json
|
49
|
-
Tilt.register '_text', Wunderbar::Template::Text
|
50
|
-
|
51
|
-
# define helpers
|
52
|
-
helpers do
|
53
|
-
def _html(*args, &block)
|
54
|
-
if block
|
55
|
-
Wunderbar::Template::Html.evaluate('_html', self) do
|
56
|
-
_html(*args) { instance_eval &block }
|
57
|
-
end
|
58
|
-
else
|
59
|
-
Wunderbar::Template::Html.evaluate('_html', self, *args)
|
60
|
-
end
|
61
|
-
end
|
35
|
+
require 'wunderbar/sinatra'
|
62
36
|
|
63
|
-
|
64
|
-
if env['HTTP_ACCEPT'] and not env['HTTP_ACCEPT'].include? 'xhtml'
|
65
|
-
return _html(*args, &block)
|
66
|
-
end
|
67
|
-
|
68
|
-
if block
|
69
|
-
Wunderbar::Template::Xhtml.evaluate('_xhtml', self) do
|
70
|
-
_xhtml(*args) { instance_eval &block }
|
71
|
-
end
|
72
|
-
else
|
73
|
-
Wunderbar::Template::Xhtml.evaluate('_xhtml', self, *args)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def _json(*args, &block)
|
78
|
-
Wunderbar::Template::Json.evaluate('_json', self, *args, &block)
|
79
|
-
end
|
80
|
-
|
81
|
-
def _text(*args, &block)
|
82
|
-
Wunderbar::Template::Text.evaluate('_text', self, *args, &block)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
else
|
37
|
+
elsif Wunderbar.queue.length > 0
|
87
38
|
|
88
39
|
# allow the REQUEST_METHOD to be set for command line invocations
|
89
40
|
ENV['REQUEST_METHOD'] ||= 'POST' if ARGV.delete('--post')
|
90
41
|
ENV['REQUEST_METHOD'] ||= 'GET' if ARGV.delete('--get')
|
91
42
|
|
92
|
-
#
|
93
|
-
|
43
|
+
# Only prompt if explicitly asked for
|
44
|
+
ARGV.push '' if ARGV.empty?
|
45
|
+
ARGV.delete('--prompt') or ARGV.delete('--offline')
|
94
46
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
self[name.to_s].join
|
47
|
+
cgi = CGI.new
|
48
|
+
cgi.instance_variable_set '@env', ENV
|
49
|
+
class << cgi
|
50
|
+
attr_accessor :env
|
51
|
+
|
52
|
+
# was this invoked via HTTP POST?
|
53
|
+
%w(delete get head options post put trace).each do |http_method|
|
54
|
+
define_method "#{http_method}?" do
|
55
|
+
env['REQUEST_METHOD'].to_s.downcase == http_method
|
105
56
|
end
|
106
57
|
end
|
107
58
|
end
|
108
59
|
|
60
|
+
# get arguments if CGI couldn't find any...
|
61
|
+
cgi.params.merge!(CGI.parse(ARGV.join('&'))) if cgi.params.empty?
|
62
|
+
|
63
|
+
require 'etc'
|
64
|
+
$USER = ENV['REMOTE_USER'] ||= ENV['USER'] || Etc.getlogin
|
65
|
+
if $USER.nil?
|
66
|
+
if RUBY_PLATFORM =~ /darwin/i
|
67
|
+
$USER = `dscl . -search /Users UniqueID #{Process.uid}`.split.first
|
68
|
+
elsif RUBY_PLATFORM =~ /linux/i
|
69
|
+
$USER = `getent passwd #{Process.uid}`.split(':').first
|
70
|
+
end
|
71
|
+
|
72
|
+
ENV['USER'] ||= $USER
|
73
|
+
end
|
74
|
+
|
75
|
+
ENV['HOME'] ||= Dir.home($USER) rescue nil
|
76
|
+
ENV['HOME'] = ENV['DOCUMENT_ROOT'] if not File.exist? ENV['HOME'].to_s
|
77
|
+
|
109
78
|
# CGI or command line
|
110
|
-
Wunderbar::CGI.call(
|
79
|
+
Wunderbar::CGI.call(cgi)
|
111
80
|
end
|
112
81
|
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
module Wunderbar
|
5
|
+
# Tilt template implementation
|
6
|
+
module Template
|
7
|
+
class Base < Tilt::Template
|
8
|
+
def self.engine_initialized?
|
9
|
+
defined? ::Wunderbar
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize_engine
|
13
|
+
require_template_library 'wunderbar'
|
14
|
+
end
|
15
|
+
|
16
|
+
def prepare
|
17
|
+
end
|
18
|
+
|
19
|
+
def precompiled_template(locals)
|
20
|
+
raise NotImplementedError.new("dynamic only")
|
21
|
+
end
|
22
|
+
|
23
|
+
def precompiled_preamble(locals)
|
24
|
+
raise NotImplementedError.new("dynamic only")
|
25
|
+
end
|
26
|
+
|
27
|
+
def precompiled_postamble(locals)
|
28
|
+
raise NotImplementedError.new("dynamic only")
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.evaluate(template, scope, *args, &block)
|
32
|
+
scope.content_type default_mime_type
|
33
|
+
if block
|
34
|
+
output = new(&Proc.new {}).evaluate(scope, {}, &block)
|
35
|
+
else
|
36
|
+
output = scope.send :render, template, *args
|
37
|
+
end
|
38
|
+
scope.etag Digest::MD5.hexdigest(output)
|
39
|
+
output
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def _evaluate(builder, scope, locals, &block)
|
45
|
+
builder.instance_eval do
|
46
|
+
scope.params.merge(locals).each do |key,value|
|
47
|
+
value = value.first if ::Array === value
|
48
|
+
instance_variable_set "@#{key}", value if key =~ /^[a-z]\w+$/
|
49
|
+
end
|
50
|
+
end
|
51
|
+
if block
|
52
|
+
builder.instance_eval(&block)
|
53
|
+
elsif data
|
54
|
+
builder.instance_eval(data, eval_file)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Html < Base
|
60
|
+
self.default_mime_type = 'text/html'
|
61
|
+
|
62
|
+
def evaluate(scope, locals, &block)
|
63
|
+
builder = HtmlMarkup.new(scope)
|
64
|
+
begin
|
65
|
+
_evaluate(builder, scope, locals, &block)
|
66
|
+
rescue Exception => exception
|
67
|
+
scope.response.status = 500
|
68
|
+
builder.clear!
|
69
|
+
builder.html do
|
70
|
+
_head do
|
71
|
+
_title 'Internal Server Error'
|
72
|
+
end
|
73
|
+
_body do
|
74
|
+
_h1 'Internal Server Error'
|
75
|
+
_exception exception
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
builder._.target!.join
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Xhtml < Html
|
84
|
+
self.default_mime_type = 'application/xhtml+xml'
|
85
|
+
end
|
86
|
+
|
87
|
+
class Json < Base
|
88
|
+
self.default_mime_type = 'application/json'
|
89
|
+
|
90
|
+
def evaluate(scope, locals, &block)
|
91
|
+
builder = JsonBuilder.new(scope)
|
92
|
+
begin
|
93
|
+
_evaluate(builder, scope, locals, &block)
|
94
|
+
rescue Exception => exception
|
95
|
+
scope.content_type self.class.default_mime_type, :charset => 'utf-8'
|
96
|
+
scope.response.status = 500
|
97
|
+
builder._exception exception
|
98
|
+
end
|
99
|
+
builder.target!
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Text < Base
|
104
|
+
self.default_mime_type = 'text/plain'
|
105
|
+
|
106
|
+
def evaluate(scope, locals, &block)
|
107
|
+
builder = TextBuilder.new(scope)
|
108
|
+
begin
|
109
|
+
_evaluate(builder, scope, locals, &block)
|
110
|
+
scope.response.status = 404 if builder.target!.empty?
|
111
|
+
rescue Exception => exception
|
112
|
+
scope.headers['Content-Type'] = self.class.default_mime_type
|
113
|
+
scope.response.status = 500
|
114
|
+
builder._exception exception
|
115
|
+
end
|
116
|
+
builder.target!
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
module SinatraHelpers
|
122
|
+
def _html(*args, &block)
|
123
|
+
if block
|
124
|
+
Wunderbar::Template::Html.evaluate('_html', self) do
|
125
|
+
_html(*args) { instance_eval &block }
|
126
|
+
end
|
127
|
+
else
|
128
|
+
Wunderbar::Template::Html.evaluate('_html', self, *args)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def _xhtml(*args, &block)
|
133
|
+
if env['HTTP_ACCEPT'] and not env['HTTP_ACCEPT'].include? 'xhtml'
|
134
|
+
return _html(*args, &block)
|
135
|
+
end
|
136
|
+
|
137
|
+
if block
|
138
|
+
Wunderbar::Template::Xhtml.evaluate('_xhtml', self) do
|
139
|
+
_xhtml(*args) { instance_eval &block }
|
140
|
+
end
|
141
|
+
else
|
142
|
+
Wunderbar::Template::Xhtml.evaluate('_xhtml', self, *args)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def _json(*args, &block)
|
147
|
+
Wunderbar::Template::Json.evaluate('_json', self, *args, &block)
|
148
|
+
end
|
149
|
+
|
150
|
+
def _text(*args, &block)
|
151
|
+
Wunderbar::Template::Text.evaluate('_text', self, *args, &block)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
Tilt.register '_html', Wunderbar::Template::Html
|
157
|
+
Tilt.register '_xhtml', Wunderbar::Template::Xhtml
|
158
|
+
Tilt.register '_json', Wunderbar::Template::Json
|
159
|
+
Tilt.register '_text', Wunderbar::Template::Text
|
160
|
+
|
161
|
+
helpers Wunderbar::SinatraHelpers
|
data/lib/wunderbar/version.rb
CHANGED
data/wunderbar.gemspec
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "wunderbar"
|
5
|
-
s.version = "0.10.
|
5
|
+
s.version = "0.10.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Sam Ruby"]
|
9
|
-
s.date = "2012-04-
|
9
|
+
s.date = "2012-04-12"
|
10
10
|
s.description = " Wunderbar makes it easy to produce valid HTML5, wellformed XHTML, Unicode\n (utf-8), consistently indented, readable applications. This includes\n output that conforms to the Polyglot specification and the emerging\n results from the XML Error Recovery Community Group.\n"
|
11
11
|
s.email = "rubys@intertwingly.net"
|
12
|
-
s.files = ["wunderbar.gemspec", "README.md", "COPYING", "lib/wunderbar.rb", "lib/wunderbar", "lib/wunderbar/installation.rb", "lib/wunderbar/html-methods.rb", "lib/wunderbar/job-control.rb", "lib/wunderbar/
|
12
|
+
s.files = ["wunderbar.gemspec", "README.md", "COPYING", "lib/wunderbar.rb", "lib/wunderbar", "lib/wunderbar/installation.rb", "lib/wunderbar/html-methods.rb", "lib/wunderbar/job-control.rb", "lib/wunderbar/server.rb", "lib/wunderbar/logger.rb", "lib/wunderbar/rack.rb", "lib/wunderbar/builder.rb", "lib/wunderbar/sinatra.rb", "lib/wunderbar/environment.rb", "lib/wunderbar/cgi-methods.rb", "lib/wunderbar/cssproxy.rb", "lib/wunderbar/version.rb"]
|
13
13
|
s.homepage = "http://github.com/rubys/wunderbar"
|
14
14
|
s.require_paths = ["lib"]
|
15
15
|
s.rubygems_version = "1.8.21"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wunderbar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 53
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 10
|
9
|
-
-
|
10
|
-
version: 0.10.
|
9
|
+
- 1
|
10
|
+
version: 0.10.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sam Ruby
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-04-
|
18
|
+
date: 2012-04-12 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: builder
|
@@ -62,10 +62,11 @@ files:
|
|
62
62
|
- lib/wunderbar/installation.rb
|
63
63
|
- lib/wunderbar/html-methods.rb
|
64
64
|
- lib/wunderbar/job-control.rb
|
65
|
-
- lib/wunderbar/template.rb
|
66
65
|
- lib/wunderbar/server.rb
|
67
66
|
- lib/wunderbar/logger.rb
|
67
|
+
- lib/wunderbar/rack.rb
|
68
68
|
- lib/wunderbar/builder.rb
|
69
|
+
- lib/wunderbar/sinatra.rb
|
69
70
|
- lib/wunderbar/environment.rb
|
70
71
|
- lib/wunderbar/cgi-methods.rb
|
71
72
|
- lib/wunderbar/cssproxy.rb
|
data/lib/wunderbar/template.rb
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
require 'tilt/template'
|
2
|
-
|
3
|
-
module Wunderbar
|
4
|
-
# Tilt template implementation
|
5
|
-
module Template
|
6
|
-
class Base < Tilt::Template
|
7
|
-
def self.engine_initialized?
|
8
|
-
defined? ::Wunderbar
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize_engine
|
12
|
-
require_template_library 'wunderbar'
|
13
|
-
end
|
14
|
-
|
15
|
-
def prepare
|
16
|
-
end
|
17
|
-
|
18
|
-
def precompiled_template(locals)
|
19
|
-
raise NotImplementedError.new("dynamic only")
|
20
|
-
end
|
21
|
-
|
22
|
-
def precompiled_preamble(locals)
|
23
|
-
raise NotImplementedError.new("dynamic only")
|
24
|
-
end
|
25
|
-
|
26
|
-
def precompiled_postamble(locals)
|
27
|
-
raise NotImplementedError.new("dynamic only")
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.evaluate(template, scope, *args, &block)
|
31
|
-
scope.content_type default_mime_type
|
32
|
-
if block
|
33
|
-
new(&Proc.new {}).evaluate(scope, {}, &block)
|
34
|
-
else
|
35
|
-
scope.send :render, template, *args
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def _evaluate(builder, scope, locals, &block)
|
42
|
-
builder.instance_eval do
|
43
|
-
scope.params.merge(locals).each do |key,value|
|
44
|
-
value = value.first if ::Array === value
|
45
|
-
instance_variable_set "@#{key}", value if key =~ /^[a-z]\w+$/
|
46
|
-
end
|
47
|
-
end
|
48
|
-
if block
|
49
|
-
builder.instance_eval(&block)
|
50
|
-
else
|
51
|
-
builder.instance_eval(data, eval_file)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class Html < Base
|
57
|
-
self.default_mime_type = 'text/html'
|
58
|
-
|
59
|
-
def evaluate(scope, locals, &block)
|
60
|
-
builder = HtmlMarkup.new(scope)
|
61
|
-
_evaluate(builder, scope, locals, &block)
|
62
|
-
builder._.target!.join
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
class Xhtml < Html
|
67
|
-
self.default_mime_type = 'application/xhtml+xml'
|
68
|
-
end
|
69
|
-
|
70
|
-
class Json < Base
|
71
|
-
self.default_mime_type = 'application/json'
|
72
|
-
|
73
|
-
def evaluate(scope, locals, &block)
|
74
|
-
builder = JsonBuilder.new(scope)
|
75
|
-
_evaluate(builder, scope, locals)
|
76
|
-
builder.target!
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
class Text < Base
|
81
|
-
self.default_mime_type = 'text/plain'
|
82
|
-
|
83
|
-
def evaluate(scope, locals, &block)
|
84
|
-
builder = JsonBuilder.new(scope)
|
85
|
-
_evaluate(builder, scope, locals)
|
86
|
-
builder.target!
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|