wunderbar 0.9.0 → 0.10.0
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 +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
|