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