json_builder 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/blankslate.rb +109 -0
- data/lib/json_builder.rb +7 -0
- data/lib/json_builder/blankslate.rb +22 -0
- data/lib/json_builder/jsonbase.rb +131 -0
- data/lib/json_builder/jsonevents.rb +52 -0
- data/lib/json_builder/jsonmarkup.rb +229 -0
- data/lib/json_builder/xchar.rb +119 -0
- data/lib/test.rb +21 -0
- metadata +84 -0
data/lib/blankslate.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
|
4
|
+
# All rights reserved.
|
5
|
+
|
6
|
+
# Permission is granted for use, copying, modification, distribution,
|
7
|
+
# and distribution of modified versions of this work as long as the
|
8
|
+
# above copyright notice is included.
|
9
|
+
#++
|
10
|
+
|
11
|
+
######################################################################
|
12
|
+
# BlankSlate provides an abstract base class with no predefined
|
13
|
+
# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
|
14
|
+
# BlankSlate is useful as a base class when writing classes that
|
15
|
+
# depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
|
16
|
+
#
|
17
|
+
class BlankSlate
|
18
|
+
class << self
|
19
|
+
|
20
|
+
# Hide the method named +name+ in the BlankSlate class. Don't
|
21
|
+
# hide +instance_eval+ or any method beginning with "__".
|
22
|
+
def hide(name)
|
23
|
+
if instance_methods.include?(name.to_s) and
|
24
|
+
name !~ /^(__|instance_eval)/
|
25
|
+
@hidden_methods ||= {}
|
26
|
+
@hidden_methods[name.to_sym] = instance_method(name)
|
27
|
+
undef_method name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_hidden_method(name)
|
32
|
+
@hidden_methods ||= {}
|
33
|
+
@hidden_methods[name] || superclass.find_hidden_method(name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Redefine a previously hidden method so that it may be called on a blank
|
37
|
+
# slate object.
|
38
|
+
def reveal(name)
|
39
|
+
hidden_method = find_hidden_method(name)
|
40
|
+
fail "Don't know how to reveal method '#{name}'" unless hidden_method
|
41
|
+
define_method(name, hidden_method)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
instance_methods.each { |m| hide(m) }
|
46
|
+
end
|
47
|
+
|
48
|
+
######################################################################
|
49
|
+
# Since Ruby is very dynamic, methods added to the ancestors of
|
50
|
+
# BlankSlate <em>after BlankSlate is defined</em> will show up in the
|
51
|
+
# list of available BlankSlate methods. We handle this by defining a
|
52
|
+
# hook in the Object and Kernel classes that will hide any method
|
53
|
+
# defined after BlankSlate has been loaded.
|
54
|
+
#
|
55
|
+
module Kernel
|
56
|
+
class << self
|
57
|
+
alias_method :blank_slate_method_added, :method_added
|
58
|
+
|
59
|
+
# Detect method additions to Kernel and remove them in the
|
60
|
+
# BlankSlate class.
|
61
|
+
def method_added(name)
|
62
|
+
result = blank_slate_method_added(name)
|
63
|
+
return result if self != Kernel
|
64
|
+
BlankSlate.hide(name)
|
65
|
+
result
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
######################################################################
|
71
|
+
# Same as above, except in Object.
|
72
|
+
#
|
73
|
+
class Object
|
74
|
+
class << self
|
75
|
+
alias_method :blank_slate_method_added, :method_added
|
76
|
+
|
77
|
+
# Detect method additions to Object and remove them in the
|
78
|
+
# BlankSlate class.
|
79
|
+
def method_added(name)
|
80
|
+
result = blank_slate_method_added(name)
|
81
|
+
return result if self != Object
|
82
|
+
BlankSlate.hide(name)
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
def find_hidden_method(name)
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
######################################################################
|
93
|
+
# Also, modules included into Object need to be scanned and have their
|
94
|
+
# instance methods removed from blank slate. In theory, modules
|
95
|
+
# included into Kernel would have to be removed as well, but a
|
96
|
+
# "feature" of Ruby prevents late includes into modules from being
|
97
|
+
# exposed in the first place.
|
98
|
+
#
|
99
|
+
class Module
|
100
|
+
alias blankslate_original_append_features append_features
|
101
|
+
def append_features(mod)
|
102
|
+
result = blankslate_original_append_features(mod)
|
103
|
+
return result if mod != Object
|
104
|
+
instance_methods.each do |name|
|
105
|
+
BlankSlate.hide(name)
|
106
|
+
end
|
107
|
+
result
|
108
|
+
end
|
109
|
+
end
|
data/lib/json_builder.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
|
4
|
+
# All rights reserved.
|
5
|
+
|
6
|
+
# Permission is granted for use, copying, modification, distribution,
|
7
|
+
# and distribution of modified versions of this work as long as the
|
8
|
+
# above copyright notice is included.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'blankslate'
|
12
|
+
|
13
|
+
######################################################################
|
14
|
+
# BlankSlate has been promoted to a top level name and is now
|
15
|
+
# available as a standalone gem. We make the name available in the
|
16
|
+
# Builder namespace for compatibility.
|
17
|
+
#
|
18
|
+
unless defined?(Builder)
|
19
|
+
module JsonBuilder
|
20
|
+
BlankSlate = ::BlankSlate
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'json_builder/blankslate'
|
2
|
+
|
3
|
+
module JsonBuilder
|
4
|
+
|
5
|
+
# Generic error for builder
|
6
|
+
class IllegalBlockError < RuntimeError; end
|
7
|
+
|
8
|
+
# JsonBase is a base class for building XML builders. See
|
9
|
+
# JsonBuilder::JsonMarkup and JsonBuilder::JsonEvents for examples.
|
10
|
+
class JsonBase < BlankSlate
|
11
|
+
|
12
|
+
# Create an XML markup builder.
|
13
|
+
#
|
14
|
+
# out:: Object receiving the markup. +out+ must respond to
|
15
|
+
# <tt><<</tt>.
|
16
|
+
# indent:: Number of spaces used for indentation (0 implies no
|
17
|
+
# indentation and no line breaks).
|
18
|
+
# initial:: Level of initial indentation.
|
19
|
+
# encoding:: When <tt>encoding</tt> and $KCODE are set to 'utf-8'
|
20
|
+
# characters aren't converted to character entities in
|
21
|
+
# the output stream.
|
22
|
+
def initialize(encoding='utf-8')
|
23
|
+
@encoding = encoding.downcase
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a tag named +sym+. Other than the first argument which
|
27
|
+
# is the tag name, the arguments are the same as the tags
|
28
|
+
# implemented via <tt>method_missing</tt>.
|
29
|
+
def tag!(sym, *args, &block)
|
30
|
+
method_missing(sym.to_sym, args, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create XML markup based on the name of the method. This method
|
34
|
+
# is never invoked directly, but is called for each markup method
|
35
|
+
# in the markup block.
|
36
|
+
def method_missing(sym, *args, &block)
|
37
|
+
text = nil
|
38
|
+
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
|
39
|
+
|
40
|
+
@target = '{' if @target.length == 0
|
41
|
+
@target.chop! if @target.last == '}'
|
42
|
+
|
43
|
+
args.each do |arg|
|
44
|
+
case arg
|
45
|
+
when Hash
|
46
|
+
attrs ||= {}
|
47
|
+
attrs.merge!(arg)
|
48
|
+
else
|
49
|
+
text = arg
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if block
|
54
|
+
raise ArgumentError, "JsonMarkup cannot mix a text argument with a block" unless text.nil?
|
55
|
+
|
56
|
+
_start_tag(sym)
|
57
|
+
block.call(self)
|
58
|
+
_end_tag
|
59
|
+
elsif text.nil?
|
60
|
+
_start_tag(sym)
|
61
|
+
else
|
62
|
+
_start_attr(sym)
|
63
|
+
text! text
|
64
|
+
_end_tag
|
65
|
+
end
|
66
|
+
|
67
|
+
@target.gsub(/,(\}+)$/, '\1')
|
68
|
+
end
|
69
|
+
|
70
|
+
# Append text to the output target. Escape any markup. May be
|
71
|
+
# used within the markup brackets as:
|
72
|
+
#
|
73
|
+
# builder.p { |b| b.br; b.text! "HI" } #=> <p><br/>HI</p>
|
74
|
+
def text!(text)
|
75
|
+
value = case text
|
76
|
+
when TrueClass then 'true'
|
77
|
+
when FalseClass then 'false'
|
78
|
+
when NilClass then 'undefined'
|
79
|
+
when Float then text.to_s
|
80
|
+
when Fixnum then text.to_s
|
81
|
+
else "\"#{_escape(text)}\""
|
82
|
+
end
|
83
|
+
|
84
|
+
_text(value)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Append text to the output target without escaping any markup.
|
88
|
+
# May be used within the markup brackets as:
|
89
|
+
#
|
90
|
+
# builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
|
91
|
+
#
|
92
|
+
# This is useful when using non-builder enabled software that
|
93
|
+
# generates strings. Just insert the string directly into the
|
94
|
+
# builder without changing the inserted markup.
|
95
|
+
#
|
96
|
+
# It is also useful for stacking builder objects. Builders only
|
97
|
+
# use <tt><<</tt> to append to the target, so by supporting this
|
98
|
+
# method/operation builders can use other builders as their
|
99
|
+
# targets.
|
100
|
+
def <<(text)
|
101
|
+
_text(text)
|
102
|
+
end
|
103
|
+
|
104
|
+
# For some reason, nil? is sent to the JsonMarkup object. If nil?
|
105
|
+
# is not defined and method_missing is invoked, some strange kind
|
106
|
+
# of recursion happens. Since nil? won't ever be an XML tag, it
|
107
|
+
# is pretty safe to define it here. (Note: this is an example of
|
108
|
+
# cargo cult programming,
|
109
|
+
# cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
|
110
|
+
def nil?
|
111
|
+
false
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
require 'json_builder/xchar'
|
117
|
+
def _escape(text)
|
118
|
+
text.to_xs((@encoding != 'utf-8' or $KCODE != 'UTF8'))
|
119
|
+
rescue NoMethodError
|
120
|
+
text
|
121
|
+
end
|
122
|
+
|
123
|
+
def _escape_quote(text)
|
124
|
+
_escape(text).gsub(%r{"}, '"') # " WART
|
125
|
+
end
|
126
|
+
|
127
|
+
def _newline
|
128
|
+
text! "\n"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'json_builder/jsonmarkup'
|
2
|
+
|
3
|
+
module JsonBuilder
|
4
|
+
|
5
|
+
# Create a series of SAX-like XML events (e.g. start_tag, end_tag)
|
6
|
+
# from the markup code. XmlEvent objects are used in a way similar
|
7
|
+
# to JsonMarkup objects, except that a series of events are generated
|
8
|
+
# and passed to a handler rather than generating character-based
|
9
|
+
# markup.
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
# xe = JsonBuilder::JsonEvents.new(hander)
|
13
|
+
# xe.title("HI") # Sends start_tag/end_tag/text messages to the handler.
|
14
|
+
#
|
15
|
+
# Indentation may also be selected by providing value for the
|
16
|
+
# indentation size and initial indentation level.
|
17
|
+
#
|
18
|
+
# xe = JsonBuilder::JsonEvents.new(handler, indent_size, initial_indent_level)
|
19
|
+
#
|
20
|
+
# == XML Event Handler
|
21
|
+
#
|
22
|
+
# The handler object must expect the following events.
|
23
|
+
#
|
24
|
+
# [<tt>start_tag(tag, attrs)</tt>]
|
25
|
+
# Announces that a new tag has been found. +tag+ is the name of
|
26
|
+
# the tag and +attrs+ is a hash of attributes for the tag.
|
27
|
+
#
|
28
|
+
# [<tt>end_tag(tag)</tt>]
|
29
|
+
# Announces that an end tag for +tag+ has been found.
|
30
|
+
#
|
31
|
+
# [<tt>text(text)</tt>]
|
32
|
+
# Announces that a string of characters (+text+) has been found.
|
33
|
+
# A series of characters may be broken up into more than one
|
34
|
+
# +text+ call, so the client cannot assume that a single
|
35
|
+
# callback contains all the text data.
|
36
|
+
#
|
37
|
+
class JsonEvents < JsonMarkup
|
38
|
+
def text!(text)
|
39
|
+
@target.text(text)
|
40
|
+
end
|
41
|
+
|
42
|
+
def _start_tag(sym, attrs)
|
43
|
+
@target.start_tag(sym, attrs)
|
44
|
+
_end_tag
|
45
|
+
end
|
46
|
+
|
47
|
+
def _end_tag
|
48
|
+
@target.end_tag
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'json_builder/jsonbase'
|
2
|
+
|
3
|
+
module JsonBuilder
|
4
|
+
|
5
|
+
# Create XML markup easily. All (well, almost all) methods sent to
|
6
|
+
# an JsonMarkup object will be translated to the equivalent XML
|
7
|
+
# markup. Any method with a block will be treated as an XML markup
|
8
|
+
# tag with nested markup in the block.
|
9
|
+
#
|
10
|
+
# Examples will demonstrate this easier than words. In the
|
11
|
+
# following, +xm+ is an +JsonMarkup+ object.
|
12
|
+
#
|
13
|
+
# xm.em("emphasized") # => <em>emphasized</em>
|
14
|
+
# xm.em { xm.b("emp & bold") } # => <em><b>emph & bold</b></em>
|
15
|
+
# xm.a("A Link", "href"=>"http://onestepback.org")
|
16
|
+
# # => <a href="http://onestepback.org">A Link</a>
|
17
|
+
# xm.div { xm.br } # => <div><br/></div>
|
18
|
+
# xm.target("name"=>"compile", "option"=>"fast")
|
19
|
+
# # => <target option="fast" name="compile"\>
|
20
|
+
# # NOTE: order of attributes is not specified.
|
21
|
+
#
|
22
|
+
# xm.instruct! # <?xml version="1.0" encoding="UTF-8"?>
|
23
|
+
# xm.html { # <html>
|
24
|
+
# xm.head { # <head>
|
25
|
+
# xm.title("History") # <title>History</title>
|
26
|
+
# } # </head>
|
27
|
+
# xm.body { # <body>
|
28
|
+
# xm.comment! "HI" # <!-- HI -->
|
29
|
+
# xm.h1("Header") # <h1>Header</h1>
|
30
|
+
# xm.p("paragraph") # <p>paragraph</p>
|
31
|
+
# } # </body>
|
32
|
+
# } # </html>
|
33
|
+
#
|
34
|
+
# == Notes:
|
35
|
+
#
|
36
|
+
# * The order that attributes are inserted in markup tags is
|
37
|
+
# undefined.
|
38
|
+
#
|
39
|
+
# * Sometimes you wish to insert text without enclosing tags. Use
|
40
|
+
# the <tt>text!</tt> method to accomplish this.
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
#
|
44
|
+
# xm.div { # <div>
|
45
|
+
# xm.text! "line"; xm.br # line<br/>
|
46
|
+
# xm.text! "another line"; xmbr # another line<br/>
|
47
|
+
# } # </div>
|
48
|
+
#
|
49
|
+
# * The special XML characters <, >, and & are converted to <,
|
50
|
+
# > and & automatically. Use the <tt><<</tt> operation to
|
51
|
+
# insert text without modification.
|
52
|
+
#
|
53
|
+
# * Sometimes tags use special characters not allowed in ruby
|
54
|
+
# identifiers. Use the <tt>tag!</tt> method to handle these
|
55
|
+
# cases.
|
56
|
+
#
|
57
|
+
# Example:
|
58
|
+
#
|
59
|
+
# xml.tag!("SOAP:Envelope") { ... }
|
60
|
+
#
|
61
|
+
# will produce ...
|
62
|
+
#
|
63
|
+
# <SOAP:Envelope> ... </SOAP:Envelope>"
|
64
|
+
#
|
65
|
+
# <tt>tag!</tt> will also take text and attribute arguments (after
|
66
|
+
# the tag name) like normal markup methods. (But see the next
|
67
|
+
# bullet item for a better way to handle XML namespaces).
|
68
|
+
#
|
69
|
+
# * Direct support for XML namespaces is now available. If the
|
70
|
+
# first argument to a tag call is a symbol, it will be joined to
|
71
|
+
# the tag to produce a namespace:tag combination. It is easier to
|
72
|
+
# show this than describe it.
|
73
|
+
#
|
74
|
+
# xml.SOAP :Envelope do ... end
|
75
|
+
#
|
76
|
+
# Just put a space before the colon in a namespace to produce the
|
77
|
+
# right form for builder (e.g. "<tt>SOAP:Envelope</tt>" =>
|
78
|
+
# "<tt>xml.SOAP :Envelope</tt>")
|
79
|
+
#
|
80
|
+
# * JsonMarkup builds the markup in any object (called a _target_)
|
81
|
+
# that accepts the <tt><<</tt> method. If no target is given,
|
82
|
+
# then JsonMarkup defaults to a string target.
|
83
|
+
#
|
84
|
+
# Examples:
|
85
|
+
#
|
86
|
+
# xm = JsonBuilder::JsonMarkup.new
|
87
|
+
# result = xm.title("yada")
|
88
|
+
# # result is a string containing the markup.
|
89
|
+
#
|
90
|
+
# buffer = ""
|
91
|
+
# xm = JsonBuilder::JsonMarkup.new(buffer)
|
92
|
+
# # The markup is appended to buffer (using <<)
|
93
|
+
#
|
94
|
+
# xm = JsonBuilder::JsonMarkup.new(STDOUT)
|
95
|
+
# # The markup is written to STDOUT (using <<)
|
96
|
+
#
|
97
|
+
# xm = JsonBuilder::JsonMarkup.new
|
98
|
+
# x2 = JsonBuilder::JsonMarkup.new(:target=>xm)
|
99
|
+
# # Markup written to +x2+ will be send to +xm+.
|
100
|
+
#
|
101
|
+
# * Indentation is enabled by providing the number of spaces to
|
102
|
+
# indent for each level as a second argument to XmlBuilder.new.
|
103
|
+
# Initial indentation may be specified using a third parameter.
|
104
|
+
#
|
105
|
+
# Example:
|
106
|
+
#
|
107
|
+
# xm = Builder.new(:indent=>2)
|
108
|
+
# # xm will produce nicely formatted and indented XML.
|
109
|
+
#
|
110
|
+
# xm = Builder.new(:indent=>2, :margin=>4)
|
111
|
+
# # xm will produce nicely formatted and indented XML with 2
|
112
|
+
# # spaces per indent and an over all indentation level of 4.
|
113
|
+
#
|
114
|
+
# builder = JsonBuilder::JsonMarkup.new(:target=>$stdout, :indent=>2)
|
115
|
+
# builder.name { |b| b.first("Jim"); b.last("Weirich) }
|
116
|
+
# # prints:
|
117
|
+
# # <name>
|
118
|
+
# # <first>Jim</first>
|
119
|
+
# # <last>Weirich</last>
|
120
|
+
# # </name>
|
121
|
+
#
|
122
|
+
# * The instance_eval implementation which forces self to refer to
|
123
|
+
# the message receiver as self is now obsolete. We now use normal
|
124
|
+
# block calls to execute the markup block. This means that all
|
125
|
+
# markup methods must now be explicitly send to the xml builder.
|
126
|
+
# For instance, instead of
|
127
|
+
#
|
128
|
+
# xml.div { strong("text") }
|
129
|
+
#
|
130
|
+
# you need to write:
|
131
|
+
#
|
132
|
+
# xml.div { xml.strong("text") }
|
133
|
+
#
|
134
|
+
# Although more verbose, the subtle change in semantics within the
|
135
|
+
# block was found to be prone to error. To make this change a
|
136
|
+
# little less cumbersome, the markup block now gets the markup
|
137
|
+
# object sent as an argument, allowing you to use a shorter alias
|
138
|
+
# within the block.
|
139
|
+
#
|
140
|
+
# For example:
|
141
|
+
#
|
142
|
+
# xml_builder = JsonBuilder::JsonMarkup.new
|
143
|
+
# xml_builder.div { |xml|
|
144
|
+
# xml.stong("text")
|
145
|
+
# }
|
146
|
+
#
|
147
|
+
class JsonMarkup < JsonBase
|
148
|
+
|
149
|
+
# Create an XML markup builder. Parameters are specified by an
|
150
|
+
# option hash.
|
151
|
+
#
|
152
|
+
# :target=><em>target_object</em>::
|
153
|
+
# Object receiving the markup. +target_object+ must respond to
|
154
|
+
# the <tt><<(<em>a_string</em>)</tt> operator and return
|
155
|
+
# itself. The default target is a plain string target.
|
156
|
+
#
|
157
|
+
# :indent=><em>indentation</em>::
|
158
|
+
# Number of spaces used for indentation. The default is no
|
159
|
+
# indentation and no line breaks.
|
160
|
+
#
|
161
|
+
# :margin=><em>initial_indentation_level</em>::
|
162
|
+
# Amount of initial indentation (specified in levels, not
|
163
|
+
# spaces).
|
164
|
+
#
|
165
|
+
# :escape_attrs=><em>OBSOLETE</em>::
|
166
|
+
# The :escape_attrs option is no longer supported by builder
|
167
|
+
# (and will be quietly ignored). String attribute values are
|
168
|
+
# now automatically escaped. If you need unescaped attribute
|
169
|
+
# values (perhaps you are using entities in the attribute
|
170
|
+
# values), then give the value as a Symbol. This allows much
|
171
|
+
# finer control over escaping attribute values.
|
172
|
+
#
|
173
|
+
def initialize(options={})
|
174
|
+
super()
|
175
|
+
@target = options[:target] || ''
|
176
|
+
end
|
177
|
+
|
178
|
+
# Return the target of the builder.
|
179
|
+
def target!
|
180
|
+
@target
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
# NOTE: All private methods of a builder object are prefixed when
|
186
|
+
# a "_" character to avoid possible conflict with XML tag names.
|
187
|
+
|
188
|
+
# Insert text directly in to the builder's target.
|
189
|
+
def _text(text)
|
190
|
+
@target << "#{text},"
|
191
|
+
end
|
192
|
+
|
193
|
+
# Start an JSON object. If <tt>end_too</tt> is true, then the start
|
194
|
+
# tag is also the end tag (e.g. <br/>
|
195
|
+
def _start_tag(sym)
|
196
|
+
case @target.last
|
197
|
+
when ['}',','] then @target << '{'
|
198
|
+
end
|
199
|
+
|
200
|
+
@target << "\"#{sym}\":{"
|
201
|
+
end
|
202
|
+
|
203
|
+
# Start an XML tag. If <tt>end_too</tt> is true, then the start
|
204
|
+
# tag is also the end tag (e.g. <br/>
|
205
|
+
def _start_attr(sym)
|
206
|
+
@target << "\"#{sym}\":"
|
207
|
+
end
|
208
|
+
|
209
|
+
# Insert an ending tag.
|
210
|
+
def _end_tag
|
211
|
+
@target << '}'
|
212
|
+
end
|
213
|
+
|
214
|
+
def _ensure_no_block(got_block)
|
215
|
+
if got_block
|
216
|
+
fail IllegalBlockError,
|
217
|
+
"Blocks are not allowed on JSON instructions"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
class String
|
226
|
+
def last
|
227
|
+
self.split('')[self.length-1]
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# The XChar library is provided courtesy of Sam Ruby (See
|
4
|
+
# http://intertwingly.net/stories/2005/09/28/xchar.rb)
|
5
|
+
|
6
|
+
# --------------------------------------------------------------------
|
7
|
+
|
8
|
+
# If the JsonBuilder::XChar module is not currently defined, fail on any
|
9
|
+
# name clashes in standard library classes.
|
10
|
+
|
11
|
+
module JsonBuilder
|
12
|
+
def self.check_for_name_collision(klass, method_name, defined_constant=nil)
|
13
|
+
if klass.instance_methods.include?(method_name.to_s)
|
14
|
+
fail RuntimeError,
|
15
|
+
"Name Collision: Method '#{method_name}' is already defined in #{klass}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if ! defined?(JsonBuilder::XChar)
|
21
|
+
JsonBuilder.check_for_name_collision(String, "to_xs")
|
22
|
+
JsonBuilder.check_for_name_collision(Fixnum, "xchr")
|
23
|
+
end
|
24
|
+
|
25
|
+
######################################################################
|
26
|
+
module JsonBuilder
|
27
|
+
|
28
|
+
####################################################################
|
29
|
+
# XML Character converter, from Sam Ruby:
|
30
|
+
# (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
|
31
|
+
#
|
32
|
+
module XChar # :nodoc:
|
33
|
+
|
34
|
+
# See
|
35
|
+
# http://intertwingly.net/stories/2004/04/14/i18n.html#CleaningWindows
|
36
|
+
# for details.
|
37
|
+
CP1252 = { # :nodoc:
|
38
|
+
128 => 8364, # euro sign
|
39
|
+
130 => 8218, # single low-9 quotation mark
|
40
|
+
131 => 402, # latin small letter f with hook
|
41
|
+
132 => 8222, # double low-9 quotation mark
|
42
|
+
133 => 8230, # horizontal ellipsis
|
43
|
+
134 => 8224, # dagger
|
44
|
+
135 => 8225, # double dagger
|
45
|
+
136 => 710, # modifier letter circumflex accent
|
46
|
+
137 => 8240, # per mille sign
|
47
|
+
138 => 352, # latin capital letter s with caron
|
48
|
+
139 => 8249, # single left-pointing angle quotation mark
|
49
|
+
140 => 338, # latin capital ligature oe
|
50
|
+
142 => 381, # latin capital letter z with caron
|
51
|
+
145 => 8216, # left single quotation mark
|
52
|
+
146 => 8217, # right single quotation mark
|
53
|
+
147 => 8220, # left double quotation mark
|
54
|
+
148 => 8221, # right double quotation mark
|
55
|
+
149 => 8226, # bullet
|
56
|
+
150 => 8211, # en dash
|
57
|
+
151 => 8212, # em dash
|
58
|
+
152 => 732, # small tilde
|
59
|
+
153 => 8482, # trade mark sign
|
60
|
+
154 => 353, # latin small letter s with caron
|
61
|
+
155 => 8250, # single right-pointing angle quotation mark
|
62
|
+
156 => 339, # latin small ligature oe
|
63
|
+
158 => 382, # latin small letter z with caron
|
64
|
+
159 => 376, # latin capital letter y with diaeresis
|
65
|
+
}
|
66
|
+
|
67
|
+
# See http://www.w3.org/TR/REC-xml/#dt-chardata for details.
|
68
|
+
PREDEFINED = {
|
69
|
+
38 => '&', # ampersand
|
70
|
+
60 => '<', # left angle bracket
|
71
|
+
62 => '>', # right angle bracket
|
72
|
+
}
|
73
|
+
|
74
|
+
# See http://www.w3.org/TR/REC-xml/#charsets for details.
|
75
|
+
VALID = [
|
76
|
+
0x9, 0xA, 0xD,
|
77
|
+
(0x20..0xD7FF),
|
78
|
+
(0xE000..0xFFFD),
|
79
|
+
(0x10000..0x10FFFF)
|
80
|
+
]
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
######################################################################
|
87
|
+
# Enhance the Fixnum class with a XML escaped character conversion.
|
88
|
+
#
|
89
|
+
class Fixnum
|
90
|
+
XChar = JsonBuilder::XChar if ! defined?(XChar)
|
91
|
+
|
92
|
+
# XML escaped version of chr. When <tt>escape</tt> is set to false
|
93
|
+
# the CP1252 fix is still applied but utf-8 characters are not
|
94
|
+
# converted to character entities.
|
95
|
+
def xchr(escape=true)
|
96
|
+
n = XChar::CP1252[self] || self
|
97
|
+
case n when *XChar::VALID
|
98
|
+
XChar::PREDEFINED[n] or (n<128 ? n.chr : (escape ? "&##{n};" : [n].pack('U*')))
|
99
|
+
else
|
100
|
+
'*'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
######################################################################
|
107
|
+
# Enhance the String class with a XML escaped character version of
|
108
|
+
# to_s.
|
109
|
+
#
|
110
|
+
class String
|
111
|
+
# XML escaped version of to_s. When <tt>escape</tt> is set to false
|
112
|
+
# the CP1252 fix is still applied but utf-8 characters are not
|
113
|
+
# converted to character entities.
|
114
|
+
def to_xs(escape=true)
|
115
|
+
unpack('U*').map {|n| n.xchr(escape)}.join # ASCII, UTF-8
|
116
|
+
rescue
|
117
|
+
unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
|
118
|
+
end
|
119
|
+
end
|
data/lib/test.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'json_builder'
|
2
|
+
@json = JsonBuilder::JsonMarkup.new
|
3
|
+
string = @json.user do
|
4
|
+
@json.name "Garrett"
|
5
|
+
@json.email "xhtmlthis@me.com"
|
6
|
+
@json.avatar do
|
7
|
+
@json.url "http://url.com"
|
8
|
+
@json.sizes do
|
9
|
+
@json.bool true
|
10
|
+
@json.sizes 123123123123
|
11
|
+
@json.floats 13.37
|
12
|
+
@json.small "small"
|
13
|
+
@json.tiny "tiny"
|
14
|
+
@json.large "large"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
user = JSON.parse(string)
|
20
|
+
puts user['user']['name']
|
21
|
+
# puts string
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json_builder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 1.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Garrett Bjerkhoel
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-25 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 4
|
30
|
+
- 3
|
31
|
+
version: 1.4.3
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
description: JSON Builder
|
35
|
+
email: garrett@clientend.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
files:
|
43
|
+
- lib/blankslate.rb
|
44
|
+
- lib/json_builder/blankslate.rb
|
45
|
+
- lib/json_builder/jsonbase.rb
|
46
|
+
- lib/json_builder/jsonevents.rb
|
47
|
+
- lib/json_builder/jsonmarkup.rb
|
48
|
+
- lib/json_builder/xchar.rb
|
49
|
+
- lib/json_builder.rb
|
50
|
+
- lib/test.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://www.clientend.com
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 1
|
66
|
+
- 8
|
67
|
+
- 7
|
68
|
+
version: 1.8.7
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
requirements:
|
77
|
+
- none
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.6
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: A JSON Builder for Rails
|
83
|
+
test_files: []
|
84
|
+
|