wunderbar 0.8.14 → 0.9.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 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
- Quick Start
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
- expression
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. For html, use the `_` methods described here. For json, the results (typically a hash or array) are converted to JSON. For text, use `puts` and `print` statements to produce the desired results.
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
@@ -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
- $param.each do |key,value|
8
- instance_variable_set "@#{key}", value.first if key =~ /^\w+$/
9
- end
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
- output = {
14
- :exception => exception.inspect,
15
- :backtrace => exception.backtrace
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
- begin
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
- require 'stringio'
31
- buffer = StringIO.new
32
- $param.each do |key,value|
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
- buffer << "\n" unless buffer.size == 0
39
- buffer << exception.inspect + "\n"
40
- exception.backtrace.each {|frame| buffer << " #{frame}\n"}
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
- buffer.string
47
+ builder.target!
45
48
  end
46
49
  end
47
50
 
@@ -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(STDERR)
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
 
@@ -1,8 +1,8 @@
1
1
  module Wunderbar
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 8
5
- TINY = 14
4
+ MINOR = 9
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
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.8.14"
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-03-30"
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: 35
4
+ hash: 59
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 8
9
- - 14
10
- version: 0.8.14
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-03-30 00:00:00 Z
18
+ date: 2012-04-01 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: builder