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 +7 -0
- data/lib/roundtrip/base_cleanroom.rb +82 -0
- data/lib/roundtrip/dsl_builder.rb +38 -0
- data/lib/roundtrip/dsl_runtime.rb +53 -0
- data/lib/roundtrip/plain_accessors.rb +17 -0
- data/lib/roundtrip/root_cleanroom.rb +25 -0
- data/lib/roundtrip/roxml_builder.rb +88 -0
- data/lib/roundtrip_xml.rb +7 -0
- metadata +107 -0
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: []
|