jsonbuilder 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/builder/hash_structure.rb +31 -53
- data/lib/builder/json_format.rb +1 -12
- data/lib/ext/stackable_array.rb +35 -0
- data/lib/ext/stackable_hash.rb +27 -0
- data/lib/jsonbuilder.rb +4 -2
- data/spec/builder/hash_structure_spec.rb +67 -16
- metadata +7 -4
data/Rakefile
CHANGED
@@ -13,7 +13,7 @@ include FileUtils
|
|
13
13
|
NAME = "jsonbuilder"
|
14
14
|
AUTHOR = "nov"
|
15
15
|
EMAIL = "nov@matake.jp"
|
16
|
-
DESCRIPTION = "Builder::XmlMarkup like JsonBuilder
|
16
|
+
DESCRIPTION = "Builder::XmlMarkup like JsonBuilder"
|
17
17
|
RUBYFORGE_PROJECT = NAME
|
18
18
|
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
19
19
|
BIN_FILES = %w( )
|
@@ -6,26 +6,24 @@ module Builder
|
|
6
6
|
# in this case, we need some key for value.
|
7
7
|
@default_content_key = (options[:default_content_key] || :content).to_sym
|
8
8
|
@include_root = options[:include_root]
|
9
|
-
@target =
|
9
|
+
@target = StackableHash.new
|
10
10
|
@array_mode = false
|
11
11
|
end
|
12
12
|
|
13
13
|
# NOTICE: you have to call this method to use array in json
|
14
14
|
def array_mode(key = nil, &block)
|
15
|
-
if
|
15
|
+
if @target.current.is_a?(Hash) && !@target.current.empty?
|
16
16
|
key ||= :entry
|
17
|
-
|
18
|
-
_move_current(key.to_sym) do
|
17
|
+
_setup_key(key.to_sym) do
|
19
18
|
_array_mode(&block)
|
20
19
|
end
|
21
20
|
else
|
22
|
-
eval("#{_current} = []")
|
23
21
|
_array_mode(&block)
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
25
|
def target!
|
28
|
-
if @include_root
|
26
|
+
if @include_root || @target.is_a?(Array)
|
29
27
|
@target
|
30
28
|
else
|
31
29
|
@target[@root]
|
@@ -48,25 +46,22 @@ module Builder
|
|
48
46
|
|
49
47
|
def <<(_target)
|
50
48
|
if @array_mode
|
51
|
-
|
52
|
-
|
53
|
-
@
|
49
|
+
@target.current << _target
|
50
|
+
elsif _target.is_a?(Hash)
|
51
|
+
@target.current.merge!(_target)
|
54
52
|
else
|
55
|
-
|
56
|
-
eval("#{_current} = _target")
|
57
|
-
else
|
58
|
-
eval("#{_current} ||= {}")
|
59
|
-
eval("#{_current}.merge!(_target)")
|
60
|
-
end
|
53
|
+
@target.current = _target
|
61
54
|
end
|
62
55
|
end
|
63
56
|
|
64
57
|
def text!(text, default_content_key = nil)
|
65
|
-
@
|
66
|
-
|
67
|
-
|
58
|
+
if @target.current.is_a?(Array)
|
59
|
+
@target.current << text
|
60
|
+
elsif @target.current.is_a?(Hash) && !@target.current.empty?
|
61
|
+
@default_content_key = default_content_key.to_sym unless default_content_key.nil?
|
62
|
+
@target.current.merge!(StackableHash.new.replace(@default_content_key => text))
|
68
63
|
else
|
69
|
-
|
64
|
+
@target.current = text
|
70
65
|
end
|
71
66
|
end
|
72
67
|
alias_method :cdata!, :text!
|
@@ -76,40 +71,30 @@ module Builder
|
|
76
71
|
end
|
77
72
|
|
78
73
|
def method_missing(key, *args, &block)
|
79
|
-
key =
|
80
|
-
|
81
|
-
|
82
|
-
_child(key, args, &block)
|
83
|
-
else
|
84
|
-
_root(key, args, &block)
|
74
|
+
key, args = _explore_key_and_args(key, *args)
|
75
|
+
_setup_key(key) do
|
76
|
+
_set_args(args, &block)
|
85
77
|
end
|
86
78
|
target!
|
87
79
|
end
|
88
80
|
|
89
81
|
private
|
90
82
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
_set_args(args, &block)
|
96
|
-
yield(self) if block_given?
|
97
|
-
end
|
98
|
-
|
99
|
-
def _child(key, args, &block)
|
100
|
-
eval("#{_current} ||= {}") unless @array_mode
|
101
|
-
_move_current(key) do
|
102
|
-
_set_args(args, &block)
|
83
|
+
def _explore_key_and_args(key, *args)
|
84
|
+
key = (args.first.is_a?(Symbol) ? "#{key}:#{args.shift}" : key.to_s).gsub(/[-:]/, "_").to_sym
|
85
|
+
if args.size > 1 && !args[0].is_a?(Hash)
|
86
|
+
args[0] = StackableHash.new.replace(@default_content_key => args[0])
|
103
87
|
end
|
88
|
+
[key, args]
|
104
89
|
end
|
105
90
|
|
106
91
|
def _set_args(args, &block)
|
107
92
|
args.each do |arg|
|
108
|
-
case arg
|
109
|
-
when
|
110
|
-
self << arg
|
93
|
+
case arg
|
94
|
+
when Hash
|
95
|
+
self << StackableHash.new.replace(arg)
|
111
96
|
else
|
112
|
-
|
97
|
+
@target.current = arg
|
113
98
|
end
|
114
99
|
end
|
115
100
|
if @array_mode && block_given?
|
@@ -121,24 +106,17 @@ module Builder
|
|
121
106
|
end
|
122
107
|
end
|
123
108
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
127
|
-
end
|
128
|
-
"@target#{current_path}"
|
129
|
-
end
|
130
|
-
|
131
|
-
def _move_current(key, &block)
|
132
|
-
@path.push(key) unless @array_mode
|
109
|
+
def _setup_key(key, &block)
|
110
|
+
@root = key unless @root
|
111
|
+
@target = @target.child(key) unless @array_mode
|
133
112
|
yield
|
134
|
-
@
|
113
|
+
@target = @target.parent unless @array_mode
|
135
114
|
end
|
136
115
|
|
137
116
|
def _array_mode(&block)
|
138
117
|
@array_mode = true
|
139
|
-
@
|
118
|
+
@target.current = StackableArray.new
|
140
119
|
yield
|
141
|
-
@path.pop
|
142
120
|
@array_mode = false
|
143
121
|
end
|
144
122
|
|
data/lib/builder/json_format.rb
CHANGED
@@ -22,18 +22,7 @@ module Builder
|
|
22
22
|
_target.symbolize_keys! if _target.is_a?(Hash)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
key = @path.pop
|
27
|
-
eval("#{_current} << _target")
|
28
|
-
@path.push(key)
|
29
|
-
else
|
30
|
-
if _target.is_a?(String)
|
31
|
-
eval("#{_current} = _target")
|
32
|
-
else
|
33
|
-
eval("#{_current} ||= {}")
|
34
|
-
eval("#{_current}.merge!(_target)")
|
35
|
-
end
|
36
|
-
end
|
25
|
+
super(_target)
|
37
26
|
end
|
38
27
|
|
39
28
|
def target!
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class StackableArray < Array
|
2
|
+
attr_accessor :parent, :current_key
|
3
|
+
|
4
|
+
def child(key)
|
5
|
+
hash = StackableHash.new
|
6
|
+
hash[key] = StackableHash.new
|
7
|
+
|
8
|
+
new_target = self.current
|
9
|
+
new_target.merge!(hash)
|
10
|
+
new_target.current_key = key
|
11
|
+
new_target.parent = self
|
12
|
+
new_target
|
13
|
+
end
|
14
|
+
|
15
|
+
def merge!(hash)
|
16
|
+
if self.last.is_a?(Hash) && !self.last.key?(hash.keys.first)
|
17
|
+
self.last.merge!(hash)
|
18
|
+
else
|
19
|
+
self << hash
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def current=(value)
|
24
|
+
if self.current.is_a?(Hash)
|
25
|
+
self.last[current_key] = value
|
26
|
+
else
|
27
|
+
self << value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def current
|
32
|
+
self.current_key ? self.last[current_key] : self.last
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class StackableHash < Hash
|
2
|
+
attr_accessor :parent, :current_key
|
3
|
+
|
4
|
+
def child(key)
|
5
|
+
hash = StackableHash.new
|
6
|
+
hash[key] = StackableHash.new
|
7
|
+
|
8
|
+
new_target = self.current || self
|
9
|
+
new_target.merge!(hash)
|
10
|
+
new_target.current_key = key
|
11
|
+
new_target.parent = self
|
12
|
+
new_target
|
13
|
+
end
|
14
|
+
|
15
|
+
def <<(value)
|
16
|
+
self[current_key] << value
|
17
|
+
end
|
18
|
+
|
19
|
+
def current=(value)
|
20
|
+
self[current_key] = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def current
|
24
|
+
self[current_key]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/jsonbuilder.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module JsonBuilder
|
2
2
|
module Version
|
3
3
|
MAJOR = 0
|
4
|
-
MINOR =
|
5
|
-
REVISION =
|
4
|
+
MINOR = 2
|
5
|
+
REVISION = 0
|
6
6
|
class << self
|
7
7
|
def to_version
|
8
8
|
"#{MAJOR}.#{MINOR}.#{REVISION}"
|
@@ -19,3 +19,5 @@ require 'builder/abstract'
|
|
19
19
|
require 'builder/xml_markup'
|
20
20
|
require 'builder/hash_structure'
|
21
21
|
require 'builder/json_format'
|
22
|
+
require 'ext/stackable_hash'
|
23
|
+
require 'ext/stackable_array'
|
@@ -13,6 +13,16 @@ end
|
|
13
13
|
|
14
14
|
describe Builder::HashStructure do
|
15
15
|
|
16
|
+
it "should replace ':' and '-' with '_' if those characters are used as a key" do
|
17
|
+
builder = Builder::HashStructure.new
|
18
|
+
builder.root do
|
19
|
+
builder.atom :name, "atom:name" # atom:name
|
20
|
+
builder.thr :"in-reply-to", "thr:in-reply-to" # thr:in-reply-to
|
21
|
+
builder.tag! :"dc:creator", "dc:creator" # thr:in-reply-to
|
22
|
+
end
|
23
|
+
builder.target!.should == {:atom_name => "atom:name", :thr_in_reply_to => "thr:in-reply-to", :dc_creator => "dc:creator"}
|
24
|
+
end
|
25
|
+
|
16
26
|
it "should remove the root tag" do
|
17
27
|
builder = Builder::HashStructure.new
|
18
28
|
# XML :: <root><tag>value</tag></root>
|
@@ -58,7 +68,25 @@ describe Builder::HashStructure do
|
|
58
68
|
builder.target!.should == {:tag => {:id => 1, :text => "value"}}
|
59
69
|
end
|
60
70
|
|
61
|
-
it "should
|
71
|
+
it "should accept strings for insertion" do
|
72
|
+
builder = Builder::HashStructure.new
|
73
|
+
sub_builder = Builder::HashStructure.new
|
74
|
+
sub_builder.tag('value')
|
75
|
+
|
76
|
+
# XML :: <root><tag id="1">value</tag></root>
|
77
|
+
builder.root do
|
78
|
+
builder.tags do |tag|
|
79
|
+
builder << sub_builder.target!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
builder.target!.should == {:tags => "value"}
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
describe Builder::HashStructure, "#cdata!" do
|
88
|
+
|
89
|
+
it "should use the default_content_key when called with attributes" do
|
62
90
|
builder = Builder::HashStructure.new
|
63
91
|
# XML :: <root><tag id="1"><![CDATA[value]]></tag></root>
|
64
92
|
builder.root do
|
@@ -69,7 +97,18 @@ describe Builder::HashStructure do
|
|
69
97
|
builder.target!.should == {:tag => {:id => 1, :content => "value"}}
|
70
98
|
end
|
71
99
|
|
72
|
-
it "should
|
100
|
+
it "should not use the default_content_key when called without attributes" do
|
101
|
+
builder = Builder::HashStructure.new
|
102
|
+
# XML :: <root><tag><![CDATA[value]]></tag></root>
|
103
|
+
builder.root do
|
104
|
+
builder.tag do
|
105
|
+
builder.cdata! "value"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
builder.target!.should == {:tag => "value"}
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should allow the default_content_key to be specified as a second argument" do
|
73
112
|
builder = Builder::HashStructure.new
|
74
113
|
# XML :: <quotes><quote id=\"1\"><![CDATA[All generalizations are false, including this one.]]></quote></quotes>
|
75
114
|
builder.quotes do
|
@@ -80,31 +119,45 @@ describe Builder::HashStructure do
|
|
80
119
|
builder.target!.should == {:quote => {:id => 1, :text => "All generalizations are false, including this one."}}
|
81
120
|
end
|
82
121
|
|
83
|
-
it "should
|
122
|
+
it "should overwrite previous value when called multiple times out of array mode" do
|
84
123
|
builder = Builder::HashStructure.new
|
85
|
-
# XML :: <root><tag
|
124
|
+
# XML :: <root><tag><![CDATA[value1]]><![CDATA[value2]]></tag></root>
|
86
125
|
builder.root do
|
87
|
-
builder.
|
126
|
+
builder.tag do
|
127
|
+
builder.cdata! "value1"
|
128
|
+
builder.cdata! "value2"
|
129
|
+
end
|
88
130
|
end
|
89
|
-
builder.target!.should == {:tag =>
|
131
|
+
builder.target!.should == {:tag => "value2"}
|
90
132
|
end
|
91
133
|
|
92
|
-
it "should
|
134
|
+
it "should add new value when called multiple times in array mode" do
|
93
135
|
builder = Builder::HashStructure.new
|
94
|
-
|
95
|
-
sub_builder.tag('value')
|
96
|
-
|
97
|
-
# XML :: <root><tag id="1">value</tag></root>
|
136
|
+
# XML :: <root><tag id="1"><![CDATA[value]]></tag></root>
|
98
137
|
builder.root do
|
99
|
-
builder.
|
100
|
-
builder
|
138
|
+
builder.tag do
|
139
|
+
builder.array_mode do
|
140
|
+
builder.cdata! "value1"
|
141
|
+
builder.cdata! "value2"
|
142
|
+
end
|
101
143
|
end
|
102
144
|
end
|
103
|
-
builder.target!.should == {:
|
145
|
+
builder.target!.should == {:tag => ["value1", "value2"]}
|
104
146
|
end
|
105
147
|
|
106
148
|
end
|
107
149
|
|
150
|
+
describe Builder::HashStructure, "#content!" do
|
151
|
+
it "should use the specified default_content_key when it and content and attributes are specified via the content!" do
|
152
|
+
builder = Builder::HashStructure.new
|
153
|
+
# XML :: <root><tag id="1">value</tag></root>
|
154
|
+
builder.root do
|
155
|
+
builder.content!(:tag, :text, "value", :id => 1)
|
156
|
+
end
|
157
|
+
builder.target!.should == {:tag => {:id => 1, :text => "value"}}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
108
161
|
describe Builder::HashStructure, '#serialization_method!' do
|
109
162
|
it 'should report the to_hash method' do
|
110
163
|
Builder::HashStructure.new.serialization_method!.should == :to_hash
|
@@ -126,7 +179,6 @@ describe Builder::HashStructure, "#target!" do
|
|
126
179
|
builder.target!.should == {:root => "value"}
|
127
180
|
end
|
128
181
|
|
129
|
-
|
130
182
|
it "should return a HashStructure when root has deeper structure" do
|
131
183
|
builder = Builder::HashStructure.new
|
132
184
|
builder.root do
|
@@ -138,7 +190,6 @@ describe Builder::HashStructure, "#target!" do
|
|
138
190
|
end
|
139
191
|
|
140
192
|
describe Builder::HashStructure, "#root!" do
|
141
|
-
|
142
193
|
it "should force the root tag" do
|
143
194
|
builder = Builder::HashStructure.new
|
144
195
|
builder.root!(:root) do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonbuilder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nov
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-07-
|
12
|
+
date: 2009-07-27 00:00:00 +09:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,7 +22,7 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: "0"
|
24
24
|
version:
|
25
|
-
description: Builder::XmlMarkup like JsonBuilder
|
25
|
+
description: Builder::XmlMarkup like JsonBuilder
|
26
26
|
email: nov@matake.jp
|
27
27
|
executables: []
|
28
28
|
|
@@ -47,6 +47,9 @@ files:
|
|
47
47
|
- lib/builder/hash_structure.rb
|
48
48
|
- lib/builder/json_format.rb
|
49
49
|
- lib/builder/xml_markup.rb
|
50
|
+
- lib/ext
|
51
|
+
- lib/ext/stackable_array.rb
|
52
|
+
- lib/ext/stackable_hash.rb
|
50
53
|
- lib/jsonbuilder.rb
|
51
54
|
- lib/patch
|
52
55
|
- lib/patch/active_support_json_decode.rb
|
@@ -86,6 +89,6 @@ rubyforge_project: jsonbuilder
|
|
86
89
|
rubygems_version: 1.3.1
|
87
90
|
signing_key:
|
88
91
|
specification_version: 2
|
89
|
-
summary: Builder::XmlMarkup like JsonBuilder
|
92
|
+
summary: Builder::XmlMarkup like JsonBuilder
|
90
93
|
test_files:
|
91
94
|
- spec/jsonbuilder_spec.rb
|