jsonbuilder 0.1.3 → 0.2.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/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 (Builder::JsonMarkup)"
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 eval("#{_current}").is_a?(::Hash)
15
+ if @target.current.is_a?(Hash) && !@target.current.empty?
16
16
  key ||= :entry
17
- eval("#{_current}.merge!(key => [])")
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
- key = @path.pop
52
- eval("#{_current} << _target")
53
- @path.push(key)
49
+ @target.current << _target
50
+ elsif _target.is_a?(Hash)
51
+ @target.current.merge!(_target)
54
52
  else
55
- if _target.is_a?(String)
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
- @default_content_key = default_content_key.to_sym unless default_content_key.nil?
66
- if eval("#{_current}").is_a?(::Hash)
67
- eval("#{_current}.merge!({@default_content_key => text})")
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
- eval("#{_current} = text")
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 = args.first.is_a?(Symbol) ? "#{key}:#{args.shift}".to_sym : key.to_sym
80
- args[0] = {@default_content_key => args[0]} if args.size > 1 && !args[0].is_a?(::Hash)
81
- if @root
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 _root(root, args, &block)
92
- @root = root
93
- @target[root] = {}
94
- @path = [root]
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 ::Hash
110
- self << arg
93
+ case arg
94
+ when Hash
95
+ self << StackableHash.new.replace(arg)
111
96
  else
112
- eval("#{_current} = arg")
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 _current
125
- current_path = @path.inject('') do |current_path, key|
126
- current_path += key.is_a?(Integer) ? "[#{key}]" : "[:\"#{key}\"]"
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
- @array_mode ? @path[@path.size - 1] += 1 : @path.pop
113
+ @target = @target.parent unless @array_mode
135
114
  end
136
115
 
137
116
  def _array_mode(&block)
138
117
  @array_mode = true
139
- @path.push(0)
118
+ @target.current = StackableArray.new
140
119
  yield
141
- @path.pop
142
120
  @array_mode = false
143
121
  end
144
122
 
@@ -22,18 +22,7 @@ module Builder
22
22
  _target.symbolize_keys! if _target.is_a?(Hash)
23
23
  end
24
24
 
25
- if @array_mode
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 = 1
5
- REVISION = 3
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 use the default_content_key when both cdata! and attributes exist" do
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 allow the default_content_key to be specified as a second argument to cdata!" do
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 use the specified default_content_key when it and content and attributes are specified via the content!" do
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 id="1">value</tag></root>
124
+ # XML :: <root><tag><![CDATA[value1]]><![CDATA[value2]]></tag></root>
86
125
  builder.root do
87
- builder.content!(:tag, :text, "value", :id => 1)
126
+ builder.tag do
127
+ builder.cdata! "value1"
128
+ builder.cdata! "value2"
129
+ end
88
130
  end
89
- builder.target!.should == {:tag => {:id => 1, :text => "value"}}
131
+ builder.target!.should == {:tag => "value2"}
90
132
  end
91
133
 
92
- it "should accept strings for insertion" do
134
+ it "should add new value when called multiple times in array mode" do
93
135
  builder = Builder::HashStructure.new
94
- sub_builder = Builder::HashStructure.new
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.tags do |tag|
100
- builder << sub_builder.target!
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 == {:tags => "value"}
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.1.3
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-02 00:00:00 +09:00
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 (Builder::JsonMarkup)
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 (Builder::JsonMarkup)
92
+ summary: Builder::XmlMarkup like JsonBuilder
90
93
  test_files:
91
94
  - spec/jsonbuilder_spec.rb