Name | Total Lines | Lines of Code | Total Coverage | Code Coverage |
---|---|---|---|---|
lib/asir/coder/xml.rb | 212 | 191 | 96.70%
|
96.34%
|
Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.
1 require 'asir' |
2 require 'asir/object_resolving' |
3 gem 'libxml-ruby' |
4 require 'xml' |
5 |
6 module ASIR |
7 class Coder |
8 # !SLIDE |
9 # XML |
10 # |
11 # Encode/Decode objects as XML. |
12 class XML < self |
13 class Error < ::ASIR::Error |
14 class BadIdref < self; end |
15 end |
16 def _encode obj |
17 @stream = '' |
18 @dom_id_map = { } |
19 @dom_id = 0 |
20 @cls_tag_map = { } |
21 encode_dom obj |
22 @stream |
23 end |
24 |
25 def _decode obj |
26 @stream = obj |
27 @decoder ||= DECODER; @decoder_object = nil |
28 @dom_id_map = { } |
29 @dom_id = 0 |
30 @cls_tag_map = { } |
31 @parser = ::XML::Parser.string(@stream) |
32 @dom = @parser.parse |
33 decode_dom @dom.root |
34 end |
35 |
36 def encode_dom obj |
37 if dom_id = @dom_id_map[obj.object_id] |
38 tag_obj(obj, nil, :idref => dom_id.first) |
39 else |
40 _encode_dom obj |
41 end |
42 end |
43 |
44 def _encode_dom obj |
45 case obj |
46 when NilClass, TrueClass, FalseClass |
47 tag_obj(obj) |
48 when Numeric |
49 tag_obj(obj, nil, :v => obj.to_s) |
50 when Symbol |
51 tag_obj(obj) do |
52 @stream << obj.to_s |
53 end |
54 when String |
55 tag_obj(obj, :id) do |
56 @stream << obj.to_s |
57 end |
58 when Array |
59 tag_obj(obj, :id) do |
60 obj.each do | elem | |
61 encode_dom elem |
62 end |
63 end |
64 when Hash |
65 tag_obj(obj, :id) do |
66 obj.each do | key, val | |
67 encode_dom key |
68 encode_dom val |
69 end |
70 end |
71 else |
72 tag_obj(obj, :id) do |
73 obj.instance_variables.each do | attr | |
74 val = obj.instance_variable_get(attr) |
75 key = attr.to_s.sub(/^@/, '') |
76 tag(key) do |
77 encode_dom val |
78 end |
79 end |
80 end |
81 end |
82 end |
83 |
84 def tag_obj obj, with_id = false, attrs = nil |
85 if block_given? |
86 tag(cls_tag(obj), with_id ? { with_id => map_obj_to_dom_id!(obj) } : nil) do |
87 yield |
88 end |
89 else |
90 tag(cls_tag(obj), attrs) |
91 end |
92 end |
93 |
94 CC = '::'.freeze; D = '.'.freeze |
95 def cls_tag obj |
96 obj = obj.class |
97 @cls_tag_map[obj] ||= obj.name.gsub(CC, D).freeze |
98 end |
99 |
100 def map_obj_to_dom_id! obj |
101 if dom_id = @dom_id_map[obj.object_id] |
102 dom_id.first |
103 else |
104 @dom_id_map[obj.object_id] = [ @dom_id += 1, obj ] |
105 @dom_id |
106 end |
107 end |
108 |
109 B = '<'.freeze; S = ' '.freeze; E = '>'.freeze; SE = '/>'.freeze |
110 BS = '</'.freeze; A = '='.freeze |
111 def tag tag, attrs = nil |
112 tag = tag.to_s |
113 @stream << B << tag << S |
114 if attrs |
115 attrs.each do | key, val | |
116 @stream << key.to_s << A << val.to_s.inspect << S |
117 end |
118 end |
119 if block_given? |
120 @stream << E; yield; @stream << BS << tag << E |
121 else |
122 @stream << SE |
123 end |
124 end |
125 |
126 ################################################################ |
127 |
128 def decode_dom dom |
129 if dom_id = dom.attributes[:idref] |
130 unless obj = @dom_id_map[dom_id] |
131 raise Error::BadIdref, "in element #{dom}" |
132 end |
133 obj |
134 else |
135 obj = _decode_dom(dom) |
136 map_dom_id_to_obj! dom, obj if dom.attributes[:id] |
137 obj |
138 end |
139 end |
140 |
141 def _decode_dom dom |
142 cls_name = dom.name |
143 decoder = @decoder[cls_name] || |
144 (@decoder_object ||= @decoder['Object']) |
145 raise Error, "BUG: " unless decoder |
146 decoder.call(self, dom) |
147 end |
148 |
149 DECODER = { |
150 'NilClass' => lambda { | _, dom | nil }, |
151 'TrueClass' => lambda { | _, dom | true }, |
152 'FalseClass' => lambda { | _, dom | false }, |
153 'String' => lambda { | _, dom | (dom.attributes[:v] || dom.content) }, |
154 'Symbol' => lambda { | _, dom | (dom.attributes[:v] || dom.content).to_sym }, |
155 'Integer' => lambda { | _, dom | (dom.attributes[:v] || dom.content).to_i }, |
156 'Float' => lambda { | _, dom | (dom.attributes[:v] || dom.content).to_f }, |
157 "Array" => lambda { | _, dom | |
158 obj = [ ] |
159 _.map_dom_id_to_obj! dom, obj |
160 dom.each_element do | elem | |
161 obj << _.decode_dom(elem) |
162 end |
163 obj |
164 }, |
165 'Hash' => lambda { | _, dom | |
166 obj = { } |
167 _.map_dom_id_to_obj! dom, obj |
168 key = nil |
169 dom.each_element do | val | |
170 if key |
171 obj[_.decode_dom(key)] = _.decode_dom(val) |
172 key = nil |
173 else |
174 key = val |
175 end |
176 end |
177 obj |
178 }, |
179 'Object' => lambda { | _, dom | |
180 cls_name = dom.name |
181 # $stderr.puts "cls_name = #{cls_name.inspect}" |
182 cls = _.tag_cls(cls_name) |
183 obj = cls.allocate |
184 _.map_dom_id_to_obj! dom, obj |
185 dom.each_element do | child | |
186 key = child.name |
187 val = _.decode_dom child.first |
188 obj.instance_variable_set("@#{key}", val) |
189 end |
190 obj |
191 }, |
192 } |
193 DECODER['Fixnum'] = DECODER['Bignum'] = DECODER['Integer'] |
194 |
195 def map_dom_id_to_obj! dom, obj |
196 dom_id = dom.attributes[:id] |
197 debugger unless dom_id |
198 raise Error, "no :id attribute in #{dom}" unless dom_id |
199 if (other_obj = @dom_id_map[dom_id]) and other_obj.object_id != obj.object_id |
200 raise Error, "BUG: :id #{dom_id} already used for #{other_obj.class.name} #{other_obj.inspect}" |
201 end |
202 @dom_id_map[dom_id] = obj |
203 end |
204 |
205 include ObjectResolving |
206 def tag_cls cls_name |
207 @cls_tag_map[cls_name.freeze] ||= resolve_object(cls_name.gsub('.', '::')) |
208 end |
209 |
210 end |
211 end |
212 end |
Generated on Fri Jan 27 17:37:46 -0600 2012 with rcov 0.9.8