extreml 0.1.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/extreml/type_element.rb +141 -0
- data/lib/extreml.rb +153 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2176ef101bb8536701a8e2c98d674fc57ebb014cdc22dda621ede9cbbc896605
|
4
|
+
data.tar.gz: f282d659eb2c3a53235e856c4ca1675d02a4d2c442bc94a86a43792f3a56f830
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d709f51ba09daf3e226cacd62cb4283da4153f748b7a18e28a2af57d65ed8455eb60b1a77daf7b3c7f5e4ea4af3568f504cb3bfa4ccdcff1ec31f019e18461e2
|
7
|
+
data.tar.gz: d8264e22eb185f33ceb7ae8d294dcaf6cee6c1207e7608feb1d29dd5a52821aec2042a7d5235923020cb9d0470a4aa35bd5b0a11ea6508bc1aa213d7bb2bd9c4
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# MIT License
|
2
|
+
#
|
3
|
+
# Copyright (c) 2020 Fabio Boccacini - fboccacini@gmail.com
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
require 'pp'
|
24
|
+
|
25
|
+
class TypeElement
|
26
|
+
|
27
|
+
def initialize document
|
28
|
+
|
29
|
+
# document model:
|
30
|
+
#
|
31
|
+
# {
|
32
|
+
# name: 'string',
|
33
|
+
# namespace: 'string'|nil,
|
34
|
+
# attributes: [attributes_array]|nil,
|
35
|
+
# content: [mixed_array]|nil
|
36
|
+
# }
|
37
|
+
#
|
38
|
+
# attributes model:
|
39
|
+
#
|
40
|
+
# {
|
41
|
+
# property: 'string',
|
42
|
+
# namespace: 'string'|nil,
|
43
|
+
# value: 'string'
|
44
|
+
# }
|
45
|
+
#
|
46
|
+
# Initialize properties
|
47
|
+
@name = document[:name]
|
48
|
+
@namespace = document[:namespace]
|
49
|
+
@attributes = document[:attributes]
|
50
|
+
@content = document[:content]
|
51
|
+
@types = Array.new
|
52
|
+
|
53
|
+
# Add a type for every element in content
|
54
|
+
unless @content.nil?
|
55
|
+
@content.each do |v|
|
56
|
+
if v.class == Hash
|
57
|
+
__add_type v[:name], v
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# Getters
|
65
|
+
def name
|
66
|
+
@name
|
67
|
+
end
|
68
|
+
alias __name name
|
69
|
+
|
70
|
+
def attributes
|
71
|
+
@attributes
|
72
|
+
end
|
73
|
+
alias __attributes attributes
|
74
|
+
|
75
|
+
def namespace
|
76
|
+
@namespace
|
77
|
+
end
|
78
|
+
alias __namespace namespace
|
79
|
+
|
80
|
+
def content
|
81
|
+
if @content.nil? || @content.length > 1
|
82
|
+
return @content
|
83
|
+
else
|
84
|
+
return @content[0]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
alias __content content
|
88
|
+
|
89
|
+
def types
|
90
|
+
# return self.methods - TypeElement.instance_methods
|
91
|
+
return @types
|
92
|
+
end
|
93
|
+
alias __types types
|
94
|
+
|
95
|
+
# Add a type method, that returns an array if there are more elements with the same tag,
|
96
|
+
# a Type object if it's just one, or the content if it's the last level
|
97
|
+
def add_type name, content
|
98
|
+
|
99
|
+
# If method exists, override it and return an array including all previous elements
|
100
|
+
# Else create a method that returns a new object of the content
|
101
|
+
if self.__types.any? name.to_sym
|
102
|
+
array = self.send name.to_sym
|
103
|
+
define_singleton_method name.to_sym do
|
104
|
+
return [array].flatten + [(content.class == Hash ? (TypeElement.new content) : content)]
|
105
|
+
end
|
106
|
+
else
|
107
|
+
define_singleton_method name.to_sym do
|
108
|
+
if content.class == Hash
|
109
|
+
return TypeElement.new content
|
110
|
+
else
|
111
|
+
return content
|
112
|
+
end
|
113
|
+
end
|
114
|
+
@types << name.to_sym
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
alias __add_type add_type
|
119
|
+
|
120
|
+
# Override to_s to use in strings (useful for the last nesting level)
|
121
|
+
def to_s
|
122
|
+
return self.__content
|
123
|
+
end
|
124
|
+
alias __to_s to_s
|
125
|
+
|
126
|
+
# This method is for debug purposes only, it prints a tree of the document
|
127
|
+
def tree level: 0, attributes: false
|
128
|
+
|
129
|
+
pre = level > 0 ? "#{' ' * level}|#{'-->'}" : ""
|
130
|
+
puts "#{pre}#{self.__namespace}:#{self.__name} #{self.__types.inspect}" + (attributes ? " #{self.__attributes}" : "")
|
131
|
+
level += 1
|
132
|
+
self.__types.each do |m|
|
133
|
+
next_type = self.send(m)
|
134
|
+
[next_type].flatten.each do |nt|
|
135
|
+
(nt.__tree level: level, attributes: attributes) unless next_type.nil?
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
alias __tree tree
|
140
|
+
|
141
|
+
end
|
data/lib/extreml.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# MIT License
|
2
|
+
#
|
3
|
+
# Copyright (c) 2020 Fabio Boccacini - fboccacini@gmail.com
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
require 'extreml/type_element'
|
24
|
+
|
25
|
+
class XmlHeader
|
26
|
+
end
|
27
|
+
|
28
|
+
class Extreml
|
29
|
+
|
30
|
+
def initialize xml_file = nil, warnings: true, xml_header: nil
|
31
|
+
|
32
|
+
# Warnings flag
|
33
|
+
@warnings = warnings
|
34
|
+
@header = xml_header
|
35
|
+
|
36
|
+
if xml_file.nil?
|
37
|
+
raise 'Error: please specify an xml file. Nil was given.'
|
38
|
+
elsif !File.file? xml_file
|
39
|
+
raise "Error: file #{xml_file} not found."
|
40
|
+
else
|
41
|
+
|
42
|
+
# Read file
|
43
|
+
xml = File.read xml_file
|
44
|
+
|
45
|
+
@body = Hash.new
|
46
|
+
|
47
|
+
# Get xml header informations
|
48
|
+
header = xml[/^\<\?xml (.*)\?\>/]
|
49
|
+
|
50
|
+
if header.nil?
|
51
|
+
puts "Warning: #{xml_file}: xml header missing." if @warnings
|
52
|
+
define_singleton_method :header do
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
else
|
56
|
+
h = header.scan /([\w\?\<]*)=["|']([^'"]*)["|']/
|
57
|
+
|
58
|
+
@xml_header = XmlHeader.new
|
59
|
+
h.each do |param|
|
60
|
+
@xml_header.instance_eval do
|
61
|
+
define_singleton_method param[0].to_sym do
|
62
|
+
return param[1]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
define_singleton_method :header do
|
68
|
+
return @xml_header
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# Read document
|
75
|
+
doc = xml.match /(?:\<\?xml .*?(?: ?\?\>))?[\t\n\r\f ]*(.*)/m
|
76
|
+
@document = unpack doc[1]
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
# Expose the entire document
|
81
|
+
def document
|
82
|
+
return TypeElement.new({name: 'document', content: @document})
|
83
|
+
end
|
84
|
+
|
85
|
+
# Print the entire document tree. For debug purposes
|
86
|
+
def tree attributes: false
|
87
|
+
self.document.__tree attributes
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def unpack string
|
93
|
+
|
94
|
+
# Remove comments
|
95
|
+
string = string.gsub(/\<\!--[^>]*--\>/,'').strip
|
96
|
+
|
97
|
+
# Match a tag pair at a time, recurse for nested content
|
98
|
+
string = string.strip
|
99
|
+
tags = string.match /(?<prerest>.*?)?\<(?:(?<namespace>\w*):)?(?<name>[^\/]\w*)(?:[\t]*(?<attributes>[^>]*)?)\>(?<content>.*?)[\t\n\r\f]*\<\/\k<namespace>?:?\k<name>\>(?<rest>.*)/m
|
100
|
+
|
101
|
+
if tags.nil?
|
102
|
+
parts = string.match /(?<prerest>.*?)?\<(?:(?<namespace>\w*):)?(?<name>[^\/]\w*)(?:[\t]*(?<attributes>[^>]*)?)[\t\n\r\f]*\/\>(?<rest>.*)/m
|
103
|
+
|
104
|
+
if parts.nil?
|
105
|
+
return string
|
106
|
+
else
|
107
|
+
attributes = Array.new
|
108
|
+
a = parts[:attributes].scan /[\t\n\r\f ]*(?:(?<namespace>[^:\n\r\f\t]*):)?(?<property>[^=\n\r\f\t ]*)[\t ]*=[\t ]*"(?<value>[^"]*)"[\t\n\r\f]*/m
|
109
|
+
a.each do |p|
|
110
|
+
attributes << {
|
111
|
+
:namespace => p[0],
|
112
|
+
:property => p[1],
|
113
|
+
:value => p[2]
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
pack = Hash[
|
118
|
+
:name => parts[:name],
|
119
|
+
:namespace => parts[:namespace],
|
120
|
+
:attributes => attributes
|
121
|
+
]
|
122
|
+
return [unpack(parts[:prerest].strip), pack, unpack(parts[:rest].strip)].reject{ |p| p == ""}
|
123
|
+
end
|
124
|
+
else
|
125
|
+
|
126
|
+
if tags[:attributes].strip! == ""
|
127
|
+
attributes = nil
|
128
|
+
else
|
129
|
+
attributes = Array.new
|
130
|
+
a = tags[:attributes].scan /[\t\n\r\f ]*(?:(?<namespace>[^:\n\r\f\t]*):)?(?<property>[^=\n\r\f\t ]*)[\t ]*=[\t ]*"(?<value>[^"]*)"[\t\n\r\f]*/m
|
131
|
+
a.each do |p|
|
132
|
+
attributes << {
|
133
|
+
:namespace => p[0],
|
134
|
+
:property => p[1],
|
135
|
+
:value => p[2]
|
136
|
+
}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
if pack.nil?
|
140
|
+
pack = Hash[
|
141
|
+
:name => tags[:name].strip,
|
142
|
+
:namespace => tags[:namespace].nil? ? nil : tags[:namespace].strip,
|
143
|
+
:attributes => attributes,
|
144
|
+
:content => Array.new
|
145
|
+
]
|
146
|
+
end
|
147
|
+
pack[:content] += [unpack(tags[:content].strip)].flatten
|
148
|
+
end
|
149
|
+
return [unpack(tags[:prerest].strip), pack, unpack(tags[:rest].strip)].reject{ |p| p == ""}
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: extreml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fabio Boccacini
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
# Extreml
|
15
|
+
### Ruby XML manipulation gem
|
16
|
+
|
17
|
+
A powerful ruby gem to easily manipulate XML documents, that gives acces to the elements through dynamic methods.
|
18
|
+
|
19
|
+
This library contains another class, TypeElement, which exposes dynamic methods named after its nested tags.
|
20
|
+
|
21
|
+
The document method in of Extreml returns aTypeElement object with a method named after the root tag which returns the first level of nesting represented as TypeElement objects.
|
22
|
+
|
23
|
+
Any subsequent object has methods that reflect its structure, that return recursively an object that has the nested elements as methods.
|
24
|
+
|
25
|
+
If there are more than one element with the same tag, that method will return an array containing all subsequent objects.
|
26
|
+
|
27
|
+
All basic methods of TypeElement have aliases, in order to prevent method overriding in case the document contains tags with similar names, which are called with two leading underscores (es. __types).
|
28
|
+
|
29
|
+
|
30
|
+
Usage:
|
31
|
+
------
|
32
|
+
|
33
|
+
### Basic methods:
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
**Extreml:**
|
38
|
+
|
39
|
+
document: returns the representation of the entire document as a TypeElement object.
|
40
|
+
|
41
|
+
tree: prints the entire structure of the document for debugging purposes.
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
**TypeElement:**
|
46
|
+
|
47
|
+
name: returns the name of the element (= tag name).
|
48
|
+
|
49
|
+
namespace: returns the namespace of the tag.
|
50
|
+
|
51
|
+
attributes: returns an hash with the property names as key and the values as value.
|
52
|
+
|
53
|
+
types: returns an array containing the names of the dynamic methods referring to the nested elements.
|
54
|
+
|
55
|
+
to_s: returns the content (overrides
|
56
|
+
tree: prints the structure referred part of the document for debugging purposes.
|
57
|
+
|
58
|
+
All these methods have aliases (eg. __name, __namespace, etc.)
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
**File example:**
|
63
|
+
funny_people.xml
|
64
|
+
|
65
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
66
|
+
<ns0:funnyPeople>
|
67
|
+
<ns1:businessCard>
|
68
|
+
<name>
|
69
|
+
<firstName>Guybrush</firstName>
|
70
|
+
<lastName>Threepwood</lastName>
|
71
|
+
</name>
|
72
|
+
<jobs:occupation name="pirate" description="Terror of threeheaded monkeys"/>
|
73
|
+
<address>
|
74
|
+
<island>Scabb Island</island>
|
75
|
+
<city>Pirate's city</city>
|
76
|
+
<street>Voodoo Blvd.</street>
|
77
|
+
<number>3</number>
|
78
|
+
</address>
|
79
|
+
</ns1:businessCard>
|
80
|
+
<ns1:businessCard>
|
81
|
+
<name>
|
82
|
+
<firstName>Senbei</firstName>
|
83
|
+
<lastName>Norimaki</lastName>
|
84
|
+
</name>
|
85
|
+
<jobs:occupation name="inventor" description="A great genius"/>
|
86
|
+
<address>
|
87
|
+
<city>Penguin Village</city>
|
88
|
+
<street>Tanuki doro</street>
|
89
|
+
<number>1</number>
|
90
|
+
</address>
|
91
|
+
</ns1:businessCard>
|
92
|
+
<types>
|
93
|
+
<type name="main character"/>
|
94
|
+
<type name="villain"/>
|
95
|
+
<otherTypes/>
|
96
|
+
</types>
|
97
|
+
</ns0:funnyPeople>
|
98
|
+
|
99
|
+
## Code example:
|
100
|
+
|
101
|
+
xml = Extreml.new './funny_people.xml'
|
102
|
+
|
103
|
+
xml.header.version # => "1.0"
|
104
|
+
xml.header.encoding # => "UTF-8"
|
105
|
+
|
106
|
+
xml.document.funnyPeople.businessCard[0].name.firstName.to_s # => Guybrush
|
107
|
+
xml.document.funnyPeople.businessCard[0].__name # => "businessCard"
|
108
|
+
|
109
|
+
xml.document.funnyPeople.types # => #<TypeElement:0x0000557373082fc8>
|
110
|
+
xml.document.funnyPeople.__types # => [:businessCard, :types]
|
111
|
+
email: fboccacini@gmail.com
|
112
|
+
executables: []
|
113
|
+
extensions: []
|
114
|
+
extra_rdoc_files: []
|
115
|
+
files:
|
116
|
+
- lib/extreml.rb
|
117
|
+
- lib/extreml/type_element.rb
|
118
|
+
homepage: https://github.com/fboccacini/extreml
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubygems_version: 3.0.3
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: A gem to easily mamipulate XML documents.
|
141
|
+
test_files: []
|