roundtrip_xml 0.1.6 → 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.
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