extreml 0.1.0

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