roundtrip_xml 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f0012fb7fe73d2b380d6ccb789ce81e220a5cb5
4
- data.tar.gz: b851118b3e91dd5644c77f95545621e65a98caf7
3
+ metadata.gz: 8be8e12666739bebd81cc31be68c2707004172cb
4
+ data.tar.gz: 89c888102932ba9719a7b57a89ba58ca5c736c1d
5
5
  SHA512:
6
- metadata.gz: d70527738c6a247d6769eca5e1b9135310b3eb1228dca18753a8d7cd9ffb51d2be3801a124431e4bd907699cd8532eb44997094d92f98c2209782fb73a211210
7
- data.tar.gz: 77733f8c0ca731e0d1350f78fc12ca3244ae01910e63260a4a07b1fa546cc02ccbf3e72136023c6c6348f5fd4cc3cf513b5bd848151b92ad79229089ebf8e97e
6
+ metadata.gz: a95e401af921c46d3d7a36d8248a776230729a6b81630890c6496062e1d05431f916d89f9895e017f3abf0adb9db1496daf814c99ac919403468ab167d6afb71
7
+ data.tar.gz: fd001af3052e83f90617fee528178bd6065f1b43cbafbf9716fc24e6cf3eb358c5ae57f368a249dc94b968a18ffdbd338cfeb7decb9697454110379e158d4843
@@ -1,4 +1,5 @@
1
1
  require 'cleanroom'
2
+ require 'roundtrip_xml/utils'
2
3
  # Generates cleanroom methods corresponding to all the xml_accessors and plain_accessors of @el
3
4
  class BaseCleanroom
4
5
  include Cleanroom
@@ -6,7 +7,9 @@ class BaseCleanroom
6
7
  @el
7
8
  end
8
9
 
9
- def initialize(el, runtime)
10
+ attr_reader :show_undefined_params
11
+ def initialize(el, runtime, show_undefined_params = false)
12
+ @show_undefined_params = show_undefined_params
10
13
  @runtime = runtime
11
14
  @el = el
12
15
  get_el.attributes.each do |attr|
@@ -39,8 +42,13 @@ class BaseCleanroom
39
42
  def expose_attr_accessors()
40
43
  get_el.class.plain_accessors.each do |a|
41
44
  create_method(a) do |value = nil|
42
- return get_el.send(a) unless value
43
- get_el.send("#{a}=".to_sym, value) if value
45
+ if value
46
+ get_el.send("#{a}=".to_sym, value) if value
47
+ elsif !value && show_undefined_params
48
+ return get_el.send(a) || Utils::UndefinedParam.new(a)
49
+ else
50
+ return get_el.send(a)
51
+ end
44
52
  end
45
53
  self.class.send(:expose, a)
46
54
  end
@@ -51,10 +59,9 @@ class BaseCleanroom
51
59
 
52
60
  def expand(clazz, &block)
53
61
  plain_accessors = @el.class.plain_accessors
54
- hash = {}
55
62
  @value_holder ||= {}
56
- hash = plain_accessors.inject({}) {|h, a| h[a] = @el.send(a); h}
57
- child = @runtime.create_cleanroom(clazz)
63
+ hash = plain_accessors.inject({}) {|h, a| h[a] = @el.send(a) || Utils::UndefinedParam.new(a); h}
64
+ child = @runtime.create_cleanroom(clazz, @show_undefined_params)
58
65
  child.inherit_properties hash.merge(@value_holder)
59
66
 
60
67
  child.evaluate &block
@@ -15,34 +15,54 @@ class DslRuntime
15
15
  @classes = {}
16
16
  @root_classes = Set.new
17
17
  end
18
- def populate(files, root_method=nil)
19
- files.map {|f| populate_from_file f, root_method }
18
+ def populate(files)
19
+ files.map {|f| populate_from_file f }
20
20
  end
21
21
 
22
- def populate_from_file (file, root_method=nil)
23
- populate_raw File.read(file), root_method
22
+ def populate_from_file (file)
23
+ populate_raw File.read(file)
24
24
 
25
25
  end
26
26
 
27
27
  ##
28
28
  # @param root_method Symbol The method of the ROXML object to access its children
29
- def populate_raw (raw, root_method = nil)
29
+ def populate_raw (raw)
30
30
  builder = RoxmlBuilder.new (Nokogiri::XML(raw).root), @classes
31
31
  new_classes = builder.build_classes
32
32
  @root_classes << builder.root_class_name
33
33
  @classes.merge! new_classes
34
- if root_method
35
- roxml_root = fetch(builder.root_class_name).from_xml raw
34
+ end
36
35
 
37
- extractor = Extractor.new roxml_root.send(root_method), self
36
+ # @param block Proc This proc is passed each health rule and must return a string value which specifies the partition for it
37
+ def write_dsl(xml, root_class_name, root_method, helpers = '', &block)
38
+ roxml_root = fetch(root_class_name).from_xml xml
38
39
 
39
- new_objs = extractor.convert_roxml_objs
40
- subclasses = extractor.subclasses
41
- roxml_root.send("#{root_method}=", new_objs)
42
- builder = SexpDslBuilder.new [roxml_root], subclasses, self
40
+ extractor = Extractor.new roxml_root.send(root_method), self, root_class_name, helpers
43
41
 
44
- builder.write_full_dsl
42
+ new_objs = extractor.convert_roxml_objs
43
+ if block_given?
44
+ partitions = {}
45
+ new_objs.each do |obj|
46
+ partition = yield obj
47
+ partitions[partition] ||= []
48
+ partitions[partition] << obj
49
+ end
50
+ partitions
51
+ partitions.inject({}) do |out, (partition, objs)|
52
+ out[partition] = write_dsl_helper roxml_root, root_method, objs
53
+ out
54
+ end
55
+ else
56
+ write_dsl_helper roxml_root, root_method, new_objs
45
57
  end
58
+
59
+ end
60
+
61
+ def write_dsl_helper(root, root_method, objs)
62
+ root.send("#{root_method}=", objs)
63
+ builder = SexpDslBuilder.new root, self
64
+
65
+ builder.write_full_dsl root_method
46
66
  end
47
67
 
48
68
  def fetch(class_name)
@@ -73,8 +93,8 @@ class DslRuntime
73
93
  BaseCleanroom.new(fetch(root_class).new, self)
74
94
  end
75
95
 
76
- def create_cleanroom(root_class)
77
- BaseCleanroom.new(root_class.new, self)
96
+ def create_cleanroom(root_class, show_undefined_params = false)
97
+ BaseCleanroom.new(root_class.new, self, show_undefined_params)
78
98
  end
79
99
 
80
100
  def marshal_dump
@@ -8,80 +8,121 @@ require 'multiset'
8
8
  require 'roundtrip_xml/roxml_subclass_helpers.rb'
9
9
 
10
10
  class Extractor
11
- include Utils
12
- attr_reader :subclasses
13
- def initialize(roxml_objs, runtime)
11
+
12
+ def initialize(roxml_objs, runtime, root_class, definitions = nil, &block)
14
13
  @roxml_objs = roxml_objs
15
- @hashes = @roxml_objs.map { |obj| obj.to_hash }
16
14
  @runtime = runtime
17
- @subclass_names = Multiset.new
18
- @subclasses = []
15
+ @root_class = root_class
16
+ if block_given?
17
+ @definitions = eval_definitions root_class, &block
18
+ else
19
+ @definitions = eval_definitions root_class, definitions
20
+ end
19
21
  end
20
- def list_diffs
21
22
 
22
- HashDiff.best_diff(@hashes[0], @hashes[6]).sort_by do |diff|
23
- m = diff[1].match(/\./)
24
- m ? m.size : 0
25
- end.map {|diff| diff[1]}
23
+ def eval_definitions(root_class, str = nil, &block)
24
+ old_names = @runtime.instance_variable_get(:@classes).keys
25
+ if block_given?
26
+ @runtime.evaluate_raw '', root_class, &block
27
+ else
28
+ @runtime.evaluate_raw str, root_class
29
+ end
30
+ new_names = @runtime.instance_variable_get(:@classes).keys
31
+ def_names = new_names - old_names
32
+ def_names.inject({}) do |out, name|
33
+ clazz = @runtime.fetch(name)
34
+ parent = get_root_class clazz
35
+ obj = clazz.new
36
+ cleanroom = @runtime.create_cleanroom clazz, true
37
+ cleanroom.get_el.process.each { |p| cleanroom.evaluate &p }
38
+ out[parent] ||= []
39
+ out[parent] << cleanroom.get_el
40
+ out
41
+ end
26
42
  end
