XmlEasy 0.0.1

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.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 1.0.0 / 2007-12-08
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/xml_easy
6
+ lib/xml_easy.rb
7
+ test/test_xml_easy.rb
8
+ test/test.xml
data/README.txt ADDED
@@ -0,0 +1,51 @@
1
+ XmlEasy
2
+ by ANDO Yasushi
3
+ andyjpn@gmail.com
4
+
5
+ == DESCRIPTION:
6
+
7
+ ruby-easy offers you a way to handle xml quite easily.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * handle xml
12
+
13
+ == SYNOPSIS:
14
+
15
+ xml_easy = XmlEasy::Document.new 'text.html'
16
+
17
+ # return a link target for the 3rd item in sidebar
18
+ xml_easy.html.body.div{|e| e[:class] == 'sidebar'}.li[3].a[:href]
19
+
20
+ == REQUIREMENTS:
21
+
22
+ * cosmetics
23
+
24
+ == INSTALL:
25
+
26
+ * sudo gem install xml-easy
27
+
28
+ == LICENSE:
29
+
30
+ (The MIT License)
31
+
32
+ Copyright (c) 2007 FIX
33
+
34
+ Permission is hereby granted, free of charge, to any person obtaining
35
+ a copy of this software and associated documentation files (the
36
+ 'Software'), to deal in the Software without restriction, including
37
+ without limitation the rights to use, copy, modify, merge, publish,
38
+ distribute, sublicense, and/or sell copies of the Software, and to
39
+ permit persons to whom the Software is furnished to do so, subject to
40
+ the following conditions:
41
+
42
+ The above copyright notice and this permission notice shall be
43
+ included in all copies or substantial portions of the Software.
44
+
45
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
46
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
47
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
48
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
49
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
50
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
51
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/xml_easy.rb'
6
+
7
+ #Hoe.new('XmlEasy', XmlEasy::VERSION) do |p|
8
+ Hoe.new('XmlEasy', XmlEasy::VERSION) do |p|
9
+ p.rubyforge_name = 'xml_easy'
10
+ p.author = 'ANDO Yasushi'
11
+ p.email = 'andyjpn@gmail.com'
12
+ p.summary = 'handle xml easily'
13
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
14
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
15
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
16
+ end
17
+
18
+ # vim: syntax=Ruby
data/bin/xml_easy ADDED
File without changes
data/lib/xml_easy.rb ADDED
@@ -0,0 +1,246 @@
1
+ require 'stringio'
2
+ require 'rexml/document'
3
+ require 'rexml/parsers/streamparser'
4
+ require 'rexml/parsers/baseparser'
5
+ require 'rexml/streamlistener'
6
+
7
+ class Array
8
+ def method_missing(name, *args, &block)
9
+ if all? {|e| nil? or e.class == XmlEasy::Element}
10
+ elms = map{|e| e.send(name, *args, &block) rescue nil}.flatten.compact
11
+ unless block.nil?
12
+ elms = elms.select &block
13
+ end
14
+ elms
15
+ else
16
+ raise NoMethodError.new(name)
17
+ end
18
+ end
19
+ end
20
+
21
+ class HashWithIndifferentAccess < Hash
22
+ def get_with_indifferent_access(key)
23
+ self[key.to_s] || self[key.to_sym]
24
+ end
25
+ alias get_with_indifferent_access []
26
+
27
+ def set_with_indifferent_access(key, val)
28
+ self[key.to_s] = val
29
+ end
30
+ alias set_with_indifferent_access []=
31
+ end
32
+
33
+ class XmlEasy
34
+ VERSION = '0.0.1'
35
+
36
+ class EasyListener
37
+ include REXML::StreamListener
38
+ def initialize(doc)
39
+ @doc = doc
40
+ @current = doc.root
41
+ end
42
+
43
+ def attlistdecl(elm_name, attrs, content); end
44
+ def cdata(content); end
45
+ def comment(content); end
46
+ def elementdecl(content); end
47
+ def entity(content); end
48
+ def instruction(name, inst); end
49
+ def notationdecl(content); end
50
+
51
+ def xmldecl(version, encoding, standalone)
52
+ @doc.xml_dec = "<?xml#{
53
+ version ? " version='#{version}'" : ''
54
+ }#{
55
+ encoding ? " encoding='#{encoding}'" : ''
56
+ }#{
57
+ standalone ? " standalone='#{standalone}'" : ''
58
+ }?>"
59
+ end
60
+
61
+ def doctype(name, pub_sys, long_name, uri)
62
+ #TODO: temporary
63
+ @doc.doc_type = "<!DOCTYPE window [<!ENTITY nl '&#38;#10;'>]>"
64
+ end
65
+
66
+ def entitydecl(content)
67
+ end
68
+
69
+ def doctype_end
70
+ end
71
+
72
+ def tag_start(name, attrs)
73
+ element = Element.new name
74
+ attrs.each{|k, v| element[k] = v.gsub('"', '&quot;').inspect}
75
+ @current << element
76
+ @current = element
77
+ end
78
+
79
+ def tag_end(name)
80
+ if @current.name == name.to_sym
81
+ @current = @current.parent
82
+ else
83
+ raise RuntimeError.new("unmatch end-tag: #{name.inspect}")
84
+ end
85
+ end
86
+
87
+ def text(content)
88
+ @current.body = content
89
+ end
90
+ end
91
+
92
+ class Document
93
+ attr_accessor :xml_dec, :doc_type, :root
94
+
95
+ def initialize(filename=nil)
96
+ @root = Element.new
97
+ @filename = filename
98
+ if @filename =~ /^<.*$/
99
+ REXML::Document.parse_stream StringIO.new(filename), EasyListener.new(self)
100
+ else
101
+ REXML::Document.parse_stream File.new(filename), EasyListener.new(self) if @filename
102
+ end
103
+ end
104
+
105
+ def copy
106
+ copied = self.class.new
107
+ copied.xml_dec = @xml_dec
108
+ copied.doc_type = @doc_type
109
+ copied.root = @root.copy
110
+ copied
111
+ end
112
+
113
+ def to_s
114
+ <<-eos
115
+ #{@xml_dec}
116
+ #{@doc_type}
117
+ #{@root.children.first.to_s}
118
+ eos
119
+ end
120
+
121
+ def method_missing(name, *args, &block)
122
+ @root.send name
123
+ end
124
+ end
125
+
126
+ class Element
127
+ attr_accessor :parent, :children, :name, :body, :attributes
128
+
129
+ def initialize(name=nil)
130
+ @parent = nil
131
+ @name = name.to_sym if name
132
+ @body = ''
133
+ @attributes = HashWithIndifferentAccess.new
134
+ @children = []
135
+ end
136
+
137
+ def body=(content)
138
+ if @body =~ /^\s+$/m
139
+ @body = ''
140
+ else
141
+ @body = content
142
+ end
143
+ self
144
+ end
145
+
146
+ def []=(key, val)
147
+ val = $1 if val =~ /^"(.*)"$/ or val =~ /^"(.*)"$/ #TODO
148
+ @attributes[key.to_s] = val
149
+ self
150
+ end
151
+
152
+ def [](key)
153
+ @attributes[key.to_s]
154
+ end
155
+
156
+ def <<(element, option=nil)
157
+ case option
158
+ when NilClass
159
+ @children << element
160
+ when Hash
161
+ if option[:after]
162
+ @children.insert @children.index(option[:after]) + 1, element
163
+ elsif option[:before]
164
+ @children.insert @children.index(option[:before]), element
165
+ end
166
+ when String, Symbol
167
+ case option.to_sym
168
+ when :top
169
+ @children.unshift element
170
+ when :bottom
171
+ @children << element
172
+ else
173
+ raise ArgumentError.new('invalid argument')
174
+ end
175
+ else
176
+ raise ArgumentError.new('invalid argument')
177
+ end
178
+ element.parent = self
179
+ end
180
+
181
+ def prune_children(num)
182
+ @children = @children[0...num]
183
+ end
184
+
185
+ def remove
186
+ @parent.children.delete self
187
+ end
188
+
189
+ def each(&block)
190
+ @children.each &block
191
+ end
192
+
193
+ def each_attribute(&block)
194
+ @attributes.each &block
195
+ end
196
+
197
+ def to_s(indent=0)
198
+ "#{"\t" * indent}<#{@name}" +
199
+ unless @attributes.empty?
200
+ "\n#{@attributes.to_a.map{|k, v| "#{"\t" * (indent + 1)} #{k}=#{v.inspect}"}.join("\n")}"
201
+ else
202
+ ''
203
+ end +
204
+ if @children.empty? and @body.empty?
205
+ " />\n"
206
+ elsif !@body.empty? and @children.empty?
207
+ ">#{@body}\n#{"\t" * indent}</#{@name}>\n"
208
+ else
209
+ ">\n#{@children.map{|c| c.to_s(indent + 1)}.join}#{"\t" * indent}</#{@name}>\n"
210
+ end
211
+ end
212
+
213
+ def copy
214
+ copied = self.class.new @name
215
+ copied.body = @body
216
+ copied.attributes = HashWithIndifferentAccess[@attributes]
217
+ @children.each do |c|
218
+ copied << c
219
+ end
220
+ copied
221
+ end
222
+
223
+ def method_missing(name, *args, &block)
224
+ if name == :each_with_index
225
+ [self].each_with_index &block
226
+ return
227
+ end
228
+ children = @children.select {|c| c.name == name.to_sym}
229
+ if children.empty?
230
+ name = name.to_s.sub(/^(.*)_$/, '\1').to_sym
231
+ children = @children.select {|c| c.name == name.to_sym}
232
+ end
233
+ unless block.nil?
234
+ children = children.select &block
235
+ end
236
+ if children.empty?
237
+ #puts "<#{name}> does not exist in <#{@name}>."
238
+ raise NameError.new(name)
239
+ elsif children.size == 1
240
+ children.first
241
+ else
242
+ children
243
+ end
244
+ end
245
+ end
246
+ end
data/test/test.xml ADDED
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0"?>
2
+ <root>
3
+ <child1></child1>
4
+ <child2 attr1="val1"></child2>
5
+ </root>
@@ -0,0 +1,122 @@
1
+ require 'test/unit'
2
+ require 'lib/xml_easy'
3
+
4
+ class XmlEasyTest < Test::Unit::TestCase
5
+ TEST_XML = "#{File.dirname(__FILE__)}/test.xml"
6
+ def setup
7
+ @xml = XmlEasy::Document.new TEST_XML
8
+ end
9
+
10
+ def test_new_doc_with_string
11
+ xml = XmlEasy::Document.new <<-target
12
+ <?xml version="1.0"?>
13
+ <root attr="new with string">
14
+ <child1></child1>
15
+ <child2 attr1="val1"></child2>
16
+ </root>
17
+ target
18
+
19
+ assert_element <<-expected, xml
20
+ <?xml version='1.0'?>
21
+
22
+ <root
23
+ attr="new with string">
24
+ <child1 />
25
+ <child2
26
+ attr1="val1" />
27
+ </root>
28
+ expected
29
+ end
30
+
31
+ def test_access_to_child
32
+ assert_equal "<child1 />\n", @xml.root_.child1.to_s
33
+ end
34
+
35
+ def test_insert_after
36
+ c1 = @xml.root_.child1
37
+ c2 = @xml.root_.child2
38
+
39
+ c21 = c2.copy
40
+ c21[:attr1] = 'val11'
41
+ c1.parent.<< c21, :after => c2
42
+ assert_element <<-expected, @xml
43
+ <?xml version='1.0'?>
44
+
45
+ <root>
46
+ <child1 />
47
+ <child2
48
+ attr1="val1" />
49
+ <child2
50
+ attr1="val11" />
51
+ </root>
52
+ expected
53
+ end
54
+
55
+ def test_insert_before
56
+ c1 = @xml.root_.child1
57
+ c2 = @xml.root_.child2
58
+
59
+ c22 = c2.copy
60
+ c22[:attr1] = 'val12'
61
+ c1.parent.<< c22, :before => c2
62
+ assert_element <<-expected, @xml
63
+ <?xml version='1.0'?>
64
+
65
+ <root>
66
+ <child1 />
67
+ <child2
68
+ attr1="val12" />
69
+ <child2
70
+ attr1="val1" />
71
+ </root>
72
+ expected
73
+ end
74
+
75
+ def test_insert_top
76
+ c1 = @xml.root_.child1
77
+ c2 = @xml.root_.child2
78
+
79
+ c23 = c2.copy
80
+ c23[:attr1] = 'val13'
81
+ c1.parent.<< c23, :top
82
+ assert_element <<-expected, @xml
83
+ <?xml version='1.0'?>
84
+
85
+ <root>
86
+ <child2
87
+ attr1="val13" />
88
+ <child1 />
89
+ <child2
90
+ attr1="val1" />
91
+ </root>
92
+ expected
93
+ end
94
+
95
+ def test_insert_bottom
96
+ c1 = @xml.root_.child1
97
+ c2 = @xml.root_.child2
98
+
99
+ c24 = c2.copy
100
+ c24[:attr1] = 'val14'
101
+ c1.parent.<< c24, :bottom
102
+ assert_element <<-expected, @xml
103
+ <?xml version='1.0'?>
104
+
105
+ <root>
106
+ <child1 />
107
+ <child2
108
+ attr1="val1" />
109
+ <child2
110
+ attr1="val14" />
111
+ </root>
112
+ expected
113
+ end
114
+
115
+ protected
116
+
117
+ def assert_element(expected, element)
118
+ assert_equal expected.strip, element.to_s.strip.gsub("\t", ' ')
119
+ end
120
+ end
121
+
122
+
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: XmlEasy
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2007-12-08 00:00:00 +09:00
8
+ summary: handle xml easily
9
+ require_paths:
10
+ - lib
11
+ email: andyjpn@gmail.com
12
+ homepage: " by ANDO Yasushi"
13
+ rubyforge_project: xml_easy
14
+ description: "ruby-easy offers you a way to handle xml quite easily. == FEATURES/PROBLEMS: * handle xml == SYNOPSIS: xml_easy = XmlEasy::Document.new 'text.html'"
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - ANDO Yasushi
31
+ files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - Rakefile
36
+ - bin/xml_easy
37
+ - lib/xml_easy.rb
38
+ - test/test_xml_easy.rb
39
+ - test/test.xml
40
+ test_files:
41
+ - test/test_xml_easy.rb
42
+ rdoc_options:
43
+ - --main
44
+ - README.txt
45
+ extra_rdoc_files:
46
+ - History.txt
47
+ - Manifest.txt
48
+ - README.txt
49
+ executables:
50
+ - xml_easy
51
+ extensions: []
52
+
53
+ requirements: []
54
+
55
+ dependencies:
56
+ - !ruby/object:Gem::Dependency
57
+ name: hoe
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Version::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 1.3.0
64
+ version: