roundtrip_xml 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c1d89e8b67e0e4e6d17b36a6f7bd669f848978a0
4
+ data.tar.gz: 3c9dabb9fad98650def6f2befaab68adfd56fccf
5
+ SHA512:
6
+ metadata.gz: 53589a94506c4e41bc5bcb8f478929c003aec8f92f4040c4f871524a5daeeaa6b702ffaa167041d8ea4cf42b057c07f355031e88ead55a63e203f71b9e9fa816
7
+ data.tar.gz: 80edb85109be632bd9d1ad6b3003ab7931c679b04f7b489518677d48228e91a39664e0aa3cafe3696c0f5e737d4871f031aa2e02603b893d8aa02a46486b95cf
@@ -0,0 +1,82 @@
1
+ require 'cleanroom'
2
+ # Generates cleanroom methods corresponding to all the xml_accessors and plain_accessors of @el
3
+ class BaseCleanroom
4
+ include Cleanroom
5
+ def get_el
6
+ @el
7
+ end
8
+
9
+ def initialize(el, runtime)
10
+ @runtime = runtime
11
+ @el = el
12
+ get_el.attributes.each do |attr|
13
+ method_name = attr.accessor.to_sym
14
+ create_method(method_name) do |name = nil, &block|
15
+ if !block.nil?
16
+ clazz = name ? @runtime.fetch(name) : attr.sought_type
17
+ value = expand(clazz, &block)
18
+ elsif name
19
+ value = name
20
+ else
21
+ return get_el.send(attr.accessor.to_sym)
22
+ end
23
+
24
+ if attr.array?
25
+ array_attr = get_el.send(attr.accessor.to_sym)
26
+ array_attr ||= attr.default
27
+ array_attr << value
28
+ get_el.send(attr.setter.to_sym, array_attr)
29
+ else
30
+ get_el.send(attr.setter.to_sym, value) unless get_el.send(attr.accessor.to_sym)
31
+ end
32
+ end
33
+ self.class.send(:expose, method_name)
34
+ end
35
+
36
+ expose_attr_accessors
37
+ end
38
+
39
+ def expose_attr_accessors()
40
+ get_el.class.plain_accessors.each do |a|
41
+ create_method(a) do |value = nil|
42
+ return get_el.send(a) unless value
43
+ get_el.send("#{a}=".to_sym, value) if value
44
+ end
45
+ self.class.send(:expose, a)
46
+ end
47
+ end
48
+ def create_method(name, &block)
49
+ self.class.send(:define_method, name, &block)
50
+ end
51
+
52
+ def expand(clazz, &block)
53
+ plain_accessors = @el.class.plain_accessors
54
+ hash = {}
55
+ @value_holder ||= {}
56
+ if plain_accessors != 0
57
+ hash = plain_accessors.inject({}) {|h, a| h[a] = @el.send(a); h}
58
+ end
59
+ child = @runtime.create_cleanroom(clazz)
60
+ child.inherit_properties @value_holder.merge(hash)
61
+ # child.inherit_properties(@args, @value_holder) if instance_variable_defined?(:@args)
62
+ child.evaluate &block
63
+ child.evaluate &child.get_el.process if child.get_el.respond_to? :process
64
+ # evaluate the ROXML object's proc, which further modifies the element
65
+ # child.evaluate &child.get_el.class.proc if child.respond_to? :proc
66
+ child.get_el
67
+ end
68
+
69
+ def inherit_properties(props)
70
+ @value_holder = props
71
+ props.each do |name, val|
72
+ # @value_holder.class.send(:attr_accessor, arg)
73
+ # @value_holder.send("#{arg}=".to_sym, value_holder.send(arg))
74
+ # create_method(arg) do
75
+ # @value_holder.send(arg)
76
+ # end
77
+ self.class.send(:attr_reader, name)
78
+ self.instance_variable_set("@#{name}".to_sym, val)
79
+ self.class.send(:expose, name)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,38 @@
1
+ require 'nokogiri'
2
+
3
+ class DslBuilder
4
+ def initialize(xmlStr, runtime, root_class)
5
+ @doc = Nokogiri::XML(xmlStr)
6
+ @runtime = runtime
7
+ @root_class = root_class
8
+ end
9
+
10
+ def to_dsl
11
+ write_attrs @runtime.fetch(@root_class), @doc.root
12
+ end
13
+
14
+ def write_attrs(clazz, xml, inset='')
15
+ clazz.roxml_attrs.inject('') do |out, attr|
16
+ accessor = attr.accessor
17
+ selector = attr.name
18
+
19
+ if attr.sought_type == :text
20
+ out += inset + "#{accessor} '#{xml.xpath(selector)[0].content}'\n"
21
+ elsif attr.sought_type == :attr
22
+ out += inset + "#{accessor} '#{xml.xpath("@#{selector}")[0].content}'\n"
23
+ elsif !attr.array?
24
+ out += inset + "#{accessor} do\n#{write_attrs attr.sought_type, xml.xpath(selector)[0], inset + ' '}#{inset}end\n"
25
+
26
+ else
27
+ xml.xpath(selector).each do |node|
28
+ out += inset + "#{accessor} do\n#{write_attrs attr.sought_type, node, inset + ' '}#{inset}end\n"
29
+ if inset == ''
30
+ out += "\n"
31
+ end
32
+ end
33
+ end
34
+
35
+ out
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,53 @@
1
+ require 'roxml'
2
+ require 'nokogiri'
3
+ require './lib/roundtrip/roxml_builder'
4
+ require './lib/roundtrip/root_cleanroom'
5
+ require './lib/roundtrip/base_cleanroom'
6
+ # Class which evaluates DSL and read XML files to populate the namespace with classes
7
+ class DslRuntime
8
+ def initialize()
9
+ @classes = {}
10
+ end
11
+ def populate(files)
12
+ files.each {|f| populate_from_file f }
13
+ end
14
+
15
+ def populate_from_file (file)
16
+ populate_raw File.read(file)
17
+
18
+ end
19
+
20
+ def populate_raw (raw)
21
+ builder = RoxmlBuilder.new (Nokogiri::XML(raw).root), @classes
22
+ new_classes = builder.build_classes
23
+ @classes.merge! new_classes
24
+ end
25
+
26
+
27
+ def fetch(class_name)
28
+ @classes[class_name]
29
+ end
30
+
31
+ def add_class(name, clazz)
32
+ @classes[name] = clazz
33
+ end
34
+
35
+ def evaluate_file(path, root_class)
36
+ cleanroom = RootCleanroom.new(fetch(root_class).new, self)
37
+ cleanroom.evaluate_file path
38
+
39
+ end
40
+
41
+ def evaluate_raw(dsl, root_class)
42
+ cleanroom = RootCleanroom.new(fetch(root_class).new, self)
43
+ cleanroom.evaluate dsl
44
+
45
+ end
46
+
47
+ def fetch_cleanroom(root_class)
48
+ BaseCleanroom.new(fetch(root_class).new, self)
49
+ end
50
+ def create_cleanroom(root_class)
51
+ BaseCleanroom.new(root_class.new, self)
52
+ end
53
+ end
@@ -0,0 +1,17 @@
1
+ module PlainAccessors
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def plain_accessor(name)
8
+ @plain_accessors ||= []
9
+ @plain_accessors << name
10
+ attr_accessor name
11
+ end
12
+
13
+ def plain_accessors
14
+ @plain_accessors || []
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ require './lib/roundtrip/base_cleanroom'
2
+ # addes the `define` and `use_file` method to a cleanroom. Only used when evaluating a root file
3
+ class RootCleanroom < BaseCleanroom
4
+
5
+ def define(name, parent, *params, &block)
6
+ new_class = Class.new(@runtime.fetch parent) do
7
+ # simply using @@params is referring to `AContainer`
8
+ self.class_variable_set(:@@block, block)
9
+ params.each do |param|
10
+ plain_accessor param
11
+ end
12
+
13
+ def process
14
+ self.class.class_variable_get(:@@block)
15
+ end
16
+ end
17
+ @runtime.add_class name, new_class
18
+ end
19
+ expose(:define)
20
+
21
+ def use_file(path)
22
+ self.evaluate_file(path)
23
+ end
24
+ expose(:use_file)
25
+ end
@@ -0,0 +1,88 @@
1
+ # require 'roxml'
2
+ require 'nokogiri'
3
+ require './lib/roundtrip/plain_accessors'
4
+ # Builds dynamic classes based on an XML file.
5
+ # Classes that already exist in the DslRuntime instance are modified if necessary, not overridden.
6
+ class RoxmlBuilder
7
+ def initialize (root, current_classes = {})
8
+ # @type Nokogiri::Element
9
+ @root = root
10
+
11
+ # @type Map<Symbol, ROXML>
12
+ @generated_classes = current_classes
13
+
14
+ @root_class = @generated_classes[name_to_sym(root.name)] || Class.new do
15
+ include ROXML
16
+ include PlainAccessors
17
+ xml_convention :dasherize
18
+ xml_name root.name
19
+ def attributes
20
+ self.class.roxml_attrs
21
+ end
22
+ end
23
+
24
+ @generated_classes[ name_to_sym(@root.name)] = @root_class
25
+ end
26
+ def build_classes
27
+ # @root_class.xml_name (@root.name)
28
+ @root.xpath("//#{@root.name}/*|//#{@root.name}/@*").each do |child|
29
+ default_opts = {from:child.name}
30
+ if is_leaf_element?(child)
31
+ add_accessor name_to_sym(child.name, true), default_opts, @root
32
+ elsif child.type == Nokogiri::XML::Node::ATTRIBUTE_NODE
33
+ add_accessor name_to_sym(child.name, true), {from: "@#{child.name}"}, @root
34
+ else
35
+ builder = RoxmlBuilder.new child, @generated_classes
36
+ new_classes = builder.build_classes
37
+ child_name = name_to_sym(child.name, true)
38
+ child_class_name = name_to_sym child.name
39
+ add_accessor child_name, default_opts.merge({as: new_classes[child_class_name]}), @root
40
+ @generated_classes.merge!(new_classes)
41
+ end
42
+ end
43
+
44
+ @generated_classes
45
+ end
46
+
47
+ def name_to_sym(name, lower_case = false)
48
+ # name.gsub('-', '_').to_sym
49
+ new_name = name.split('-').collect(&:capitalize).join
50
+ new_name[0] = new_name[0].downcase! if lower_case
51
+ new_name.to_sym
52
+ end
53
+
54
+
55
+ def add_accessor(name, opts = {}, node = nil)
56
+ attrs = @root_class.roxml_attrs
57
+ attr = attrs.find do |a|
58
+ a.accessor.to_sym == name
59
+ end
60
+ # if class already has xml attribute, delete the old version and add the new version
61
+
62
+ if attr && node
63
+ if node.xpath("./#{attr.name}").size > 1
64
+ @root_class.instance_variable_set(:@roxml_attrs, attrs.select {|i| i != attr })
65
+ new_attr_type = opts[:as]
66
+ # add a new attribute with the array type.
67
+ @root_class.xml_accessor name, opts.merge({as: [new_attr_type]})
68
+ end
69
+ # attr_type = attr.sought_type
70
+ # new_attr_type = opts[:as]
71
+ # if new_attr_type && attr_type != :text && new_attr_type.tag_name == attr_type.tag_name
72
+ # # remove `attr` from the class's attributes
73
+ # @root_class.instance_variable_set(:@roxml_attrs, attrs.select {|i| i != attr })
74
+ # # add a new attribute with the array type.
75
+ # @root_class.xml_accessor name, opts.merge({as: [new_attr_type]})
76
+ # end
77
+ else
78
+ @root_class.xml_accessor name, opts
79
+ end
80
+ end
81
+
82
+ # element is a leaf it has text content and no attributes
83
+ def is_leaf_element?(element)
84
+ element.type == Nokogiri::XML::Node::ELEMENT_NODE &&
85
+ element.attributes.size == 0 &&
86
+ element.children.select {|c| c.type == Nokogiri::XML::Node::ELEMENT_NODE}.empty?
87
+ end
88
+ end
@@ -0,0 +1,7 @@
1
+ require './lib/roundtrip/dsl_runtime'
2
+ require './lib/roundtrip/dsl_builder'
3
+ require './lib/roundtrip/base_cleanroom'
4
+ require './lib/roundtrip/root_cleanroom'
5
+ require './lib/roundtrip/roxml_builder'
6
+ require './lib/roundtrip/plain_accessors'
7
+ puts DslRuntime.new
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: roundtrip_xml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Usick
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: roxml
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: cleanroom
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri-diff
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.2'
69
+ description: A DSL that reads existing XML, generating an internal schema and using
70
+ it to create a (somewhat) type safe DSL
71
+ email: chris.usick@northfieldit.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/roundtrip/base_cleanroom.rb
77
+ - lib/roundtrip/dsl_builder.rb
78
+ - lib/roundtrip/dsl_runtime.rb
79
+ - lib/roundtrip/plain_accessors.rb
80
+ - lib/roundtrip/root_cleanroom.rb
81
+ - lib/roundtrip/roxml_builder.rb
82
+ - lib/roundtrip_xml.rb
83
+ homepage: https://github.com/chrisUsick/roundtrip-xml
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.6
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: DSL which learns from XML
107
+ test_files: []