wunderbar 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +10 -14
- data/lib/wunderbar/builder.rb +42 -20
- data/lib/wunderbar/cgi-methods.rb +99 -70
- data/lib/wunderbar/environment.rb +23 -56
- data/lib/wunderbar/html-methods.rb +37 -7
- data/lib/wunderbar/installation.rb +16 -11
- data/lib/wunderbar/server.rb +112 -0
- data/lib/wunderbar/template.rb +91 -0
- data/lib/wunderbar/version.rb +1 -1
- data/lib/wunderbar.rb +3 -2
- data/wunderbar.gemspec +4 -4
- metadata +7 -5
data/README.md
CHANGED
@@ -181,7 +181,9 @@ number of other convenience methods are defined:
|
|
181
181
|
|
182
182
|
* `_.post?` -- was this invoked via HTTP POST?
|
183
183
|
* `_.system` -- invokes a shell command, captures stdin, stdout, and stderr
|
184
|
-
* `_.submit
|
184
|
+
* `_.submit` -- runs command (or block) as a deamon process
|
185
|
+
* `_.SELF` -- Request URI
|
186
|
+
* `_.SELF?` -- Request URI with '?' appended (avoids spoiling the cache)
|
185
187
|
|
186
188
|
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:
|
187
189
|
|
@@ -260,21 +262,14 @@ output stream, which provides access to other useful methods, for example:
|
|
260
262
|
|
261
263
|
Globals provided
|
262
264
|
---
|
263
|
-
* `$cgi`
|
264
|
-
* `$
|
265
|
-
* `$env`
|
266
|
-
* `$USER`
|
267
|
-
* `$HOME`
|
268
|
-
* `$SERVER
|
269
|
-
* `SELF` - Request URI
|
270
|
-
* `SELF?` - Request URI with '?' appended (avoids spoiling the cache)
|
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
|
+
* `$USER` - Host user id
|
269
|
+
* `$HOME` - Home directory
|
270
|
+
* `$SERVER` - Server name
|
271
271
|
* `$HOME` - user's home directory
|
272
272
|
* `$HOST` - server host
|
273
|
-
* `$HTTP_GET` - request is an HTTP GET
|
274
|
-
* `$HTTP_POST` - request is an HTTP POST
|
275
|
-
* `$XHR_JSON` - request is XmlHttpRequest for JSON
|
276
|
-
* `$XHTML` - user agent accepts XHTML responses
|
277
|
-
* `$TEXT` - user agent accepts plain text responses
|
278
273
|
|
279
274
|
Also, the following environment variables are set if they aren't already:
|
280
275
|
|
@@ -292,6 +287,7 @@ HTML methods
|
|
292
287
|
* `_svg`: insert svg namespace
|
293
288
|
* `_math`: insert math namespace
|
294
289
|
* `_coffeescript`: convert [coffeescript](http://coffeescript.org/) to JS and insert script tag
|
290
|
+
* `xhtml?`: output as XHTML?
|
295
291
|
|
296
292
|
Note that adding an exclamation mark to the end of the tag name disables this
|
297
293
|
behavior.
|
data/lib/wunderbar/builder.rb
CHANGED
@@ -75,29 +75,47 @@ module Wunderbar
|
|
75
75
|
end
|
76
76
|
|
77
77
|
class XmlMarkup
|
78
|
-
def initialize(
|
79
|
-
@
|
78
|
+
def initialize(args)
|
79
|
+
@_scope = args.delete(:scope)
|
80
|
+
@_builder = SpacedMarkup.new(args)
|
80
81
|
end
|
81
82
|
|
82
|
-
# forward to
|
83
|
+
# forward to Wunderbar, XmlMarkup, or @_scope
|
83
84
|
def method_missing(method, *args, &block)
|
84
85
|
if Wunderbar.respond_to? method
|
85
86
|
Wunderbar.send method, *args, &block
|
86
87
|
elsif SpacedMarkup.public_instance_methods.include? method
|
87
|
-
@
|
88
|
+
@_builder.__send__ method, *args, &block
|
88
89
|
elsif SpacedMarkup.public_instance_methods.include? method.to_s
|
89
|
-
@
|
90
|
+
@_builder.__send__ method, *args, &block
|
91
|
+
elsif @_scope and @_scope.respond_to? method
|
92
|
+
@_scope.send method, *args, &block
|
90
93
|
else
|
91
94
|
super
|
92
95
|
end
|
93
96
|
end
|
94
97
|
|
98
|
+
def methods
|
99
|
+
result = super + Wunderbar.methods
|
100
|
+
result += SpacedMarkup.public_instance_methods
|
101
|
+
result += @_scope.methods if @_scope
|
102
|
+
result.uniq
|
103
|
+
end
|
104
|
+
|
105
|
+
def respond_to?(method)
|
106
|
+
respond true if Wunderbar.respond_to? method
|
107
|
+
respond true if SpacedMarkup.public_instance_methods.include? method
|
108
|
+
respond true if SpacedMarkup.public_instance_methods.include? method.to_s
|
109
|
+
respond true if @_scope and @_scope.respond_to? method?
|
110
|
+
super
|
111
|
+
end
|
112
|
+
|
95
113
|
# avoid method_missing overhead for the most common case
|
96
114
|
def tag!(sym, *args, &block)
|
97
115
|
if !block and (args.empty? or args == [''])
|
98
|
-
CssProxy.new(@
|
116
|
+
CssProxy.new(@_builder, @_builder.target!, sym, args)
|
99
117
|
else
|
100
|
-
@
|
118
|
+
@_builder.tag! sym, *args, &block
|
101
119
|
end
|
102
120
|
end
|
103
121
|
|
@@ -122,7 +140,7 @@ module Wunderbar
|
|
122
140
|
stdout = output_class[:stdout] || '_stdout'
|
123
141
|
stderr = output_class[:stderr] || '_stderr'
|
124
142
|
|
125
|
-
@
|
143
|
+
@_builder.tag! tag, command, :class=>stdin unless opts[:echo] == false
|
126
144
|
|
127
145
|
require 'thread'
|
128
146
|
semaphore = Mutex.new
|
@@ -131,14 +149,18 @@ module Wunderbar
|
|
131
149
|
Thread.new do
|
132
150
|
until pout.eof?
|
133
151
|
out_line = pout.readline.chomp
|
134
|
-
semaphore.synchronize
|
152
|
+
semaphore.synchronize do
|
153
|
+
@_builder.tag! tag, out_line, :class=>stdout
|
154
|
+
end
|
135
155
|
end
|
136
156
|
end,
|
137
157
|
|
138
158
|
Thread.new do
|
139
159
|
until perr.eof?
|
140
160
|
err_line = perr.readline.chomp
|
141
|
-
semaphore.synchronize
|
161
|
+
semaphore.synchronize do
|
162
|
+
@_builder.tag! tag, err_line, :class=>stderr
|
163
|
+
end
|
142
164
|
end
|
143
165
|
end,
|
144
166
|
|
@@ -157,17 +179,12 @@ module Wunderbar
|
|
157
179
|
|
158
180
|
# declaration (DOCTYPE, etc)
|
159
181
|
def declare(*args)
|
160
|
-
@
|
182
|
+
@_builder.declare!(*args)
|
161
183
|
end
|
162
184
|
|
163
185
|
# comment
|
164
186
|
def comment(*args)
|
165
|
-
@
|
166
|
-
end
|
167
|
-
|
168
|
-
# was this invoked via HTTP POST?
|
169
|
-
def post?
|
170
|
-
$HTTP_POST
|
187
|
+
@_builder.comment! *args
|
171
188
|
end
|
172
189
|
end
|
173
190
|
|
@@ -191,12 +208,14 @@ module Wunderbar
|
|
191
208
|
self
|
192
209
|
end
|
193
210
|
|
194
|
-
# forward to
|
211
|
+
# forward to Wunderbar, @_target, or @_scope
|
195
212
|
def method_missing(method, *args, &block)
|
196
213
|
if Wunderbar.respond_to? method
|
197
214
|
return Wunderbar.send method, *args, &block
|
198
215
|
elsif @_target.respond_to? method
|
199
216
|
return @_target.send method, *args, &block
|
217
|
+
elsif @_scope and @_scope.respond_to? method
|
218
|
+
return @_scope.send method, *args, &block
|
200
219
|
else
|
201
220
|
super
|
202
221
|
end
|
@@ -208,7 +227,8 @@ module Wunderbar
|
|
208
227
|
end
|
209
228
|
|
210
229
|
class JsonBuilder
|
211
|
-
def initialize
|
230
|
+
def initialize(scope=nil)
|
231
|
+
@_scope = scope
|
212
232
|
@_target = {}
|
213
233
|
end
|
214
234
|
|
@@ -221,7 +241,7 @@ module Wunderbar
|
|
221
241
|
@_target
|
222
242
|
end
|
223
243
|
|
224
|
-
# forward to
|
244
|
+
# forward to Wunderbar, @_target, or @_scope
|
225
245
|
def method_missing(method, *args, &block)
|
226
246
|
|
227
247
|
if method.to_s =~ /^_(\w*)$/
|
@@ -230,6 +250,8 @@ module Wunderbar
|
|
230
250
|
return Wunderbar.send method, *args, &block
|
231
251
|
elsif @_target.respond_to? method
|
232
252
|
return @_target.send method, *args, &block
|
253
|
+
elsif @_scope and @_scope.respond_to? method
|
254
|
+
return @_scope.send method, *args, &block
|
233
255
|
else
|
234
256
|
super
|
235
257
|
end
|
@@ -2,18 +2,21 @@ module Wunderbar
|
|
2
2
|
|
3
3
|
module CGI
|
4
4
|
|
5
|
+
HIDE_FRAME = [ %r{/(wunderbar|webrick)/},
|
6
|
+
%r{/gems/.*/(builder|rack|sinatra)/} ]
|
7
|
+
|
5
8
|
# produce json
|
6
9
|
def self.json(&block)
|
10
|
+
headers = { 'type' => 'application/json', 'Cache-Control' => 'no-cache' }
|
7
11
|
builder = JsonBuilder.new
|
8
|
-
output = builder.encode($
|
9
|
-
|
12
|
+
output = builder.encode($params, &block)
|
13
|
+
headers['status'] = "404 Not Found" if output == {}
|
10
14
|
rescue Exception => exception
|
11
|
-
Kernel.print "Status: 500 Internal Error\r\n"
|
12
15
|
Wunderbar.error exception.inspect
|
16
|
+
headers['status'] = "500 Internal Server Error"
|
13
17
|
backtrace = []
|
14
18
|
exception.backtrace.each do |frame|
|
15
|
-
next if frame =~
|
16
|
-
next if frame =~ %r{/gems/.*/builder/}
|
19
|
+
next if HIDE_FRAME.any? {|re| frame =~ re}
|
17
20
|
Wunderbar.warn " #{frame}"
|
18
21
|
backtrace << frame
|
19
22
|
end
|
@@ -21,31 +24,27 @@ module Wunderbar
|
|
21
24
|
builder._exception exception.inspect
|
22
25
|
builder._backtrace backtrace
|
23
26
|
ensure
|
24
|
-
out?
|
25
|
-
builder.target!
|
26
|
-
end
|
27
|
+
out?(headers) { builder.target! }
|
27
28
|
end
|
28
29
|
|
29
30
|
# produce text
|
30
31
|
def self.text &block
|
32
|
+
headers = {'type' => 'text/plain', 'charset' => 'UTF-8'}
|
31
33
|
builder = TextBuilder.new
|
32
|
-
output = builder.encode($
|
33
|
-
|
34
|
+
output = builder.encode($params, &block)
|
35
|
+
headers['status'] = "404 Not Found" if output == ''
|
34
36
|
rescue Exception => exception
|
35
37
|
Wunderbar.error exception.inspect
|
36
|
-
|
38
|
+
headers['status'] = "500 Internal Server Error"
|
37
39
|
builder.puts unless builder.size == 0
|
38
40
|
builder.puts exception.inspect
|
39
41
|
exception.backtrace.each do |frame|
|
40
|
-
next if frame =~
|
41
|
-
next if frame =~ %r{/gems/.*/builder/}
|
42
|
+
next if HIDE_FRAME.any? {|re| frame =~ re}
|
42
43
|
Wunderbar.warn " #{frame}"
|
43
44
|
builder.puts " #{frame}"
|
44
45
|
end
|
45
46
|
ensure
|
46
|
-
out?
|
47
|
-
builder.target!
|
48
|
-
end
|
47
|
+
out?(headers) { builder.target! }
|
49
48
|
end
|
50
49
|
|
51
50
|
# Conditionally provide output, based on ETAG
|
@@ -54,8 +53,11 @@ module Wunderbar
|
|
54
53
|
require 'digest/md5'
|
55
54
|
etag = Digest::MD5.hexdigest(content)
|
56
55
|
|
57
|
-
if
|
58
|
-
|
56
|
+
if $env.HTTP_IF_NONE_MATCH == etag.inspect
|
57
|
+
headers['Date'] = ::CGI.rfc1123_date(Time.now)
|
58
|
+
$cgi.out headers.merge('status' => '304 Not Modified') do
|
59
|
+
''
|
60
|
+
end
|
59
61
|
else
|
60
62
|
$cgi.out headers.merge('Etag' => etag.inspect) do
|
61
63
|
content
|
@@ -66,33 +68,30 @@ module Wunderbar
|
|
66
68
|
|
67
69
|
# produce html/xhtml
|
68
70
|
def self.html(*args, &block)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
mimetype = ($XHTML ? 'application/xhtml+xml' : 'text/html')
|
71
|
+
headers = { 'type' => 'text/html', 'charset' => 'UTF-8' }
|
72
|
+
headers['type'] = 'application/xhtml+xml' if @xhtml
|
73
|
+
|
74
74
|
x = HtmlMarkup.new
|
75
|
-
x._! "\xEF\xBB\xBF"
|
76
|
-
x._.declare :DOCTYPE, :html
|
77
75
|
|
78
76
|
begin
|
79
|
-
|
77
|
+
if @xhtml
|
78
|
+
output = x.xhtml *args, &block
|
79
|
+
else
|
80
|
+
output = x.html *args, &block
|
81
|
+
end
|
80
82
|
rescue ::Exception => exception
|
81
|
-
|
83
|
+
headers['status'] = "500 Internal Server Error"
|
82
84
|
x.clear!
|
83
|
-
x._! "\xEF\xBB\xBF"
|
84
|
-
x._.declare :DOCTYPE, :html
|
85
85
|
output = x.html(*args) do
|
86
86
|
_head do
|
87
|
-
_title 'Internal Error'
|
87
|
+
_title 'Internal Server Error'
|
88
88
|
end
|
89
89
|
_body do
|
90
|
-
_h1 'Internal Error'
|
90
|
+
_h1 'Internal Server Error'
|
91
91
|
text = exception.inspect
|
92
92
|
Wunderbar.error text
|
93
93
|
exception.backtrace.each do |frame|
|
94
|
-
next if frame =~
|
95
|
-
next if frame =~ %r{/gems/.*/builder/}
|
94
|
+
next if HIDE_FRAME.any? {|re| frame =~ re}
|
96
95
|
Wunderbar.warn " #{frame}"
|
97
96
|
text += "\n #{frame}"
|
98
97
|
end
|
@@ -102,10 +101,73 @@ module Wunderbar
|
|
102
101
|
end
|
103
102
|
end
|
104
103
|
|
105
|
-
out?
|
106
|
-
|
104
|
+
out?(headers) { output }
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.call(env)
|
108
|
+
require 'etc'
|
109
|
+
$USER = ENV['REMOTE_USER'] ||= ENV['USER'] || Etc.getlogin
|
110
|
+
|
111
|
+
accept = $env.HTTP_ACCEPT.to_s
|
112
|
+
request_uri = $env.REQUEST_URI.to_s
|
113
|
+
|
114
|
+
# implied request types
|
115
|
+
xhr_json = Wunderbar::Options::XHR_JSON || (accept =~ /json/)
|
116
|
+
text = Wunderbar::Options::TEXT ||
|
117
|
+
(accept =~ /plain/ and accept !~ /html/)
|
118
|
+
@xhtml = (accept =~ /xhtml/ or accept == '')
|
119
|
+
|
120
|
+
# overrides via the uri query parameter
|
121
|
+
xhr_json ||= (request_uri =~ /\?json$/)
|
122
|
+
text ||= (request_uri =~ /\?text$/)
|
123
|
+
|
124
|
+
# overrides via the command line
|
125
|
+
xhtml_override = ARGV.include?('--xhtml')
|
126
|
+
html_override = ARGV.include?('--html')
|
127
|
+
|
128
|
+
# disable conneg if only one handler is provided
|
129
|
+
if Wunderbar.queue.length == 1
|
130
|
+
type = Wunderbar.queue.first.first
|
131
|
+
xhr_json = (type == :json)
|
132
|
+
text = (type == :text)
|
133
|
+
end
|
134
|
+
|
135
|
+
Wunderbar.queue.each do |type, args, block|
|
136
|
+
case type
|
137
|
+
when :html, :xhtml
|
138
|
+
unless xhr_json or text
|
139
|
+
if type == :html
|
140
|
+
@xhtml = false unless xhtml_override
|
141
|
+
else
|
142
|
+
@xhtml = false if html_override
|
143
|
+
end
|
144
|
+
|
145
|
+
self.html(*args, &block)
|
146
|
+
return
|
147
|
+
end
|
148
|
+
when :json
|
149
|
+
if xhr_json
|
150
|
+
self.json(*args, &block)
|
151
|
+
return
|
152
|
+
end
|
153
|
+
when :text
|
154
|
+
if text
|
155
|
+
self.text(*args, &block)
|
156
|
+
return
|
157
|
+
end
|
158
|
+
end
|
107
159
|
end
|
108
160
|
end
|
161
|
+
|
162
|
+
# map Ruby CGI headers to Rack headers
|
163
|
+
def self.headers(headers)
|
164
|
+
result = headers.dup
|
165
|
+
type = result.delete('type') || 'text/html'
|
166
|
+
charset = result.delete('charset')
|
167
|
+
type = "#{type}; charset=#{charset}" if charset
|
168
|
+
result['Content-Type'] ||= type
|
169
|
+
result
|
170
|
+
end
|
109
171
|
end
|
110
172
|
|
111
173
|
@queue = []
|
@@ -127,40 +189,7 @@ module Wunderbar
|
|
127
189
|
@queue << [:text, args, block]
|
128
190
|
end
|
129
191
|
|
130
|
-
def self.
|
131
|
-
|
132
|
-
xhtml = ARGV.delete('--xhtml')
|
133
|
-
html = ARGV.delete('--html')
|
134
|
-
|
135
|
-
queue.each do |type, args, block|
|
136
|
-
case type
|
137
|
-
when :html
|
138
|
-
unless $XHR_JSON or $TEXT
|
139
|
-
$XHTML = false unless xhtml
|
140
|
-
CGI.html(*args, &block)
|
141
|
-
break
|
142
|
-
end
|
143
|
-
when :xhtml
|
144
|
-
unless $XHR_JSON or $TEXT
|
145
|
-
$XHTML = false if html
|
146
|
-
CGI.html(*args, &block)
|
147
|
-
break
|
148
|
-
end
|
149
|
-
when :json
|
150
|
-
if $XHR_JSON
|
151
|
-
CGI.json(*args, &block)
|
152
|
-
break
|
153
|
-
end
|
154
|
-
when :text
|
155
|
-
if $TEXT
|
156
|
-
CGI.text(*args, &block)
|
157
|
-
break
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
192
|
+
def self.queue
|
193
|
+
@queue
|
161
194
|
end
|
162
195
|
end
|
163
|
-
|
164
|
-
at_exit do
|
165
|
-
Wunderbar.evaluate
|
166
|
-
end
|
@@ -1,46 +1,36 @@
|
|
1
1
|
# explicit request types
|
2
|
-
$HTTP_GET = ARGV.delete('--get')
|
3
|
-
$HTTP_POST = ARGV.delete('--post')
|
4
|
-
$XHR_JSON = ARGV.delete('--json')
|
5
|
-
$TEXT = ARGV.delete('--text')
|
6
|
-
|
7
|
-
# Only prompt if explicitly asked for
|
8
|
-
ARGV.push '' if ARGV.empty?
|
9
|
-
ARGV.delete('--prompt') or ARGV.delete('--offline')
|
10
|
-
|
11
|
-
# standard objects
|
12
|
-
$cgi = CGI.new
|
13
|
-
$param = $cgi.params
|
14
|
-
|
15
|
-
# implied request types
|
16
|
-
$HTTP_GET ||= ($cgi.request_method == 'GET')
|
17
|
-
$HTTP_POST ||= ($cgi.request_method == 'POST')
|
18
|
-
$XHR_JSON ||= ($cgi.accept.to_s =~ /json/)
|
19
|
-
$TEXT ||= ($cgi.accept.to_s =~ /plain/ and $cgi.accept.to_s !~ /html/)
|
20
|
-
$XHTML = ($cgi.accept.to_s =~ /xhtml/ or $cgi.accept == nil)
|
21
|
-
|
22
|
-
# get arguments if CGI couldn't find any...
|
23
|
-
$param.merge!(CGI.parse(ARGV.join('&'))) if $param.empty?
|
24
|
-
|
25
2
|
module Wunderbar
|
3
|
+
module Options
|
4
|
+
XHR_JSON = ARGV.delete('--json')
|
5
|
+
TEXT = ARGV.delete('--text')
|
6
|
+
end
|
7
|
+
|
26
8
|
module Untaint
|
27
9
|
def untaint_if_match regexp
|
28
10
|
self.untaint if regexp.match(self)
|
29
11
|
end
|
30
12
|
end
|
31
|
-
end
|
32
13
|
|
33
|
-
#
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
14
|
+
# quick access to request_uri
|
15
|
+
def self.SELF
|
16
|
+
$env.REQUEST_URI
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.SELF?
|
20
|
+
if SELF '?'
|
21
|
+
self.self
|
38
22
|
else
|
39
|
-
|
23
|
+
SELF + "?" # avoids spoiling the cache
|
40
24
|
end
|
41
25
|
end
|
26
|
+
|
27
|
+
# was this invoked via HTTP POST?
|
28
|
+
def self.post?
|
29
|
+
$env.REQUEST_METHOD.to_s.upcase == 'POST'
|
30
|
+
end
|
42
31
|
end
|
43
32
|
|
33
|
+
# environment objects
|
44
34
|
$env = {}
|
45
35
|
def $env.method_missing(name)
|
46
36
|
delete name.to_s if ENV[name.to_s] != self[name.to_s]
|
@@ -50,32 +40,9 @@ def $env.method_missing(name)
|
|
50
40
|
self[name.to_s]
|
51
41
|
end
|
52
42
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
if SELF.include? '?'
|
57
|
-
SELF
|
58
|
-
else
|
59
|
-
SELF + "?" # avoids spoiling the cache
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# environment objects
|
64
|
-
$USER = ENV['REMOTE_USER'] || ENV['USER'] ||
|
65
|
-
if RUBY_PLATFORM =~ /darwin/
|
66
|
-
`dscl . -search /Users UniqueID #{Process.uid}`.split.first
|
67
|
-
else
|
68
|
-
`getent passwd #{Process.uid}`.split(':').first
|
69
|
-
end
|
70
|
-
|
71
|
-
ENV['REMOTE_USER'] ||= $USER
|
72
|
-
|
73
|
-
$HOME = ENV['HOME'] ||= File.expand_path('~' + $USER)
|
74
|
-
$SERVER = ENV['HTTP_HOST'] || `hostname`.chomp
|
75
|
-
|
76
|
-
# more implied request types
|
77
|
-
$XHR_JSON ||= ($env.REQUEST_URI.to_s =~ /\?json$/)
|
78
|
-
$TEXT ||= ($env.REQUEST_URI.to_s =~ /\?text$/)
|
43
|
+
require 'socket'
|
44
|
+
$SERVER = ENV['HTTP_HOST'] || Socket::gethostname
|
45
|
+
$HOME = ENV['HOME'] ||= Dir.home() rescue nil
|
79
46
|
|
80
47
|
# set encoding to UTF-8
|
81
48
|
ENV['LANG'] ||= "en_US.UTF-8"
|
@@ -5,23 +5,54 @@ class HtmlMarkup
|
|
5
5
|
link meta param source track wbr
|
6
6
|
)
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@
|
8
|
+
def initialize(scope = nil)
|
9
|
+
@_scope = scope
|
10
|
+
@x = Wunderbar::XmlMarkup.new :scope => scope, :indent => 2, :target => []
|
11
|
+
@xthml = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def xhtml(*args, &block)
|
15
|
+
@xhtml = true
|
16
|
+
html(*args, &block)
|
10
17
|
end
|
11
18
|
|
12
19
|
def html(*args, &block)
|
20
|
+
# default namespace
|
21
|
+
args << {} if args.empty?
|
22
|
+
args.first[:xmlns] ||= 'http://www.w3.org/1999/xhtml' if Hash === args.first
|
23
|
+
|
24
|
+
@x.text! "\xEF\xBB\xBF"
|
25
|
+
@x.declare! :DOCTYPE, :html
|
13
26
|
@x.tag! :html, *args do
|
14
|
-
$
|
15
|
-
|
27
|
+
if $params
|
28
|
+
$params.each do |key,value|
|
29
|
+
value = value.first if Array === value
|
30
|
+
instance_variable_set "@#{key}", value if key =~ /^\w+$/
|
31
|
+
end
|
16
32
|
end
|
17
33
|
instance_exec(@x, &block)
|
18
34
|
end
|
19
35
|
@x.target!.join
|
20
36
|
end
|
21
37
|
|
38
|
+
def _html(*args, &block)
|
39
|
+
html(*args, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def _xhtml(*args, &block)
|
43
|
+
@xhtml = true
|
44
|
+
html(*args, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def xhtml?
|
48
|
+
@xhtml
|
49
|
+
end
|
50
|
+
|
22
51
|
def method_missing(name, *args, &block)
|
23
52
|
if name.to_s =~ /^_(\w+)(!|\?|)$/
|
24
53
|
name, flag = $1, $2
|
54
|
+
elsif @_scope and @_scope.respond_to? name
|
55
|
+
return @_scope.__send__ name, *args, &block
|
25
56
|
else
|
26
57
|
error = NameError.new "undefined local variable or method `#{name}'", name
|
27
58
|
error.set_backtrace caller
|
@@ -36,7 +67,7 @@ class HtmlMarkup
|
|
36
67
|
if %w(script style).include?(name)
|
37
68
|
if String === args.first and not block
|
38
69
|
text = args.shift
|
39
|
-
if
|
70
|
+
if @xhtml
|
40
71
|
block = Proc.new {@x.indented_text! text}
|
41
72
|
else
|
42
73
|
block = Proc.new {@x.indented_data! text}
|
@@ -82,8 +113,7 @@ class HtmlMarkup
|
|
82
113
|
text = exception.inspect
|
83
114
|
Wunderbar.warn text
|
84
115
|
exception.backtrace.each do |frame|
|
85
|
-
next if frame =~
|
86
|
-
next if frame =~ %r{/gems/.*/builder/}
|
116
|
+
next if Wunderbar::CGI::HIDE_FRAME.any? {|re| frame =~ re}
|
87
117
|
Wunderbar.warn " #{frame}"
|
88
118
|
text += "\n #{frame}"
|
89
119
|
end
|
@@ -1,3 +1,18 @@
|
|
1
|
+
module Wunderbar
|
2
|
+
# Extract data from the script (after __END__)
|
3
|
+
def self.data
|
4
|
+
data = DATA.read
|
5
|
+
|
6
|
+
# process argument overrides
|
7
|
+
data.scan(/^\s*([A-Z]\w*)\s*=\s*(['"]).*\2$/).each do |name, q|
|
8
|
+
override = ARGV.find {|arg| arg =~ /--#{name}=(.*)/i}
|
9
|
+
data[/^\s*#{name}\s*=\s*(.*)/,1] = $1.inspect if override
|
10
|
+
end
|
11
|
+
|
12
|
+
data
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
1
16
|
# option to create an suexec callable wrapper
|
2
17
|
install = ARGV.find {|arg| arg =~ /--install=(.*)/}
|
3
18
|
if install and ARGV.delete(install)
|
@@ -40,17 +55,7 @@ if install and ARGV.delete(install)
|
|
40
55
|
file.puts "Dir.chdir #{File.dirname(main).inspect}"
|
41
56
|
|
42
57
|
# Optional data from the script (after __END__)
|
43
|
-
if Object.const_defined? :DATA
|
44
|
-
data = DATA.read
|
45
|
-
|
46
|
-
# process argument overrides
|
47
|
-
data.scan(/^\s*([A-Z]\w*)\s*=\s*(['"]).*\2$/).each do |name, q|
|
48
|
-
override = ARGV.find {|arg| arg =~ /--#{name}=(.*)/}
|
49
|
-
data[/^\s*#{name}\s*=\s*(.*)/,1] = $1.inspect if override
|
50
|
-
end
|
51
|
-
|
52
|
-
file.puts "\n#{data}\n"
|
53
|
-
end
|
58
|
+
file.puts "\n#{Wunderbar.data}\n" if Object.const_defined? :DATA
|
54
59
|
|
55
60
|
# Load script
|
56
61
|
require = "require #{"./#{File.basename(main).sub(/\.rb$/,'')}".inspect}"
|
@@ -0,0 +1,112 @@
|
|
1
|
+
at_exit do
|
2
|
+
# Only prompt if explicitly asked for
|
3
|
+
ARGV.push '' if ARGV.empty?
|
4
|
+
ARGV.delete('--prompt') or ARGV.delete('--offline')
|
5
|
+
|
6
|
+
$cgi = CGI.new
|
7
|
+
|
8
|
+
port = ARGV.find {|arg| arg =~ /--port=(.*)/}
|
9
|
+
if port and ARGV.delete(port)
|
10
|
+
port = $1.to_i
|
11
|
+
|
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
|
+
# Evaluate optional data from the script (after __END__)
|
35
|
+
eval Wunderbar.data if Object.const_defined? :DATA
|
36
|
+
|
37
|
+
# start the server
|
38
|
+
require 'rack'
|
39
|
+
require 'rack/showexceptions'
|
40
|
+
app = Rack::ShowExceptions.new(Rack::Lint.new($cgi))
|
41
|
+
Rack::Server.start :app => app, :Port => port
|
42
|
+
|
43
|
+
elsif defined? Sinatra
|
44
|
+
|
45
|
+
require 'wunderbar/template'
|
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
|
62
|
+
|
63
|
+
def _xhtml(*args, &block)
|
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
|
87
|
+
|
88
|
+
# allow the REQUEST_METHOD to be set for command line invocations
|
89
|
+
ENV['REQUEST_METHOD'] ||= 'POST' if ARGV.delete('--post')
|
90
|
+
ENV['REQUEST_METHOD'] ||= 'GET' if ARGV.delete('--get')
|
91
|
+
|
92
|
+
# standard objects
|
93
|
+
$params = $cgi.params
|
94
|
+
|
95
|
+
# get arguments if CGI couldn't find any...
|
96
|
+
$params.merge!(CGI.parse(ARGV.join('&'))) if $params.empty?
|
97
|
+
|
98
|
+
# fast path for accessing CGI parameters
|
99
|
+
def $params.method_missing(name)
|
100
|
+
if has_key? name.to_s
|
101
|
+
if self[name.to_s].length == 1
|
102
|
+
self[name.to_s].first.extend(Wunderbar::Untaint)
|
103
|
+
else
|
104
|
+
self[name.to_s].join
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# CGI or command line
|
110
|
+
Wunderbar::CGI.call(ENV)
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,91 @@
|
|
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
|
+
|
data/lib/wunderbar/version.rb
CHANGED
data/lib/wunderbar.rb
CHANGED
@@ -3,12 +3,13 @@ require 'builder'
|
|
3
3
|
require 'json'
|
4
4
|
|
5
5
|
require 'wunderbar/environment'
|
6
|
+
require 'wunderbar/builder'
|
6
7
|
require 'wunderbar/cgi-methods'
|
7
8
|
require 'wunderbar/cssproxy'
|
8
9
|
require 'wunderbar/html-methods'
|
9
|
-
require 'wunderbar/job-control'
|
10
10
|
require 'wunderbar/installation'
|
11
|
-
require 'wunderbar/
|
11
|
+
require 'wunderbar/job-control'
|
12
12
|
require 'wunderbar/logger'
|
13
|
+
require 'wunderbar/server'
|
13
14
|
|
14
15
|
W_ = Wunderbar
|
data/wunderbar.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "wunderbar"
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.10.0"
|
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-07"
|
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/logger.rb", "lib/wunderbar/builder.rb", "lib/wunderbar/environment.rb", "lib/wunderbar/cgi-methods.rb", "lib/wunderbar/cssproxy.rb", "lib/wunderbar/version.rb"]
|
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/template.rb", "lib/wunderbar/server.rb", "lib/wunderbar/logger.rb", "lib/wunderbar/builder.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
|
-
s.rubygems_version = "1.8.
|
15
|
+
s.rubygems_version = "1.8.21"
|
16
16
|
s.summary = "HTML Generator and CGI application support"
|
17
17
|
|
18
18
|
if s.respond_to? :specification_version then
|
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: 55
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 10
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.10.0
|
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-07 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: builder
|
@@ -62,6 +62,8 @@ 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
|
+
- lib/wunderbar/server.rb
|
65
67
|
- lib/wunderbar/logger.rb
|
66
68
|
- lib/wunderbar/builder.rb
|
67
69
|
- lib/wunderbar/environment.rb
|
@@ -97,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
99
|
requirements: []
|
98
100
|
|
99
101
|
rubyforge_project:
|
100
|
-
rubygems_version: 1.8.
|
102
|
+
rubygems_version: 1.8.21
|
101
103
|
signing_key:
|
102
104
|
specification_version: 3
|
103
105
|
summary: HTML Generator and CGI application support
|