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 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