wunderbar 0.8.14 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +108 -4
- data/lib/wunderbar/builder.rb +126 -0
- data/lib/wunderbar/cgi-methods.rb +28 -25
- data/lib/wunderbar/logger.rb +5 -1
- data/lib/wunderbar/version.rb +2 -2
- data/wunderbar.gemspec +2 -2
- metadata +5 -5
data/README.md
CHANGED
@@ -12,9 +12,42 @@ Wunderbar is both inspired by, and builds upon Jim Weirich's
|
|
12
12
|
[Builder](https://github.com/jimweirich/builder#readme), and provides
|
13
13
|
the element id and class id syntax and based on the implementation from
|
14
14
|
[Markaby](http://markaby.rubyforge.org/).
|
15
|
-
.
|
16
15
|
|
17
|
-
|
16
|
+
Wunderbar's JSON support is inspired by David Heinemeier Hansson's
|
17
|
+
[jbuilder](https://github.com/rails/jbuilder).
|
18
|
+
|
19
|
+
Overview
|
20
|
+
---
|
21
|
+
|
22
|
+
The premise of Wunderbar is that output of various types are typically formed
|
23
|
+
by appending either a series of key/value pairs or simple values, and those
|
24
|
+
operations should be optimized for. Appending a key/value pair is done via:
|
25
|
+
|
26
|
+
_key value
|
27
|
+
|
28
|
+
... and appending a simple value is done thus:
|
29
|
+
|
30
|
+
_ value
|
31
|
+
|
32
|
+
For HTML, key/value is used for element nodes, and simple values are used for
|
33
|
+
text nodes. For JSON, key/value is used for Hashes and simple values are used
|
34
|
+
for arrays. For text, simple values are output via puts, and key/value pairs
|
35
|
+
are not used.
|
36
|
+
|
37
|
+
Nesting is performed using blocks.
|
38
|
+
|
39
|
+
The underscore method, when passed no arguments, returns an object that can be
|
40
|
+
used to perform a number of special functions. Some of those functions are
|
41
|
+
unique to the output method. Others like logging methods are common.
|
42
|
+
|
43
|
+
The underscore method when passed multiple arguments or a combination of
|
44
|
+
arguments and a block may do other common functions, depending on the types of
|
45
|
+
the arguments passed.
|
46
|
+
|
47
|
+
Question mark, exclamation mark, and underscore suffixes to the method name
|
48
|
+
may modify the results.
|
49
|
+
|
50
|
+
Quick Start (HTML)
|
18
51
|
---
|
19
52
|
|
20
53
|
Simple element:
|
@@ -112,14 +145,15 @@ output. This is accomplished by providing one or more of the following:
|
|
112
145
|
end
|
113
146
|
|
114
147
|
Wunderbar.json do
|
115
|
-
|
148
|
+
code
|
116
149
|
end
|
117
150
|
|
118
151
|
Wunderbar.text do
|
119
152
|
code
|
120
153
|
end
|
121
154
|
|
122
|
-
Arbitrary Ruby code can be placed in each.
|
155
|
+
Arbitrary Ruby code can be placed in each. To append to the output produced,
|
156
|
+
use the `_` methods described here.
|
123
157
|
|
124
158
|
Methods provided to Wunderbar.html
|
125
159
|
---
|
@@ -154,6 +188,76 @@ Access to all of the builder _defined_ methods (typically these end in an esclam
|
|
154
188
|
* `_.tag! :foo`
|
155
189
|
* `_.error 'Log message'`
|
156
190
|
|
191
|
+
XHTML differs from HTML in the escaping of inline style and script elements.
|
192
|
+
XHTML will also fall back to HTML output unless the user agent indicates it
|
193
|
+
supports XHTML via the HTTP Accept header.
|
194
|
+
|
195
|
+
Methods provided to Wunderbar.json
|
196
|
+
---
|
197
|
+
|
198
|
+
Common operations are to return a Hash or an Array of values. Hashes are
|
199
|
+
a series of name/value pairs, and Arrays are a series of values.
|
200
|
+
|
201
|
+
``` ruby
|
202
|
+
Wunderbar.json do
|
203
|
+
_content format_content(@message.content)
|
204
|
+
_ @message, :created_at, :updated_at
|
205
|
+
|
206
|
+
_author do
|
207
|
+
_name @message.creator.name.familiar
|
208
|
+
_email_address @message.creator.email_address_with_name
|
209
|
+
_url url_for(@message.creator, format: :json)
|
210
|
+
end
|
211
|
+
|
212
|
+
if current_user.admin?
|
213
|
+
_visitors calculate_visitors(@message)
|
214
|
+
end
|
215
|
+
|
216
|
+
_comments @message.comments, :content, :created_at
|
217
|
+
|
218
|
+
_attachments @message.attachments do |attachment|
|
219
|
+
_filename attachment.filename
|
220
|
+
_url url_for(attachment)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
Invoking methods that start with a Unicode
|
226
|
+
[low line](http://www.fileformat.info/info/unicode/char/5f/index.htm)
|
227
|
+
character ("_") will add a key/value pair to that hash. Hashes can
|
228
|
+
also be nested. Logic can be freely intermixed.
|
229
|
+
|
230
|
+
The "`_`" method serves a number of purposes.
|
231
|
+
|
232
|
+
* calling it with multiple arguments will cause the first argument to be
|
233
|
+
treated as the object, and the remainder as the attributes to be extracted
|
234
|
+
* Example: `_ File.stat('foo'), :mtime, :size, :mode`
|
235
|
+
|
236
|
+
* calling it with a single Enumerable object and a block will cause an array
|
237
|
+
to be returned based on mapping each objection from the enumeration against
|
238
|
+
the block
|
239
|
+
* Example: `_([1,2,3]) {|n| n*n}`
|
240
|
+
|
241
|
+
* arrays can be also be built using the `_` method:
|
242
|
+
_ 1
|
243
|
+
_ 2
|
244
|
+
|
245
|
+
The `_` method returns a proxy to the object being constructed. This is often
|
246
|
+
handy when called with no arguments. Examples:
|
247
|
+
|
248
|
+
_.sort!
|
249
|
+
_['foo'] = 'bar'
|
250
|
+
|
251
|
+
Methods provided to Wunderbar.text
|
252
|
+
---
|
253
|
+
|
254
|
+
Appending to the output stream is done using the `_` method, which is
|
255
|
+
equivalent to `puts`. The `_` method returns an object which proxies the
|
256
|
+
output stream, which provides access to other useful methods, for example:
|
257
|
+
|
258
|
+
_.print 'foo'
|
259
|
+
_.printf "Hello %s!\n", 'world'
|
260
|
+
|
157
261
|
Globals provided
|
158
262
|
---
|
159
263
|
* `$cgi` - Common Gateway Interface
|
data/lib/wunderbar/builder.rb
CHANGED
@@ -170,4 +170,130 @@ module Wunderbar
|
|
170
170
|
$HTTP_POST
|
171
171
|
end
|
172
172
|
end
|
173
|
+
|
174
|
+
class TextBuilder
|
175
|
+
def initialize
|
176
|
+
require 'stringio'
|
177
|
+
@_target = StringIO.new
|
178
|
+
end
|
179
|
+
|
180
|
+
def encode(params = {}, &block)
|
181
|
+
params.each do |key,value|
|
182
|
+
instance_variable_set "@#{key}", value.first if key =~ /^\w+$/
|
183
|
+
end
|
184
|
+
|
185
|
+
self.instance_eval(&block)
|
186
|
+
@_target.string
|
187
|
+
end
|
188
|
+
|
189
|
+
def _(*args)
|
190
|
+
@_target.puts *args if args.length > 0
|
191
|
+
self
|
192
|
+
end
|
193
|
+
|
194
|
+
# forward to either Wunderbar or @_target
|
195
|
+
def method_missing(method, *args, &block)
|
196
|
+
if Wunderbar.respond_to? method
|
197
|
+
return Wunderbar.send method, *args, &block
|
198
|
+
elsif @_target.respond_to? method
|
199
|
+
return @_target.send method, *args, &block
|
200
|
+
else
|
201
|
+
super
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def target!
|
206
|
+
@_target.string
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
class JsonBuilder
|
211
|
+
def initialize
|
212
|
+
@_target = {}
|
213
|
+
end
|
214
|
+
|
215
|
+
def encode(params = {}, &block)
|
216
|
+
params.each do |key,value|
|
217
|
+
instance_variable_set "@#{key}", value.first if key =~ /^\w+$/
|
218
|
+
end
|
219
|
+
|
220
|
+
self.instance_eval(&block)
|
221
|
+
@_target
|
222
|
+
end
|
223
|
+
|
224
|
+
# forward to either Wunderbar or @_target
|
225
|
+
def method_missing(method, *args, &block)
|
226
|
+
|
227
|
+
if method.to_s =~ /^_(\w*)$/
|
228
|
+
name = $1
|
229
|
+
elsif Wunderbar.respond_to? method
|
230
|
+
return Wunderbar.send method, *args, &block
|
231
|
+
elsif @_target.respond_to? method
|
232
|
+
return @_target.send method, *args, &block
|
233
|
+
else
|
234
|
+
super
|
235
|
+
end
|
236
|
+
|
237
|
+
if args.length == 0
|
238
|
+
return self unless block
|
239
|
+
result = JsonBuilder.new.encode(&block)
|
240
|
+
elsif args.length == 1
|
241
|
+
result = args.first
|
242
|
+
|
243
|
+
if block
|
244
|
+
if Symbol === result or String === result
|
245
|
+
result = {result.to_s => JsonBuilder.new.encode(&block)}
|
246
|
+
else
|
247
|
+
result = result.map {|n| @_target = {}; block.call(n); @_target}
|
248
|
+
end
|
249
|
+
end
|
250
|
+
elsif block
|
251
|
+
::Kernel::raise ::ArgumentError,
|
252
|
+
"can't mix multiple arguments with a block"
|
253
|
+
else
|
254
|
+
object = args.shift
|
255
|
+
|
256
|
+
if not Enumerable === object or String === object or Struct === object
|
257
|
+
result = {}
|
258
|
+
args.each {|arg| result[arg.to_s] = object.send arg}
|
259
|
+
else
|
260
|
+
result = object.map do |item|
|
261
|
+
args.inject({}) {|hash, arg| hash[arg.to_s] = item.send arg; hash}
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
if name != ''
|
267
|
+
unless Hash === @_target or @_target.empty?
|
268
|
+
::Kernel::raise ::ArgumentError, "mixed array and hash calls"
|
269
|
+
end
|
270
|
+
|
271
|
+
@_target[name.to_s] = result
|
272
|
+
elsif args.length == 0 or (args.length == 1 and not block)
|
273
|
+
@_target = [] if @_target == {}
|
274
|
+
|
275
|
+
if Hash === @_target
|
276
|
+
::Kernel::raise ::ArgumentError, "mixed hash and array calls"
|
277
|
+
end
|
278
|
+
|
279
|
+
@_target << result
|
280
|
+
else
|
281
|
+
@_target = result
|
282
|
+
end
|
283
|
+
|
284
|
+
self
|
285
|
+
end
|
286
|
+
|
287
|
+
def _!(object)
|
288
|
+
@_target = object
|
289
|
+
end
|
290
|
+
|
291
|
+
def target!
|
292
|
+
begin
|
293
|
+
JSON.pretty_generate(@_target)+ "\n"
|
294
|
+
rescue
|
295
|
+
@_target.to_json + "\n"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
173
299
|
end
|
@@ -4,44 +4,47 @@ module Wunderbar
|
|
4
4
|
|
5
5
|
# produce json
|
6
6
|
def self.json(&block)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
output = instance_eval(&block)
|
7
|
+
builder = JsonBuilder.new
|
8
|
+
output = builder.encode($param, &block)
|
9
|
+
Kernel.print "Status: 404 Not Found\r\n" if output == {}
|
11
10
|
rescue Exception => exception
|
12
11
|
Kernel.print "Status: 500 Internal Error\r\n"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
Wunderbar.error exception.inspect
|
13
|
+
backtrace = []
|
14
|
+
exception.backtrace.each do |frame|
|
15
|
+
next if frame =~ %r{/wunderbar/}
|
16
|
+
next if frame =~ %r{/gems/.*/builder/}
|
17
|
+
Wunderbar.warn " #{frame}"
|
18
|
+
backtrace << frame
|
19
|
+
end
|
20
|
+
builder = JsonBuilder.new
|
21
|
+
builder._exception exception.inspect
|
22
|
+
builder._backtrace backtrace
|
17
23
|
ensure
|
18
|
-
Kernel.print "Status: 404 Not Found\r\n" unless output
|
19
24
|
out? 'type' => 'application/json', 'Cache-Control' => 'no-cache' do
|
20
|
-
|
21
|
-
JSON.pretty_generate(output)+ "\n"
|
22
|
-
rescue
|
23
|
-
output.to_json + "\n"
|
24
|
-
end
|
25
|
+
builder.target!
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
29
|
# produce text
|
29
30
|
def self.text &block
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
instance_variable_set "@#{key}", value.first if key =~ /^\w+$/
|
34
|
-
end
|
35
|
-
buffer.instance_eval &block
|
31
|
+
builder = TextBuilder.new
|
32
|
+
output = builder.encode($param, &block)
|
33
|
+
Kernel.print "Status: 404 Not Found\r\n" if output == ''
|
36
34
|
rescue Exception => exception
|
35
|
+
Wunderbar.error exception.inspect
|
37
36
|
Kernel.print "Status: 500 Internal Error\r\n"
|
38
|
-
|
39
|
-
|
40
|
-
exception.backtrace.each
|
37
|
+
builder.puts unless builder.size == 0
|
38
|
+
builder.puts exception.inspect
|
39
|
+
exception.backtrace.each do |frame|
|
40
|
+
next if frame =~ %r{/wunderbar/}
|
41
|
+
next if frame =~ %r{/gems/.*/builder/}
|
42
|
+
Wunderbar.warn " #{frame}"
|
43
|
+
builder.puts " #{frame}"
|
44
|
+
end
|
41
45
|
ensure
|
42
|
-
Kernel.print "Status: 404 Not Found\r\n" if buffer.size == 0
|
43
46
|
out? 'type' => 'text/plain', 'Cache-Control' => 'no-cache' do
|
44
|
-
|
47
|
+
builder.target!
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
data/lib/wunderbar/logger.rb
CHANGED
@@ -3,7 +3,7 @@ require 'logger'
|
|
3
3
|
module Wunderbar
|
4
4
|
def self.logger
|
5
5
|
return @logger if @logger
|
6
|
-
@logger = Logger.new(
|
6
|
+
@logger = Logger.new($stderr)
|
7
7
|
@logger.level = Logger::WARN
|
8
8
|
@logger.formatter = proc { |severity, datetime, progname, msg|
|
9
9
|
"_#{severity} #{msg}\n"
|
@@ -11,6 +11,10 @@ module Wunderbar
|
|
11
11
|
@logger
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.logger= new_logger
|
15
|
+
@logger = new_logger
|
16
|
+
end
|
17
|
+
|
14
18
|
def self.log_level=(level)
|
15
19
|
return unless level
|
16
20
|
|
data/lib/wunderbar/version.rb
CHANGED
data/wunderbar.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "wunderbar"
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.9.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-
|
9
|
+
s.date = "2012-04-01"
|
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
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"]
|
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: 59
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.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-
|
18
|
+
date: 2012-04-01 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: builder
|