27
43
 
28
- def similar_fields
29
- keys = Set.new @hashes[0].keys
30
- diff_keys = Set.new
31
- compared = {}
32
- @hashes.each do |hash_1|
33
- @hashes.each do |hash_2|
34
- compared[hash_2] ||= Set.new
35
- next if hash_1 == hash_2 || compared[hash_1].include?(hash_2)
36
- compared[hash_2].add hash_1
37
-
38
- diffs = HashDiff.diff(hash_1, hash_2).map do |diff|
39
- diff[1].match(/(\w+)\.?/)[1]
40
- end
44
+ def get_root_class(clazz)
45
+ super_clazz = @runtime.fetch clazz.superclass.class_name
46
+ if super_clazz.subclass?
47
+ get_root_class super_clazz
48
+ else
49
+ super_clazz.class_name
50
+ end
51
+ end
41
52
 
42
- diff_keys.merge diffs
43
- end
53
+ def diff_definition(defin, obj)
54
+ def_hash = defin.to_hash
55
+ obj_hash = obj.to_hash
56
+ [def_hash, obj_hash].each do |h|
57
+ h.delete '__class'
44
58
  end
45
- keys = keys - diff_keys
46
- key_hash = keys.inject({}) do |hash, key|
47
- hash[key] = @hashes[0][key]
48
- hash
59
+ diffs = HashDiff.diff(obj_hash, def_hash).map do |diff|
60
+ ROXMLDiff.new diff[1], from_hash(diff[2]), diff[3]
49
61
  end
50
- [key_hash, diff_keys]
62
+
63
+ diffs
51
64
  end
52
65
 
53
- def create_romxl_class(keys, parent)
54
- @subclass_names << parent
55
- name = "#{parent.to_s}#{@subclass_names.count(parent)}"
56
- clazz = new_roxml_class name, @runtime.fetch(parent) do
57
- include RoxmlSubclassHelpers
58
- end
59
- clazz.defaults = keys
60
- @runtime.add_class name, clazz
61
- @subclasses << clazz
62
- name
66
+ def from_hash(hash)
67
+ return hash unless hash.is_a? Hash
68
+ @runtime.fetch(hash['__class']).from_hash @runtime, hash
69
+
63
70
  end
64
71
 
65
- def convert_roxml(obj, keys, class_name)
66
- parent = @runtime.fetch(class_name)
67
- new = parent.new
68
- parent.plain_accessors.each do |new_accessor|
69
- old_accessor = new_accessor.to_s.gsub(VAR_SUFIX, '').to_sym
70
- new.send "#{new_accessor}=", obj.send(old_accessor)
72
+ def convert_roxml_objs
73
+ def_names = @definitions.keys
74
+ @roxml_objs.map do |obj|
75
+ convert_roxml_obj obj
71
76
  end
77
+ end
78
+
79
+ def convert_roxml_obj (obj)
80
+ name = obj.class.class_name
81
+ defs = @definitions[name]
82
+
83
+ defs.each do |defin|
84
+ diffs = diff_definition(defin, obj)
85
+ if diffs.all? {|diff| diff.template_val.is_a?(Utils::UndefinedParam) || diff.template_val == nil }
86
+ new_obj = defin.class.new
87
+ param_values = diffs.inject({}) do |values, diff|
88
+ name = diff.template_val ? diff.template_val.name : diff.key
89
+ values[name] = diff.obj_val
90
+ values
91
+ end
92
+ set_attributes new_obj, param_values
93
+ # return convert_roxml_obj new_obj
94
+ obj = new_obj
95
+ end
96
+ end if defs
72
97
 
73
- keys.each do |accessor|
74
- new.send "#{accessor}=", obj.send(accessor)
98
+ obj.class.roxml_attrs.each do |a|
99
+ if a.array?
100
+ elements = obj.send(a.accessor).map {|el| convert_roxml_obj el}
101
+ obj.send a.setter.to_sym, elements
102
+ elsif a.sought_type.class == Class
103
+ current_value = obj.send(a.accessor)
104
+ obj.send a.setter.to_sym, convert_roxml_obj(current_value) if current_value
105
+ end
75
106
  end
76
- new
107
+ obj
77
108
  end
78
109
 
79
- def convert_roxml_objs
80
- fields, diff_keys = similar_fields
81
- parent_name = @roxml_objs[0].class.class_name
82
- name = create_romxl_class fields, parent_name
83
- @roxml_objs.map do |roxml|
84
- convert_roxml roxml, diff_keys, name
110
+ def set_attributes(obj, params)
111
+ params.each do |param, val|
112
+ methods = param.to_s.split '.'
113
+ # set_deep_attribute obj, methods, val
114
+ obj.send "#{param}=", val
85
115
  end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ class ROXMLDiff
122
+ attr_reader :key, :obj_val, :template_val
123
+ def initialize(key, obj_val, template_val)
124
+ @key = key.to_sym
125
+ @obj_val = obj_val
126
+ @template_val = template_val
86
127
  end
87
128
  end
@@ -0,0 +1,88 @@
1
+ require 'roxml'
2
+ require 'nokogiri'
3
+ require 'roundtrip_xml/utils'
4
+ require 'hashdiff'
5
+ require 'set'
6
+ require 'roundtrip_xml/utils'
7
+ require 'multiset'
8
+ require 'roundtrip_xml/roxml_subclass_helpers.rb'
9
+
10
+ # extracts duplicate attributes from ROXML objects
11
+ class Extractor2
12
+ include Utils
13
+ attr_reader :subclasses
14
+ def initialize(roxml_objs, runtime)
15
+ @roxml_objs = roxml_objs
16
+ @hashes = @roxml_objs.map { |obj| obj.to_hash }
17
+ @runtime = runtime
18
+ @subclass_names = Multiset.new
19
+ @subclasses = []
20
+ end
21
+ def list_diffs
22
+
23
+ HashDiff.best_diff(@hashes[0], @hashes[6]).sort_by do |diff|
24
+ m = diff[1].match(/\./)
25
+ m ? m.size : 0
26
+ end.map {|diff| diff[1]}
27
+ end
28
+
29
+ def similar_fields
30
+ keys = Set.new @hashes[0].keys
31
+ diff_keys = Set.new
32
+ compared = {}
33
+ @hashes.each do |hash_1|
34
+ @hashes.each do |hash_2|
35
+ compared[hash_2] ||= Set.new
36
+ next if hash_1 == hash_2 || compared[hash_1].include?(hash_2)
37
+ compared[hash_2].add hash_1
38
+
39
+ diffs = HashDiff.diff(hash_1, hash_2).map do |diff|
40
+ diff[1].match(/(\w+)\.?/)[1]
41
+ end
42
+
43
+ diff_keys.merge diffs
44
+ end
45
+ end
46
+ keys = keys - diff_keys
47
+ key_hash = keys.inject({}) do |hash, key|
48
+ hash[key] = @hashes[0][key]
49
+ hash
50
+ end
51
+ [key_hash, diff_keys]
52
+ end
53
+
54
+ def create_romxl_class(keys, parent)
55
+ @subclass_names << parent
56
+ name = "#{parent.to_s}#{@subclass_names.count(parent)}"
57
+ clazz = new_roxml_class name, @runtime.fetch(parent) do
58
+ include RoxmlSubclassHelpers
59
+ end
60
+ clazz.defaults = keys
61
+ @runtime.add_class name, clazz
62
+ @subclasses << clazz
63
+ name
64
+ end
65
+
66
+ def convert_roxml(obj, keys, class_name)
67
+ parent = @runtime.fetch(class_name)
68
+ new = parent.new
69
+ parent.plain_accessors.each do |new_accessor|
70
+ old_accessor = new_accessor.to_s.gsub(VAR_SUFIX, '').to_sym
71
+ new.send "#{new_accessor}=", obj.send(old_accessor)
72
+ end
73
+
74
+ keys.each do |accessor|
75
+ new.send "#{accessor}=", obj.send(accessor)
76
+ end
77
+ new
78
+ end
79
+
80
+ def convert_roxml_objs
81
+ fields, diff_keys = similar_fields
82
+ parent_name = @roxml_objs[0].class.class_name
83
+ name = create_romxl_class fields, parent_name
84
+ @roxml_objs.map do |roxml|
85
+ convert_roxml roxml, diff_keys, name
86
+ end
87
+ end
88
+ end
@@ -1,3 +1,4 @@
1
+ require 'roundtrip_xml/utils'
1
2
  module PlainAccessors
