feedtools 0.1.0 → 0.2.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.
- data/CHANGELOG +11 -0
- data/lib/feed_tools.rb +2496 -810
- data/lib/feed_tools/vendor/builder.rb +2 -0
- data/lib/feed_tools/vendor/builder/blankslate.rb +2 -0
- data/lib/feed_tools/vendor/builder/xmlbase.rb +2 -1
- data/lib/feed_tools/vendor/builder/xmlevents.rb +2 -0
- data/lib/feed_tools/vendor/builder/xmlmarkup.rb +4 -2
- data/lib/feed_tools/vendor/htree.rb +97 -0
- data/lib/feed_tools/vendor/htree/container.rb +10 -0
- data/lib/feed_tools/vendor/htree/context.rb +67 -0
- data/lib/feed_tools/vendor/htree/display.rb +27 -0
- data/lib/feed_tools/vendor/htree/doc.rb +149 -0
- data/lib/feed_tools/vendor/htree/elem.rb +262 -0
- data/lib/feed_tools/vendor/htree/encoder.rb +163 -0
- data/lib/feed_tools/vendor/htree/equality.rb +218 -0
- data/lib/feed_tools/vendor/htree/extract_text.rb +37 -0
- data/lib/feed_tools/vendor/htree/fstr.rb +33 -0
- data/lib/feed_tools/vendor/htree/gencode.rb +97 -0
- data/lib/feed_tools/vendor/htree/htmlinfo.rb +672 -0
- data/lib/feed_tools/vendor/htree/inspect.rb +108 -0
- data/lib/feed_tools/vendor/htree/leaf.rb +94 -0
- data/lib/feed_tools/vendor/htree/loc.rb +367 -0
- data/lib/feed_tools/vendor/htree/modules.rb +48 -0
- data/lib/feed_tools/vendor/htree/name.rb +124 -0
- data/lib/feed_tools/vendor/htree/output.rb +207 -0
- data/lib/feed_tools/vendor/htree/parse.rb +407 -0
- data/lib/feed_tools/vendor/htree/raw_string.rb +124 -0
- data/lib/feed_tools/vendor/htree/regexp-util.rb +15 -0
- data/lib/feed_tools/vendor/htree/rexml.rb +130 -0
- data/lib/feed_tools/vendor/htree/scan.rb +166 -0
- data/lib/feed_tools/vendor/htree/tag.rb +111 -0
- data/lib/feed_tools/vendor/htree/template.rb +909 -0
- data/lib/feed_tools/vendor/htree/text.rb +115 -0
- data/lib/feed_tools/vendor/htree/traverse.rb +465 -0
- data/rakefile +1 -1
- data/test/rss_test.rb +97 -0
- metadata +30 -1
@@ -11,9 +11,10 @@
|
|
11
11
|
# Provide a flexible and easy to use Builder for creating XML markup.
|
12
12
|
# See XmlBuilder for usage details.
|
13
13
|
|
14
|
+
# :stopdoc:
|
14
15
|
require 'builder/xmlbase'
|
15
16
|
|
16
|
-
module Builder
|
17
|
+
module Builder #:nodoc:
|
17
18
|
|
18
19
|
# Create XML markup easily. All (well, almost all) methods sent to
|
19
20
|
# an XmlMarkup object will be translated to the equivalent XML
|
@@ -157,7 +158,7 @@ module Builder
|
|
157
158
|
# xml.stong("text")
|
158
159
|
# }
|
159
160
|
#
|
160
|
-
class XmlMarkup < XmlBase
|
161
|
+
class XmlMarkup < XmlBase #:nodoc:
|
161
162
|
|
162
163
|
# Create an XML markup builder. Parameters are specified by an
|
163
164
|
# option hash.
|
@@ -295,3 +296,4 @@ module Builder
|
|
295
296
|
end
|
296
297
|
|
297
298
|
end
|
299
|
+
# :startdoc:
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#
|
2
|
+
# = htree.rb
|
3
|
+
#
|
4
|
+
# HTML/XML document tree
|
5
|
+
#
|
6
|
+
# Author:: Tanaka Akira <akr@m17n.org>
|
7
|
+
#
|
8
|
+
# == Features
|
9
|
+
#
|
10
|
+
# - Permissive unified HTML/XML parser
|
11
|
+
# - byte-to-byte round-tripping unparser
|
12
|
+
# - XML namespace support
|
13
|
+
# - Dedicated class for escaped string. This ease sanitization.
|
14
|
+
# - XHTML/XML generator
|
15
|
+
# - template engine: link:files/htree/template_rb.html
|
16
|
+
# - recursive template expansion
|
17
|
+
# - REXML tree generator: link:files/htree/rexml_rb.html
|
18
|
+
#
|
19
|
+
# == Example
|
20
|
+
#
|
21
|
+
# The following one-liner prints parsed tree object.
|
22
|
+
#
|
23
|
+
# % ruby -rhtree -e 'pp HTree(ARGF)' html-file
|
24
|
+
#
|
25
|
+
# The following two-line script convert HTML to XHTML.
|
26
|
+
#
|
27
|
+
# require 'htree'
|
28
|
+
# HTree(STDIN).display_xml
|
29
|
+
#
|
30
|
+
# The conversion method to REXML is provided as to_rexml.
|
31
|
+
#
|
32
|
+
# HTree(...).to_rexml
|
33
|
+
#
|
34
|
+
# == Module/Class Hierarchy
|
35
|
+
#
|
36
|
+
# * HTree
|
37
|
+
# * HTree::Name
|
38
|
+
# * HTree::Context
|
39
|
+
# * HTree::Location
|
40
|
+
# * HTree::Node
|
41
|
+
# * HTree::Doc
|
42
|
+
# * HTree::Elem
|
43
|
+
# * HTree::Text
|
44
|
+
# * HTree::XMLDecl
|
45
|
+
# * HTree::DocType
|
46
|
+
# * HTree::ProcIns
|
47
|
+
# * HTree::Comment
|
48
|
+
# * HTree::BogusETag
|
49
|
+
# * HTree::Error
|
50
|
+
#
|
51
|
+
# == Method Summary
|
52
|
+
#
|
53
|
+
# HTree provides following methods.
|
54
|
+
#
|
55
|
+
# - Parsing Methods
|
56
|
+
# - HTree(<i>html_string</i>) -> HTree::Doc
|
57
|
+
# - HTree.parse(<i>input</i>) -> HTree::Doc
|
58
|
+
#
|
59
|
+
# - Generation Methods
|
60
|
+
# - HTree::Node#display_xml -> STDOUT
|
61
|
+
# - HTree::Node#display_xml(<i>out</i>) -> <i>out</i>
|
62
|
+
# - HTree::Node#display_xml(<i>out</i>, <i>encoding</i>) -> <i>out</i>
|
63
|
+
# - HTree::Text#to_s -> String
|
64
|
+
#
|
65
|
+
# - Template Methods
|
66
|
+
# - HTree.expand_template{<i>template_string</i>} -> STDOUT
|
67
|
+
# - HTree.expand_template(<i>out</i>){<i>template_string</i>} -> <i>out</i>
|
68
|
+
# - HTree.expand_template(<i>out</i>, <i>encoding</i>){<i>template_string</i>} -> <i>out</i>
|
69
|
+
# - HTree.compile_template(<i>template_string</i>) -> Module
|
70
|
+
# - HTree{<i>template_string</i>} -> HTree::Doc
|
71
|
+
#
|
72
|
+
# - Traverse Methods
|
73
|
+
# - HTree::Elem#attributes -> Hash[HTree::Name -> HTree::Text]
|
74
|
+
# - HTree::Elem::Location#attributes -> Hash[HTree::Name -> HTree::Location]
|
75
|
+
#
|
76
|
+
# - Predicate Methods
|
77
|
+
# - HTree::Traverse#doc? -> true or false
|
78
|
+
# - HTree::Traverse#elem? -> true or false
|
79
|
+
# - HTree::Traverse#text? -> true or false
|
80
|
+
# - HTree::Traverse#xmldecl? -> true or false
|
81
|
+
# - HTree::Traverse#doctype? -> true or false
|
82
|
+
# - HTree::Traverse#procins? -> true or false
|
83
|
+
# - HTree::Traverse#comment? -> true or false
|
84
|
+
# - HTree::Traverse#bogusetag? -> true or false
|
85
|
+
#
|
86
|
+
# - REXML Tree Generator
|
87
|
+
# - HTree::Node#to_rexml -> REXML::Child
|
88
|
+
|
89
|
+
require 'htree/parse'
|
90
|
+
require 'htree/extract_text'
|
91
|
+
require 'htree/equality'
|
92
|
+
require 'htree/inspect'
|
93
|
+
require 'htree/display'
|
94
|
+
require 'htree/loc'
|
95
|
+
require 'htree/traverse'
|
96
|
+
require 'htree/template'
|
97
|
+
require 'htree/rexml'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
module HTree
|
3
|
+
class Context
|
4
|
+
DefaultNamespaces = {'xml'=>'http://www.w3.org/XML/1998/namespace'}
|
5
|
+
DefaultNamespaces.default = ""
|
6
|
+
DefaultNamespaces.freeze
|
7
|
+
|
8
|
+
# The optional argument `namespaces' should be a hash or nil.
|
9
|
+
# HTree::DefaultNamespaces is used if nil is specified.
|
10
|
+
#
|
11
|
+
# If it is a hash, its key should be nil or a string.
|
12
|
+
# nil means default namespace.
|
13
|
+
# The string means some prefix which must not be empty.
|
14
|
+
#
|
15
|
+
# The hash value should be a string.
|
16
|
+
# The empty string "" means unbound namespace.
|
17
|
+
def initialize(namespaces=nil)
|
18
|
+
namespaces ||= DefaultNamespaces
|
19
|
+
namespaces.each_pair {|k, v|
|
20
|
+
check_namespace_prefix(k)
|
21
|
+
check_namespace_uri(v)
|
22
|
+
}
|
23
|
+
namespaces = namespaces.dup.freeze unless namespaces.frozen?
|
24
|
+
@namespaces = namespaces
|
25
|
+
end
|
26
|
+
attr_reader :namespaces
|
27
|
+
|
28
|
+
# return a namespace URI corresponding to _prefix_.
|
29
|
+
# It returns nil if _prefix_ is not defined.
|
30
|
+
def namespace_uri(prefix)
|
31
|
+
@namespaces[prefix]
|
32
|
+
end
|
33
|
+
|
34
|
+
# generate a new Context object which namespaces are substituted by
|
35
|
+
# a hash _declared_namespaces_.
|
36
|
+
def subst_namespaces(declared_namespaces)
|
37
|
+
namespaces = @namespaces.dup
|
38
|
+
declared_namespaces.each {|k, v|
|
39
|
+
check_namespace_prefix(k)
|
40
|
+
check_namespace_uri(v)
|
41
|
+
namespaces[k] = v
|
42
|
+
}
|
43
|
+
if namespaces == @namespaces
|
44
|
+
self
|
45
|
+
else
|
46
|
+
Context.new(namespaces)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def check_namespace_prefix(k)
|
52
|
+
unless (String === k && !k.empty?) || k == nil
|
53
|
+
raise ArgumentError, "invalid namespace prefix: #{k.inspect}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def check_namespace_uri(v)
|
58
|
+
unless String === v
|
59
|
+
raise ArgumentError, "invalid namespace URI: #{v.inspect}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
DefaultContext = Context.new
|
65
|
+
HTMLContext = DefaultContext.subst_namespaces(nil=>"http://www.w3.org/1999/xhtml")
|
66
|
+
end
|
67
|
+
# :startdoc:
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
require 'htree/output'
|
3
|
+
|
4
|
+
module HTree
|
5
|
+
module Node
|
6
|
+
# HTree::Node#display_xml prints the node as XML.
|
7
|
+
#
|
8
|
+
# The first optional argument, <i>out</i>,
|
9
|
+
# specifies output target.
|
10
|
+
# It should respond to <tt><<</tt>.
|
11
|
+
# If it is not specified, $stdout is used.
|
12
|
+
#
|
13
|
+
# The second optional argument, <i>encoding</i>,
|
14
|
+
# specifies output MIME charset (character encoding).
|
15
|
+
# If it is not specified, HTree::Encoder.internal_charset is used.
|
16
|
+
#
|
17
|
+
# HTree::Node#display_xml returns <i>out</i>.
|
18
|
+
def display_xml(out=$stdout, encoding=HTree::Encoder.internal_charset)
|
19
|
+
encoder = HTree::Encoder.new(encoding)
|
20
|
+
self.output(encoder, HTree::DefaultContext)
|
21
|
+
# don't call finish_with_xmldecl because self already has a xml decl.
|
22
|
+
out << encoder.finish
|
23
|
+
out
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
# :startdoc:
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
require 'htree/modules'
|
3
|
+
require 'htree/container'
|
4
|
+
|
5
|
+
module HTree
|
6
|
+
class Doc
|
7
|
+
class << self
|
8
|
+
alias new! new
|
9
|
+
end
|
10
|
+
|
11
|
+
# The arguments should be a sequence of follows.
|
12
|
+
# [String object] specified string is converted to HTree::Text.
|
13
|
+
# [HTree::Node object] used as a child.
|
14
|
+
# [HTree::Doc object]
|
15
|
+
# used as children.
|
16
|
+
# It is expanded except HTree::XMLDecl and HTree::DocType objects.
|
17
|
+
# [Array of String, HTree::Node and HTree::Doc] used as children.
|
18
|
+
#
|
19
|
+
def Doc.new(*args)
|
20
|
+
children = []
|
21
|
+
args.each {|arg|
|
22
|
+
arg = arg.to_node if HTree::Location === arg
|
23
|
+
case arg
|
24
|
+
when Array
|
25
|
+
arg.each {|a|
|
26
|
+
a = a.to_node if HTree::Location === a
|
27
|
+
case a
|
28
|
+
when HTree::Doc
|
29
|
+
children.concat(a.children.reject {|c|
|
30
|
+
HTree::XMLDecl === c || HTree::DocType === c
|
31
|
+
})
|
32
|
+
when HTree::Node
|
33
|
+
children << a
|
34
|
+
when String
|
35
|
+
children << Text.new(a)
|
36
|
+
else
|
37
|
+
raise TypeError, "unexpected argument: #{arg.inspect}"
|
38
|
+
end
|
39
|
+
}
|
40
|
+
when HTree::Doc
|
41
|
+
children.concat(arg.children.reject {|c|
|
42
|
+
HTree::XMLDecl === c || HTree::DocType === c
|
43
|
+
})
|
44
|
+
when HTree::Node
|
45
|
+
children << arg
|
46
|
+
when String
|
47
|
+
children << Text.new(arg)
|
48
|
+
else
|
49
|
+
raise TypeError, "unexpected argument: #{arg.inspect}"
|
50
|
+
end
|
51
|
+
}
|
52
|
+
new!(children)
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(children=[]) # :notnew:
|
56
|
+
@children = children.dup.freeze
|
57
|
+
unless @children.all? {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
|
58
|
+
unacceptable = @children.reject {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
|
59
|
+
unacceptable = unacceptable.map {|uc| uc.inspect }.join(', ')
|
60
|
+
raise TypeError, "Unacceptable document child: #{unacceptable}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_subnode_internal(index) # :nodoc:
|
65
|
+
unless Integer === index
|
66
|
+
raise TypeError, "invalid index: #{index.inspect}"
|
67
|
+
end
|
68
|
+
if index < 0 || @children.length <= index
|
69
|
+
nil
|
70
|
+
else
|
71
|
+
@children[index]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# doc.subst_subnode(pairs) -> doc
|
76
|
+
#
|
77
|
+
# The argument _pairs_ should be a hash or an assocs.
|
78
|
+
# Its key should be an integer which means an index for children.
|
79
|
+
#
|
80
|
+
# Its value should be one of follows.
|
81
|
+
# [HTree::Node object] specified object is used as is.
|
82
|
+
# [String object] specified string is converted to HTree::Text
|
83
|
+
# [Array of above] specified HTree::Node and String is used in that order.
|
84
|
+
# [nil] delete corresponding node.
|
85
|
+
#
|
86
|
+
# d = HTree('<a/><b/><c/>')
|
87
|
+
# p d.subst_subnode({0=>HTree('<x/>'), 2=>HTree('<z/>')})
|
88
|
+
# p d.subst_subnode([[0,HTree('<x/>')], [2,HTree('<z/>')]])
|
89
|
+
# # =>
|
90
|
+
# #<HTree::Doc {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}>
|
91
|
+
# #<HTree::Doc {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}>
|
92
|
+
#
|
93
|
+
def subst_subnode(pairs)
|
94
|
+
hash = {}
|
95
|
+
pairs.each {|index, value|
|
96
|
+
unless Integer === index
|
97
|
+
raise TypeError, "invalid index: #{index.inspect}"
|
98
|
+
end
|
99
|
+
value = value.to_node if HTree::Location === value
|
100
|
+
case value
|
101
|
+
when Node
|
102
|
+
value = [value]
|
103
|
+
when String
|
104
|
+
value = [value]
|
105
|
+
when Array
|
106
|
+
value = value.dup
|
107
|
+
when nil
|
108
|
+
value = []
|
109
|
+
else
|
110
|
+
raise TypeError, "invalid value: #{value.inspect}"
|
111
|
+
end
|
112
|
+
value.map! {|v|
|
113
|
+
v = v.to_node if HTree::Location === v
|
114
|
+
case v
|
115
|
+
when Node
|
116
|
+
v
|
117
|
+
when String
|
118
|
+
Text.new(v)
|
119
|
+
else
|
120
|
+
raise TypeError, "invalid value: #{v.inspect}"
|
121
|
+
end
|
122
|
+
}
|
123
|
+
if !hash.include?(index)
|
124
|
+
hash[index] = []
|
125
|
+
end
|
126
|
+
hash[index].concat value
|
127
|
+
}
|
128
|
+
|
129
|
+
children_left = []
|
130
|
+
children = @children.dup
|
131
|
+
children_right = []
|
132
|
+
|
133
|
+
hash.keys.sort.each {|index|
|
134
|
+
value = hash[index]
|
135
|
+
if index < 0
|
136
|
+
children_left << value
|
137
|
+
elsif children.length <= index
|
138
|
+
children_right << value
|
139
|
+
else
|
140
|
+
children[index] = value
|
141
|
+
end
|
142
|
+
}
|
143
|
+
|
144
|
+
children = [children_left, children, children_right].flatten.compact
|
145
|
+
Doc.new(children)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
# :startdoc:
|
@@ -0,0 +1,262 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
require 'htree/modules'
|
3
|
+
require 'htree/tag'
|
4
|
+
require 'htree/context'
|
5
|
+
require 'htree/container'
|
6
|
+
|
7
|
+
module HTree
|
8
|
+
class Elem
|
9
|
+
class << self
|
10
|
+
alias new! new
|
11
|
+
end
|
12
|
+
|
13
|
+
# The first argument _name_ should be an instance of String or HTree::Name.
|
14
|
+
#
|
15
|
+
# The rest of arguments should be a sequence of follows.
|
16
|
+
# [Hash object] used as attributes.
|
17
|
+
# [String object] specified string is converted to HTree::Text.
|
18
|
+
# [HTree::Node object] used as a child.
|
19
|
+
# [HTree::Doc object]
|
20
|
+
# used as children.
|
21
|
+
# It is expanded except HTree::XMLDecl and HTree::DocType objects.
|
22
|
+
# [Array of String, HTree::Node, HTree::Doc] used as children.
|
23
|
+
# [HTree::Context object]
|
24
|
+
# used as as context which represents XML namespaces.
|
25
|
+
# This should apper once at most.
|
26
|
+
#
|
27
|
+
# HTree::Location object is accepted just as HTree::Node.
|
28
|
+
#
|
29
|
+
# If the rest arguments consists only
|
30
|
+
# Hash and HTree::Context, empty element is created.
|
31
|
+
#
|
32
|
+
# p HTree::Elem.new("e").empty_element? # => true
|
33
|
+
# p HTree::Elem.new("e", []).empty_element? # => false
|
34
|
+
def Elem.new(name, *args)
|
35
|
+
attrs = []
|
36
|
+
children = []
|
37
|
+
context = nil
|
38
|
+
args.each {|arg|
|
39
|
+
arg = arg.to_node if HTree::Location === arg
|
40
|
+
case arg
|
41
|
+
when Context
|
42
|
+
raise ArgumentError, "multiple context" if context
|
43
|
+
context = arg
|
44
|
+
when Hash
|
45
|
+
arg.each {|k, v| attrs << [k, v] }
|
46
|
+
when Array
|
47
|
+
arg.each {|a|
|
48
|
+
a = a.to_node if HTree::Location === a
|
49
|
+
case a
|
50
|
+
when HTree::Doc
|
51
|
+
children.concat(a.children.reject {|c|
|
52
|
+
HTree::XMLDecl === c || HTree::DocType === c
|
53
|
+
})
|
54
|
+
when HTree::Node
|
55
|
+
children << a
|
56
|
+
when String
|
57
|
+
children << Text.new(a)
|
58
|
+
else
|
59
|
+
raise TypeError, "unexpected argument: #{arg.inspect}"
|
60
|
+
end
|
61
|
+
}
|
62
|
+
when HTree::Doc
|
63
|
+
children.concat(arg.children.reject {|c|
|
64
|
+
HTree::XMLDecl === c || HTree::DocType === c
|
65
|
+
})
|
66
|
+
when HTree::Node
|
67
|
+
children << arg
|
68
|
+
when String
|
69
|
+
children << Text.new(arg)
|
70
|
+
|
71
|
+
else
|
72
|
+
raise TypeError, "unexpected argument: #{arg.inspect}"
|
73
|
+
end
|
74
|
+
}
|
75
|
+
context ||= DefaultContext
|
76
|
+
if children.empty? && args.all? {|arg| Hash === arg || Context === arg }
|
77
|
+
children = nil
|
78
|
+
end
|
79
|
+
new!(STag.new(name, attrs, context), children)
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(stag, children=nil, etag=nil) # :notnew:
|
83
|
+
unless stag.class == STag
|
84
|
+
raise TypeError, "HTree::STag expected: #{stag.inspect}"
|
85
|
+
end
|
86
|
+
unless !children || children.all? {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
|
87
|
+
unacceptable = children.reject {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
|
88
|
+
unacceptable = unacceptable.map {|uc| uc.inspect }.join(', ')
|
89
|
+
raise TypeError, "Unacceptable element child: #{unacceptable}"
|
90
|
+
end
|
91
|
+
unless !etag || etag.class == ETag
|
92
|
+
raise TypeError, "HTree::ETag expected: #{etag.inspect}"
|
93
|
+
end
|
94
|
+
@stag = stag
|
95
|
+
@children = (children ? children.dup : []).freeze
|
96
|
+
@empty = children == nil && etag == nil
|
97
|
+
@etag = etag
|
98
|
+
end
|
99
|
+
|
100
|
+
def context; @stag.context end
|
101
|
+
|
102
|
+
# +element_name+ returns the name of the element name as a Name object.
|
103
|
+
def element_name() @stag.element_name end
|
104
|
+
|
105
|
+
def empty_element?
|
106
|
+
@empty
|
107
|
+
end
|
108
|
+
|
109
|
+
def each_attribute(&block) # :yields: attr_name, attr_text
|
110
|
+
@stag.each_attribute(&block)
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_subnode_internal(index) # :nodoc:
|
114
|
+
case index
|
115
|
+
when String
|
116
|
+
name = Name.parse_attribute_name(index, DefaultContext)
|
117
|
+
update_attribute_hash[name.universal_name]
|
118
|
+
when Name
|
119
|
+
update_attribute_hash[index.universal_name]
|
120
|
+
when Integer
|
121
|
+
if index < 0 || @children.length <= index
|
122
|
+
nil
|
123
|
+
else
|
124
|
+
@children[index]
|
125
|
+
end
|
126
|
+
else
|
127
|
+
raise TypeError, "invalid index: #{index.inspect}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# call-seq:
|
132
|
+
# elem.subst_subnode(pairs) -> elem
|
133
|
+
#
|
134
|
+
# The argument _pairs_ should be a hash or an assocs.
|
135
|
+
#
|
136
|
+
# The key of pairs should be one of following.
|
137
|
+
# [HTree::Name or String object] attribute name.
|
138
|
+
# [Integer object] child index.
|
139
|
+
#
|
140
|
+
# The value of pairs should be one of follows.
|
141
|
+
# [HTree::Node object] specified object is used as is.
|
142
|
+
# [String object] specified string is converted to HTree::Text
|
143
|
+
# [Array of above] specified HTree::Node and String is used in that order.
|
144
|
+
# [nil] delete corresponding node.
|
145
|
+
#
|
146
|
+
# e = HTree('<r><a/><b/><c/></r>').root
|
147
|
+
# p e.subst_subnode({0=>HTree('<x/>'), 2=>HTree('<z/>')})
|
148
|
+
# p e.subst_subnode([[0, HTree('<x/>')], [2,HTree('<z/>')]])
|
149
|
+
# # =>
|
150
|
+
# {elem <r> {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}}
|
151
|
+
# {elem <r> {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}}
|
152
|
+
#
|
153
|
+
def subst_subnode(pairs)
|
154
|
+
hash = {}
|
155
|
+
pairs.each {|index, value|
|
156
|
+
case index
|
157
|
+
when Name, Integer
|
158
|
+
when String
|
159
|
+
index = Name.parse_attribute_name(index, DefaultContext)
|
160
|
+
else
|
161
|
+
raise TypeError, "invalid index: #{index.inspect}"
|
162
|
+
end
|
163
|
+
value = value.to_node if HTree::Location === value
|
164
|
+
case value
|
165
|
+
when Node
|
166
|
+
value = [value]
|
167
|
+
when String
|
168
|
+
value = [value]
|
169
|
+
when Array
|
170
|
+
value = value.dup
|
171
|
+
when nil
|
172
|
+
value = []
|
173
|
+
else
|
174
|
+
raise TypeError, "invalid value: #{value.inspect}"
|
175
|
+
end
|
176
|
+
value.map! {|v|
|
177
|
+
v = v.to_node if HTree::Location === v
|
178
|
+
case v
|
179
|
+
when Node
|
180
|
+
v
|
181
|
+
when String
|
182
|
+
Text.new(v)
|
183
|
+
else
|
184
|
+
raise TypeError, "invalid value: #{v.inspect}"
|
185
|
+
end
|
186
|
+
}
|
187
|
+
if !hash.include?(index)
|
188
|
+
hash[index] = []
|
189
|
+
end
|
190
|
+
hash[index].concat value
|
191
|
+
}
|
192
|
+
|
193
|
+
attrs = []
|
194
|
+
@stag.attributes.each {|k, v|
|
195
|
+
if hash.include? k
|
196
|
+
v = hash[k]
|
197
|
+
if !v.empty?
|
198
|
+
attrs << {k=>Text.concat(*v)}
|
199
|
+
end
|
200
|
+
hash.delete k
|
201
|
+
else
|
202
|
+
attrs << {k=>v}
|
203
|
+
end
|
204
|
+
}
|
205
|
+
hash.keys.each {|k|
|
206
|
+
if Name === k
|
207
|
+
v = hash[k]
|
208
|
+
if !v.empty?
|
209
|
+
attrs << {k=>Text.concat(*v)}
|
210
|
+
end
|
211
|
+
hash.delete k
|
212
|
+
end
|
213
|
+
}
|
214
|
+
|
215
|
+
children_left = []
|
216
|
+
children = @children.dup
|
217
|
+
children_right = []
|
218
|
+
|
219
|
+
hash.keys.sort.each {|index|
|
220
|
+
value = hash[index]
|
221
|
+
if index < 0
|
222
|
+
children_left << value
|
223
|
+
elsif children.length <= index
|
224
|
+
children_right << value
|
225
|
+
else
|
226
|
+
children[index] = value
|
227
|
+
end
|
228
|
+
}
|
229
|
+
|
230
|
+
children = [children_left, children, children_right].flatten
|
231
|
+
|
232
|
+
if children.empty? && @empty
|
233
|
+
Elem.new(
|
234
|
+
@stag.element_name,
|
235
|
+
@stag.context,
|
236
|
+
*attrs)
|
237
|
+
else
|
238
|
+
Elem.new(
|
239
|
+
@stag.element_name,
|
240
|
+
@stag.context,
|
241
|
+
children,
|
242
|
+
*attrs)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
module Elem::Trav
|
248
|
+
private
|
249
|
+
def update_attribute_hash
|
250
|
+
if defined?(@attribute_hash)
|
251
|
+
@attribute_hash
|
252
|
+
else
|
253
|
+
h = {}
|
254
|
+
each_attribute {|name, text|
|
255
|
+
h[name.universal_name] = text
|
256
|
+
}
|
257
|
+
@attribute_hash = h
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
# :startdoc:
|