feedtools 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/CHANGELOG +11 -0
  2. data/lib/feed_tools.rb +2496 -810
  3. data/lib/feed_tools/vendor/builder.rb +2 -0
  4. data/lib/feed_tools/vendor/builder/blankslate.rb +2 -0
  5. data/lib/feed_tools/vendor/builder/xmlbase.rb +2 -1
  6. data/lib/feed_tools/vendor/builder/xmlevents.rb +2 -0
  7. data/lib/feed_tools/vendor/builder/xmlmarkup.rb +4 -2
  8. data/lib/feed_tools/vendor/htree.rb +97 -0
  9. data/lib/feed_tools/vendor/htree/container.rb +10 -0
  10. data/lib/feed_tools/vendor/htree/context.rb +67 -0
  11. data/lib/feed_tools/vendor/htree/display.rb +27 -0
  12. data/lib/feed_tools/vendor/htree/doc.rb +149 -0
  13. data/lib/feed_tools/vendor/htree/elem.rb +262 -0
  14. data/lib/feed_tools/vendor/htree/encoder.rb +163 -0
  15. data/lib/feed_tools/vendor/htree/equality.rb +218 -0
  16. data/lib/feed_tools/vendor/htree/extract_text.rb +37 -0
  17. data/lib/feed_tools/vendor/htree/fstr.rb +33 -0
  18. data/lib/feed_tools/vendor/htree/gencode.rb +97 -0
  19. data/lib/feed_tools/vendor/htree/htmlinfo.rb +672 -0
  20. data/lib/feed_tools/vendor/htree/inspect.rb +108 -0
  21. data/lib/feed_tools/vendor/htree/leaf.rb +94 -0
  22. data/lib/feed_tools/vendor/htree/loc.rb +367 -0
  23. data/lib/feed_tools/vendor/htree/modules.rb +48 -0
  24. data/lib/feed_tools/vendor/htree/name.rb +124 -0
  25. data/lib/feed_tools/vendor/htree/output.rb +207 -0
  26. data/lib/feed_tools/vendor/htree/parse.rb +407 -0
  27. data/lib/feed_tools/vendor/htree/raw_string.rb +124 -0
  28. data/lib/feed_tools/vendor/htree/regexp-util.rb +15 -0
  29. data/lib/feed_tools/vendor/htree/rexml.rb +130 -0
  30. data/lib/feed_tools/vendor/htree/scan.rb +166 -0
  31. data/lib/feed_tools/vendor/htree/tag.rb +111 -0
  32. data/lib/feed_tools/vendor/htree/template.rb +909 -0
  33. data/lib/feed_tools/vendor/htree/text.rb +115 -0
  34. data/lib/feed_tools/vendor/htree/traverse.rb +465 -0
  35. data/rakefile +1 -1
  36. data/test/rss_test.rb +97 -0
  37. metadata +30 -1
@@ -9,5 +9,7 @@
9
9
  # above copyright notice is included.
10
10
  #++
11
11
 
12
+ # :stopdoc:
12
13
  require 'builder/xmlmarkup'
13
14
  require 'builder/xmlevents'
15
+ # :startdoc:
@@ -8,6 +8,7 @@
8
8
  # above copyright notice is included.
9
9
  #++
10
10
 
11
+ # :stopdoc:
11
12
  module Builder #:nodoc:
12
13
 
13
14
  # BlankSlate provides an abstract base class with no predefined
@@ -51,3 +52,4 @@ class Object #:nodoc:
51
52
  end
52
53
  end
53
54
  end
55
+ # :startdoc:
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
-
2
+ # :stopdoc:
3
3
  require 'builder/blankslate'
4
4
 
5
5
  module Builder #:nodoc:
@@ -141,3 +141,4 @@ module Builder #:nodoc:
141
141
  end
142
142
  end
143
143
  end
144
+ # :startdoc:
@@ -9,6 +9,7 @@
9
9
  # above copyright notice is included.
10
10
  #++
11
11
 
12
+ # :stopdoc:
12
13
  require 'builder/xmlmarkup'
13
14
 
14
15
  module Builder
@@ -61,3 +62,4 @@ module Builder
61
62
  end
62
63
 
63
64
  end
65
+ # :startdoc:
@@ -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,10 @@
1
+ # :stopdoc:
2
+ require 'htree/modules'
3
+
4
+ module HTree::Container # :nodoc:
5
+ # +children+ returns children nodes as an array.
6
+ def children
7
+ @children.dup
8
+ end
9
+ end
10
+ # :startdoc:
@@ -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: