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