2
3
  def self.included(base)
3
4
  base.extend ClassMethods
@@ -9,7 +10,10 @@ module PlainAccessors
9
10
  @plain_accessors[name] = default
10
11
  attr_writer name
11
12
  define_method(name) do
12
- instance_variable_get "@#{name}" || @plain_accessors[name]
13
+ val = instance_variable_get "@#{name}"
14
+ val
15
+ # @plain_accessors is actually nil here
16
+ # val.nil? ? @plain_accessors[name] : val
13
17
  end
14
18
  end
15
19
 
@@ -10,6 +10,7 @@ class RootCleanroom < BaseCleanroom
10
10
  # simply using @@params is referring to `AContainer`
11
11
  # hash = self.class_variable_defined?(:@@block) ? self.class_variable_get(:@@block) : {}
12
12
  # hash[name] = block
13
+ self.instance_variable_set :@is_subclass, true
13
14
  self.instance_variable_set(:@block, block)
14
15
  params.each do |param|
15
16
  plain_accessor param
@@ -34,8 +35,8 @@ class RootCleanroom < BaseCleanroom
34
35
  else
35
36
  [proc]
36
37
  end
37
-
38
38
  end
39
+
39
40
  end
40
41
  @runtime.add_class name, new_class
41
42
  end
@@ -30,48 +30,36 @@ EOF
30
30
  s
31
31
  end
32
32
 
33
- attr_accessor :roxml_objs, :subclasses, :runtime
34
- def initialize(roxml_objs, subclasses, runtime)
35
- self.roxml_objs = roxml_objs
36
- self.subclasses = subclasses
33
+ attr_accessor :roxml_objs, :runtime
34
+ def initialize(roxml_obj, runtime)
35
+ @roxml_obj = roxml_obj
37
36
  self.runtime = runtime
38
37
  end
39
38
 
40
- def write_def_classes
41
- arr = []
42
- subclasses.each do |clazz|
43
- defaults = clazz.defaults.map do |accessor, default|
44
- [:call, nil, accessor, [:str, default]]
45
- end
46
- arr = [:iter,
47
- [:call, nil, :define,
48
- [:lit, clazz.class_name],
49
- [:lit, clazz.superclass.class_name]
50
- ], 0, [:block, *defaults]]
51
- end
52
-
53
- sexp = Sexp.from_array arr
54
- processor = Ruby2Ruby.new
55
- processor.process(sexp)
56
- end
57
-
58
39
  def create_sexp_for_roxml_obj(obj, root_method = nil)
59
- is_subclass = obj.class.respond_to?(:defaults)
60
- subclass_value = obj.class.respond_to?(:defaults) ? [:lit, obj.class.class_name] : nil
40
+ is_subclass = obj.class.subclass?
41
+ subclass_value = is_subclass ? [:lit, obj.class.class_name] : nil
61
42
  accessors = []
62
43
  obj.attributes.each do |attr|
63
44
  val = obj.send attr.accessor
64
- if !val || (is_subclass && obj.class.defaults.keys.include?(attr.accessor))
65
- next
66
- end
45
+ next unless val
46
+ # if !val || (is_subclass && obj.class.defaults.keys.include?(attr.accessor))
47
+ # next
48
+ # end
67
49
  if attr.sought_type.class == Symbol
68
- accessors << [:call, nil, attr.accessor, [:str, val || '']]
50
+ accessors << [:call, nil, attr.accessor, [:str, val]]
69
51
  elsif val.class == Array
70
52
  val.each { |v| accessors << create_sexp_for_roxml_obj(v, attr.accessor) }
71
53
  else
72
54
  accessors << create_sexp_for_roxml_obj(val, attr.accessor) if val
73
55
  end
74
56
  end.compact
