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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/extreml/type_element.rb +141 -0
  3. data/lib/extreml.rb +153 -0
  4. 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: []