wunderbar 0.10.9 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +43 -20
- data/lib/wunderbar/builder.rb +6 -2
- data/lib/wunderbar/cgi-methods.rb +2 -1
- data/lib/wunderbar/environment.rb +8 -4
- data/lib/wunderbar/html-methods.rb +27 -0
- data/lib/wunderbar/rack.rb +5 -1
- data/lib/wunderbar/rails.rb +46 -0
- data/lib/wunderbar/server.rb +9 -1
- data/lib/wunderbar/sinatra.rb +21 -6
- data/lib/wunderbar/version.rb +2 -2
- data/wunderbar.gemspec +15 -11
- metadata +13 -12
data/README.md
CHANGED
@@ -136,24 +136,28 @@ Basic interface
|
|
136
136
|
A typical main program produces one or more of HTML, JSON, or plain text
|
137
137
|
output. This is accomplished by providing one or more of the following:
|
138
138
|
|
139
|
-
|
139
|
+
_html do
|
140
140
|
code
|
141
141
|
end
|
142
142
|
|
143
|
-
|
143
|
+
_xhtml do
|
144
144
|
code
|
145
145
|
end
|
146
146
|
|
147
|
-
|
147
|
+
_json do
|
148
148
|
code
|
149
149
|
end
|
150
150
|
|
151
|
-
|
151
|
+
_text do
|
152
152
|
code
|
153
153
|
end
|
154
154
|
|
155
|
-
Arbitrary Ruby code can be placed in each.
|
156
|
-
|
155
|
+
Arbitrary Ruby code can be placed in each. Form parameters are made available
|
156
|
+
as instance variables (e.g., `@name`). Host environment (CGI, Rack, Sinatra)
|
157
|
+
values are accessible as methods of the `_` object: for example `_.headers`
|
158
|
+
(CGI), `_.set_cookie` (Rack), `_.redirect` (Sinatra).
|
159
|
+
|
160
|
+
To append to the output produced, use the `_` methods described below.
|
157
161
|
|
158
162
|
Methods provided to Wunderbar.html
|
159
163
|
---
|
@@ -182,16 +186,33 @@ number of other convenience methods are defined:
|
|
182
186
|
* `_.post?` -- was this invoked via HTTP POST?
|
183
187
|
* `_.system` -- invokes a shell command, captures stdin, stdout, and stderr
|
184
188
|
* `_.submit` -- runs command (or block) as a deamon process
|
189
|
+
* `_.xhtml?` -- output as XHTML?
|
185
190
|
|
186
191
|
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
192
|
|
188
|
-
* `_.tag! :foo
|
189
|
-
* `_.error 'Log message'
|
193
|
+
* `_.tag! :foo`: insert elements where the name can be dynamic
|
194
|
+
* `_.error 'Log message'`: write a message to the server log
|
195
|
+
* `_import!`: insert markup with indentation matching the current output
|
190
196
|
|
191
197
|
XHTML differs from HTML in the escaping of inline style and script elements.
|
192
198
|
XHTML will also fall back to HTML output unless the user agent indicates it
|
193
199
|
supports XHTML via the HTTP Accept header.
|
194
200
|
|
201
|
+
In addition to the default processing of elements, text, and attributes,
|
202
|
+
Wunderdar defines additional processing for the following:
|
203
|
+
|
204
|
+
* `_head`: insert meta charset utf-8
|
205
|
+
* `_svg`: insert svg namespace
|
206
|
+
* `_math`: insert math namespace
|
207
|
+
* `_coffeescript`: convert [coffeescript](http://coffeescript.org/) to JS and insert script tag
|
208
|
+
|
209
|
+
Note that adding an exclamation mark to the end of the tag name disables this
|
210
|
+
behavior.
|
211
|
+
|
212
|
+
If one of the attributes passed on the `_html` declaration is `:_width`, an
|
213
|
+
attempt will be made to reflow text in order to not exceed this line width.
|
214
|
+
This won't be done if it will affect what actually is displayed.
|
215
|
+
|
195
216
|
Methods provided to Wunderbar.json
|
196
217
|
---
|
197
218
|
|
@@ -258,6 +279,20 @@ output stream, which provides access to other useful methods, for example:
|
|
258
279
|
_.print 'foo'
|
259
280
|
_.printf "Hello %s!\n", 'world'
|
260
281
|
|
282
|
+
Secure by default
|
283
|
+
---
|
284
|
+
|
285
|
+
Wunderbar will properly escape all HTML and JSON output, eliminating problems
|
286
|
+
of HTML or JavaScript injection.
|
287
|
+
|
288
|
+
Unless you call `Wunderbar.unsafe!` at the top of your script, Wunderbar will
|
289
|
+
also set
|
290
|
+
[`$SAFE=1`](http://www.ruby-doc.org/docs/ProgrammingRuby/html/taint.html)
|
291
|
+
before processing requests. This means that you will need to
|
292
|
+
[`untaint`](ruby-doc.org/core/Object.html#method-i-untaint) all inputs
|
293
|
+
received from external sources before you make system calls or access the file
|
294
|
+
system.
|
295
|
+
|
261
296
|
Globals provided
|
262
297
|
---
|
263
298
|
* `$USER` - Host user id
|
@@ -276,18 +311,6 @@ Also, the following environment variables are set if they aren't already:
|
|
276
311
|
Finally, the (Ruby 1.9.x) default external and internal encodings are set to
|
277
312
|
UTF-8. For Ruby 1.8, `$KCODE` is set to `U`
|
278
313
|
|
279
|
-
HTML methods
|
280
|
-
---
|
281
|
-
* `_head`: insert meta charset utf-8
|
282
|
-
* `_svg`: insert svg namespace
|
283
|
-
* `_math`: insert math namespace
|
284
|
-
* `_coffeescript`: convert [coffeescript](http://coffeescript.org/) to JS and insert script tag
|
285
|
-
* `_import!`: insert markup with indentation matching the current output
|
286
|
-
* `xhtml?`: output as XHTML?
|
287
|
-
|
288
|
-
Note that adding an exclamation mark to the end of the tag name disables this
|
289
|
-
behavior.
|
290
|
-
|
291
314
|
Builder extensions
|
292
315
|
---
|
293
316
|
* `indented_text!`: matches text indentation to markup
|
data/lib/wunderbar/builder.rb
CHANGED
@@ -218,7 +218,7 @@ module Wunderbar
|
|
218
218
|
|
219
219
|
@_builder << data
|
220
220
|
rescue LoadError
|
221
|
-
@_builder <<
|
221
|
+
@_builder << data
|
222
222
|
end
|
223
223
|
end
|
224
224
|
|
@@ -230,11 +230,15 @@ module Wunderbar
|
|
230
230
|
instance_variable_set "@#{key}", value if key =~ /^\w+$/
|
231
231
|
end
|
232
232
|
end
|
233
|
+
|
234
|
+
def get_binding
|
235
|
+
binding
|
236
|
+
end
|
233
237
|
end
|
234
238
|
|
239
|
+
require 'stringio'
|
235
240
|
class TextBuilder < BuilderBase
|
236
241
|
def initialize(scope)
|
237
|
-
require 'stringio'
|
238
242
|
@_target = StringIO.new
|
239
243
|
@_scope = scope
|
240
244
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
1
3
|
module Wunderbar
|
2
4
|
module CGI
|
3
5
|
|
@@ -31,7 +33,6 @@ module Wunderbar
|
|
31
33
|
# Conditionally provide output, based on ETAG
|
32
34
|
def self.out?(scope, headers, &block)
|
33
35
|
content = block.call
|
34
|
-
require 'digest/md5'
|
35
36
|
etag = Digest::MD5.hexdigest(content)
|
36
37
|
|
37
38
|
if scope.env['HTTP_IF_NONE_MATCH'] == etag.inspect
|
@@ -5,10 +5,14 @@ module Wunderbar
|
|
5
5
|
TEXT = ARGV.delete('--text')
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
@@unsafe = false
|
9
|
+
|
10
|
+
def self.unsafe!(mode=true)
|
11
|
+
@@unsafe=mode
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.safe?
|
15
|
+
not @@unsafe
|
12
16
|
end
|
13
17
|
|
14
18
|
class Scope
|
@@ -27,6 +27,7 @@ class HtmlMarkup < Wunderbar::BuilderBase
|
|
27
27
|
# default namespace
|
28
28
|
args << {} if args.empty?
|
29
29
|
args.first[:xmlns] ||= 'http://www.w3.org/1999/xhtml' if Hash === args.first
|
30
|
+
@_width = args.first.delete(:_width) if Hash === args.first
|
30
31
|
|
31
32
|
@x.text! "\xEF\xBB\xBF"
|
32
33
|
@x.declare! :DOCTYPE, :html
|
@@ -34,6 +35,32 @@ class HtmlMarkup < Wunderbar::BuilderBase
|
|
34
35
|
set_variables_from_params
|
35
36
|
instance_eval(&block)
|
36
37
|
end
|
38
|
+
|
39
|
+
if @_width
|
40
|
+
# reflow long lines
|
41
|
+
source = @x.target!.slice!(0..-1)
|
42
|
+
indent = col = 0
|
43
|
+
breakable = true
|
44
|
+
while not source.empty?
|
45
|
+
token = source.shift
|
46
|
+
indent = token[/^ */].length if col == 0
|
47
|
+
breakable = false if token.start_with? '<'
|
48
|
+
while token.length + col > @_width and breakable
|
49
|
+
break if token[0...-1].include? "\n"
|
50
|
+
split = token.rindex(' ', [@_width-col,0].max) || token.index(' ')
|
51
|
+
break unless split
|
52
|
+
break if col+split < indent+@_width/2
|
53
|
+
@x.target! << token[0...split] << "\n" << (' '*indent)
|
54
|
+
col = indent
|
55
|
+
token = token[split+1..-1]
|
56
|
+
end
|
57
|
+
breakable = true if token.end_with? '>'
|
58
|
+
@x.target! << token
|
59
|
+
col += token.length
|
60
|
+
col = 0 if token.end_with? "\n"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
37
64
|
@x.target!.join
|
38
65
|
end
|
39
66
|
|
data/lib/wunderbar/rack.rb
CHANGED
@@ -6,7 +6,11 @@ module Wunderbar
|
|
6
6
|
@_request = Rack::Request.new(env)
|
7
7
|
@_response = Rack::Response.new
|
8
8
|
Wunderbar.logger = @_request.logger
|
9
|
-
Wunderbar
|
9
|
+
if Wunderbar.safe? and $SAFE==0
|
10
|
+
Proc.new { $SAFE=1; Wunderbar::CGI.call(self) }.call
|
11
|
+
else
|
12
|
+
Wunderbar::CGI.call(self)
|
13
|
+
end
|
10
14
|
@_response.finish
|
11
15
|
end
|
12
16
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
3
|
+
module Wunderbar
|
4
|
+
module Rails
|
5
|
+
class HtmlHandler
|
6
|
+
cattr_accessor :default_format
|
7
|
+
self.default_format = Mime::HTML
|
8
|
+
|
9
|
+
def self.call(template)
|
10
|
+
pre = %{
|
11
|
+
x = HtmlMarkup.new(self);
|
12
|
+
instance_variables.each do |var|
|
13
|
+
x.instance_variable_set var, instance_variable_get(var)
|
14
|
+
end
|
15
|
+
}.strip.gsub(/\s+/, ' ')
|
16
|
+
|
17
|
+
post ="x._.target!.join"
|
18
|
+
|
19
|
+
# take care to preserve line numbers in original source
|
20
|
+
"#{pre}; x.instance_eval { #{template.source} }; #{post}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class JsonHandler
|
25
|
+
cattr_accessor :default_format
|
26
|
+
self.default_format = Mime::JSON
|
27
|
+
|
28
|
+
def self.call(template)
|
29
|
+
pre = %{
|
30
|
+
x = Wunderbar::JsonBuilder.new(self);
|
31
|
+
instance_variables.each do |var|
|
32
|
+
x.instance_variable_set var, instance_variable_get(var)
|
33
|
+
end
|
34
|
+
}.strip.gsub(/\s+/, ' ')
|
35
|
+
|
36
|
+
post ="x.target!"
|
37
|
+
|
38
|
+
# take care to preserve line numbers in original source
|
39
|
+
"#{pre}; x.instance_eval { #{template.source} }; #{post}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
ActionView::Template.register_template_handler :_html, HtmlHandler
|
44
|
+
ActionView::Template.register_template_handler :_json, JsonHandler
|
45
|
+
end
|
46
|
+
end
|
data/lib/wunderbar/server.rb
CHANGED
@@ -35,6 +35,10 @@ elsif defined? Sinatra
|
|
35
35
|
|
36
36
|
require 'wunderbar/sinatra'
|
37
37
|
|
38
|
+
elsif defined? ActionView::Template
|
39
|
+
|
40
|
+
require 'wunderbar/rails'
|
41
|
+
|
38
42
|
else
|
39
43
|
|
40
44
|
require 'etc'
|
@@ -79,7 +83,11 @@ else
|
|
79
83
|
ENV['REQUEST_METHOD'] ||= 'GET' if ARGV.delete('--get')
|
80
84
|
|
81
85
|
# CGI or command line
|
82
|
-
Wunderbar
|
86
|
+
if Wunderbar.safe? and $SAFE==0
|
87
|
+
Proc.new { $SAFE=1; Wunderbar::CGI.call(cgi) }.call
|
88
|
+
else
|
89
|
+
Wunderbar::CGI.call(cgi)
|
90
|
+
end
|
83
91
|
end
|
84
92
|
end
|
85
93
|
end
|
data/lib/wunderbar/sinatra.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'sinatra'
|
2
2
|
require 'digest/md5'
|
3
|
+
require 'nokogiri'
|
3
4
|
|
4
5
|
module Wunderbar
|
5
6
|
# Tilt template implementation
|
@@ -48,10 +49,24 @@ module Wunderbar
|
|
48
49
|
instance_variable_set "@#{key}", value if key =~ /^[a-z]\w+$/
|
49
50
|
end
|
50
51
|
end
|
51
|
-
|
52
|
+
|
53
|
+
if not block
|
54
|
+
builder.instance_eval(data.untaint, eval_file)
|
55
|
+
elsif not data
|
52
56
|
builder.instance_eval(&block)
|
53
|
-
|
54
|
-
builder.
|
57
|
+
else
|
58
|
+
context = builder.get_binding do
|
59
|
+
builder.instance_eval {_import! block.call}
|
60
|
+
end
|
61
|
+
context.eval(data.untaint, eval_file)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def _evaluate_safely(*args, &block)
|
66
|
+
if Wunderbar.safe? and $SAFE==0
|
67
|
+
Proc.new { $SAFE=1; _evaluate(*args, &block) }.call
|
68
|
+
else
|
69
|
+
_evaluate(*args, &block)
|
55
70
|
end
|
56
71
|
end
|
57
72
|
end
|
@@ -62,7 +77,7 @@ module Wunderbar
|
|
62
77
|
def evaluate(scope, locals, &block)
|
63
78
|
builder = HtmlMarkup.new(scope)
|
64
79
|
begin
|
65
|
-
|
80
|
+
_evaluate_safely(builder, scope, locals, &block)
|
66
81
|
rescue Exception => exception
|
67
82
|
scope.response.status = 500
|
68
83
|
builder.clear!
|
@@ -90,7 +105,7 @@ module Wunderbar
|
|
90
105
|
def evaluate(scope, locals, &block)
|
91
106
|
builder = JsonBuilder.new(scope)
|
92
107
|
begin
|
93
|
-
|
108
|
+
_evaluate_safely(builder, scope, locals, &block)
|
94
109
|
rescue Exception => exception
|
95
110
|
scope.content_type self.class.default_mime_type, :charset => 'utf-8'
|
96
111
|
scope.response.status = 500
|
@@ -106,7 +121,7 @@ module Wunderbar
|
|
106
121
|
def evaluate(scope, locals, &block)
|
107
122
|
builder = TextBuilder.new(scope)
|
108
123
|
begin
|
109
|
-
|
124
|
+
_evaluate_safely(builder, scope, locals, &block)
|
110
125
|
scope.response.status = 404 if builder.target!.empty?
|
111
126
|
rescue Exception => exception
|
112
127
|
scope.headers['Content-Type'] = self.class.default_mime_type
|
data/lib/wunderbar/version.rb
CHANGED
data/wunderbar.gemspec
CHANGED
@@ -1,19 +1,23 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
|
-
s.name =
|
5
|
-
s.version = "0.
|
4
|
+
s.name = %q{wunderbar}
|
5
|
+
s.version = "0.11.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
-
s.authors = [
|
9
|
-
s.date =
|
10
|
-
s.description =
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
s.
|
16
|
-
s.
|
8
|
+
s.authors = [%q{Sam Ruby}]
|
9
|
+
s.date = %q{2012-04-23}
|
10
|
+
s.description = %q{ Wunderbar makes it easy to produce valid HTML5, wellformed XHTML, Unicode
|
11
|
+
(utf-8), consistently indented, readable applications. This includes
|
12
|
+
output that conforms to the Polyglot specification and the emerging
|
13
|
+
results from the XML Error Recovery Community Group.
|
14
|
+
}
|
15
|
+
s.email = %q{rubys@intertwingly.net}
|
16
|
+
s.files = [%q{wunderbar.gemspec}, %q{README.md}, %q{COPYING}, %q{lib/wunderbar.rb}, %q{lib/wunderbar}, %q{lib/wunderbar/version.rb}, %q{lib/wunderbar/installation.rb}, %q{lib/wunderbar/cssproxy.rb}, %q{lib/wunderbar/rack.rb}, %q{lib/wunderbar/logger.rb}, %q{lib/wunderbar/builder.rb}, %q{lib/wunderbar/sinatra.rb}, %q{lib/wunderbar/rails.rb}, %q{lib/wunderbar/server.rb}, %q{lib/wunderbar/html-methods.rb}, %q{lib/wunderbar/job-control.rb}, %q{lib/wunderbar/environment.rb}, %q{lib/wunderbar/cgi-methods.rb}]
|
17
|
+
s.homepage = %q{http://github.com/rubys/wunderbar}
|
18
|
+
s.require_paths = [%q{lib}]
|
19
|
+
s.rubygems_version = %q{1.8.5}
|
20
|
+
s.summary = %q{HTML Generator and CGI application support}
|
17
21
|
|
18
22
|
if s.respond_to? :specification_version then
|
19
23
|
s.specification_version = 3
|
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: 51
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 11
|
9
|
+
- 0
|
10
|
+
version: 0.11.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-23 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: builder
|
@@ -59,18 +59,19 @@ files:
|
|
59
59
|
- README.md
|
60
60
|
- COPYING
|
61
61
|
- lib/wunderbar.rb
|
62
|
+
- lib/wunderbar/version.rb
|
62
63
|
- lib/wunderbar/installation.rb
|
63
|
-
- lib/wunderbar/
|
64
|
-
- lib/wunderbar/job-control.rb
|
65
|
-
- lib/wunderbar/server.rb
|
66
|
-
- lib/wunderbar/logger.rb
|
64
|
+
- lib/wunderbar/cssproxy.rb
|
67
65
|
- lib/wunderbar/rack.rb
|
66
|
+
- lib/wunderbar/logger.rb
|
68
67
|
- lib/wunderbar/builder.rb
|
69
68
|
- lib/wunderbar/sinatra.rb
|
69
|
+
- lib/wunderbar/rails.rb
|
70
|
+
- lib/wunderbar/server.rb
|
71
|
+
- lib/wunderbar/html-methods.rb
|
72
|
+
- lib/wunderbar/job-control.rb
|
70
73
|
- lib/wunderbar/environment.rb
|
71
74
|
- lib/wunderbar/cgi-methods.rb
|
72
|
-
- lib/wunderbar/cssproxy.rb
|
73
|
-
- lib/wunderbar/version.rb
|
74
75
|
homepage: http://github.com/rubys/wunderbar
|
75
76
|
licenses: []
|
76
77
|
|
@@ -100,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
101
|
requirements: []
|
101
102
|
|
102
103
|
rubyforge_project:
|
103
|
-
rubygems_version: 1.8.
|
104
|
+
rubygems_version: 1.8.5
|
104
105
|
signing_key:
|
105
106
|
specification_version: 3
|
106
107
|
summary: HTML Generator and CGI application support
|