57
+ # plain accessors
58
+ obj.class.plain_accessors.each do |a|
59
+ val = obj.send a
60
+ next unless val
61
+ accessors << [:call, nil, a, [:str, val]]
62
+ end
75
63
  root_call = [:call, nil, root_method]
76
64
  root_call << subclass_value if subclass_value
77
65
  if root_method
@@ -83,28 +71,18 @@ EOF
83
71
  [:block,
84
72
  *accessors]
85
73
  end
86
-
87
-
88
74
  end
89
75
 
90
- def write_roxml_obj(obj, root_method)
91
- s = create_sexp_for_roxml_obj obj, root_method
76
+ def write_roxml_obj(obj)
77
+ s = create_sexp_for_roxml_obj obj
92
78
 
93
79
  sexp = Sexp.from_array s
94
80
  processor = Ruby2Ruby.new
95
- processor.process(sexp).gsub "\"", "'"
96
- end
97
-
98
- def write_roxml_objs(root_method = nil)
99
- roxml_objs.inject('') do |out, obj|
100
- out += write_roxml_obj obj, root_method
101
- out += "\n\n"
102
- out
103
- end
81
+ processor.process(sexp).gsub("\"", "'").gsub(/[\(\)]/, ' ')
104
82
  end
105
83
 
106
- def write_full_dsl()
107
- write_def_classes + "\n\n" + write_roxml_objs
84
+ def write_full_dsl(root_method)
85
+ write_roxml_obj @roxml_obj
108
86
  end
109
87
 
110
88
  end
@@ -7,6 +7,13 @@ def name_to_sym_helper(name, lower_case = false)
7
7
  new_name.to_sym
8
8
  end
9
9
  module Utils
10
+ # UNDEFINED_PARAM = 'UNDEFINED_PARAM_VALUE'
11
+ class UndefinedParam
12
+ attr_reader :name
13
+ def initialize(name)
14
+ @name = name
15
+ end
16
+ end
10
17
 
11
18
  VAR_SUFIX = '_v'.freeze
12
19
  def self.included(base)
@@ -20,12 +27,17 @@ module Utils
20
27
  include PlainAccessors
21
28
  xml_convention :dasherize
22
29
  xml_name name
30
+
31
+ # by default a ROXML class isn't a sub class
32
+ def self.subclass?
33
+ @is_subclass || false
34
+ end
23
35
  def attributes
24
36
  self.class.roxml_attrs
25
37
  end
26
38
 
27
39
  def self.class_name
28
- name_to_sym_helper self.tag_name
40
+ self.tag_name.is_a?(Symbol) ? self.tag_name : name_to_sym_helper(self.tag_name)
29
41
  end
30
42
 
31
43
  def to_hash
@@ -34,9 +46,22 @@ module Utils
34
46
  value = value.to_hash if value.respond_to? :to_hash
35
47
 
36
48
  hash[a.accessor] = value
49
+ hash['__class'] = self.class.class_name
37
50
  hash
38
51
  end
39
52
  end
53
+
54
+ def self.from_hash(runtime, hash)
55
+ hash.delete '__class'
56
+ obj = self.new
57
+ hash.each do |k, val|
58
+ if val.is_a? Hash
59
+ val = runtime.fetch(val['__class']).from_hash(runtime, val)
60
+ end
61
+ obj.send "#{k}=", val
62
+ end
63
+ obj
64
+ end
40
65
  def self.unique_parent_accessors
41
66
  plain = Set.new(plain_accessors.map {|accessor| accessor.to_s.gsub(VAR_SUFIX, '').to_sym})
42
67
  parent_accessors = Set.new(roxml_attrs.map { |attr| attr.accessor })
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roundtrip_xml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Usick
@@ -148,6 +148,7 @@ files:
148
148
  - lib/roundtrip_xml/dsl_builder.rb
149
149
  - lib/roundtrip_xml/dsl_runtime.rb
150
150
  - lib/roundtrip_xml/extractor.rb
151
+ - lib/roundtrip_xml/extractor2.rb
151
152
  - lib/roundtrip_xml/plain_accessors.rb
152
153
  - lib/roundtrip_xml/root_cleanroom.rb
153
154
  - lib/roundtrip_xml/roxml_builder.rb