wunderbar 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
|