roundtrip_xml 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []