feedtools 0.2.26 → 0.2.27

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/CHANGELOG +232 -216
  2. data/db/migration.rb +2 -0
  3. data/db/schema.mysql.sql +2 -0
  4. data/db/schema.postgresql.sql +3 -1
  5. data/db/schema.sqlite.sql +3 -1
  6. data/lib/feed_tools.rb +37 -14
  7. data/lib/feed_tools/database_feed_cache.rb +13 -2
  8. data/lib/feed_tools/feed.rb +430 -104
  9. data/lib/feed_tools/feed_item.rb +533 -268
  10. data/lib/feed_tools/helpers/generic_helper.rb +1 -1
  11. data/lib/feed_tools/helpers/html_helper.rb +78 -116
  12. data/lib/feed_tools/helpers/retrieval_helper.rb +33 -3
  13. data/lib/feed_tools/helpers/uri_helper.rb +46 -54
  14. data/lib/feed_tools/monkey_patch.rb +27 -1
  15. data/lib/feed_tools/vendor/html5/History.txt +10 -0
  16. data/lib/feed_tools/vendor/html5/Manifest.txt +117 -0
  17. data/lib/feed_tools/vendor/html5/README +45 -0
  18. data/lib/feed_tools/vendor/html5/Rakefile.rb +33 -0
  19. data/lib/feed_tools/vendor/html5/bin/html5 +217 -0
  20. data/lib/feed_tools/vendor/html5/lib/core_ext/string.rb +17 -0
  21. data/lib/feed_tools/vendor/html5/lib/html5.rb +13 -0
  22. data/lib/feed_tools/vendor/html5/lib/html5/constants.rb +1046 -0
  23. data/lib/feed_tools/vendor/html5/lib/html5/filters/base.rb +10 -0
  24. data/lib/feed_tools/vendor/html5/lib/html5/filters/inject_meta_charset.rb +82 -0
  25. data/lib/feed_tools/vendor/html5/lib/html5/filters/iso639codes.rb +752 -0
  26. data/lib/feed_tools/vendor/html5/lib/html5/filters/optionaltags.rb +198 -0
  27. data/lib/feed_tools/vendor/html5/lib/html5/filters/rfc2046.rb +30 -0
  28. data/lib/feed_tools/vendor/html5/lib/html5/filters/rfc3987.rb +89 -0
  29. data/lib/feed_tools/vendor/html5/lib/html5/filters/sanitizer.rb +15 -0
  30. data/lib/feed_tools/vendor/html5/lib/html5/filters/validator.rb +830 -0
  31. data/lib/feed_tools/vendor/html5/lib/html5/filters/whitespace.rb +36 -0
  32. data/lib/feed_tools/vendor/html5/lib/html5/html5parser.rb +248 -0
  33. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/after_body_phase.rb +46 -0
  34. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/after_frameset_phase.rb +33 -0
  35. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/after_head_phase.rb +50 -0
  36. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/before_head_phase.rb +41 -0
  37. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_body_phase.rb +613 -0
  38. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_caption_phase.rb +69 -0
  39. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_cell_phase.rb +78 -0
  40. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_column_group_phase.rb +55 -0
  41. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_frameset_phase.rb +57 -0
  42. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_head_phase.rb +138 -0
  43. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_row_phase.rb +89 -0
  44. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_select_phase.rb +85 -0
  45. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_table_body_phase.rb +86 -0
  46. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_table_phase.rb +115 -0
  47. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/initial_phase.rb +133 -0
  48. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/phase.rb +154 -0
  49. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/root_element_phase.rb +41 -0
  50. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/trailing_end_phase.rb +35 -0
  51. data/lib/feed_tools/vendor/html5/lib/html5/inputstream.rb +648 -0
  52. data/lib/feed_tools/vendor/html5/lib/html5/liberalxmlparser.rb +158 -0
  53. data/lib/feed_tools/vendor/html5/lib/html5/sanitizer.rb +188 -0
  54. data/lib/feed_tools/vendor/html5/lib/html5/serializer.rb +2 -0
  55. data/lib/feed_tools/vendor/html5/lib/html5/serializer/htmlserializer.rb +179 -0
  56. data/lib/feed_tools/vendor/html5/lib/html5/serializer/xhtmlserializer.rb +20 -0
  57. data/lib/feed_tools/vendor/html5/lib/html5/sniffer.rb +45 -0
  58. data/lib/feed_tools/vendor/html5/lib/html5/tokenizer.rb +966 -0
  59. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders.rb +24 -0
  60. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders/base.rb +334 -0
  61. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders/hpricot.rb +231 -0
  62. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders/rexml.rb +209 -0
  63. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders/simpletree.rb +185 -0
  64. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers.rb +26 -0
  65. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers/base.rb +162 -0
  66. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers/hpricot.rb +48 -0
  67. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers/rexml.rb +48 -0
  68. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers/simpletree.rb +48 -0
  69. data/lib/feed_tools/vendor/html5/lib/html5/version.rb +3 -0
  70. data/lib/feed_tools/vendor/html5/testdata/encoding/chardet/test_big5.txt +51 -0
  71. data/lib/feed_tools/vendor/html5/testdata/encoding/test-yahoo-jp.dat +10 -0
  72. data/lib/feed_tools/vendor/html5/testdata/encoding/tests1.dat +394 -0
  73. data/lib/feed_tools/vendor/html5/testdata/encoding/tests2.dat +81 -0
  74. data/lib/feed_tools/vendor/html5/testdata/sanitizer/tests1.dat +416 -0
  75. data/lib/feed_tools/vendor/html5/testdata/serializer/core.test +104 -0
  76. data/lib/feed_tools/vendor/html5/testdata/serializer/injectmeta.test +65 -0
  77. data/lib/feed_tools/vendor/html5/testdata/serializer/optionaltags.test +900 -0
  78. data/lib/feed_tools/vendor/html5/testdata/serializer/options.test +60 -0
  79. data/lib/feed_tools/vendor/html5/testdata/serializer/whitespace.test +51 -0
  80. data/lib/feed_tools/vendor/html5/testdata/sites/google-results.htm +1 -0
  81. data/lib/feed_tools/vendor/html5/testdata/sites/python-ref-import.htm +1 -0
  82. data/lib/feed_tools/vendor/html5/testdata/sites/web-apps-old.htm +1 -0
  83. data/lib/feed_tools/vendor/html5/testdata/sites/web-apps.htm +34275 -0
  84. data/lib/feed_tools/vendor/html5/testdata/sniffer/htmlOrFeed.json +43 -0
  85. data/lib/feed_tools/vendor/html5/testdata/tokenizer/contentModelFlags.test +48 -0
  86. data/lib/feed_tools/vendor/html5/testdata/tokenizer/entities.test +2339 -0
  87. data/lib/feed_tools/vendor/html5/testdata/tokenizer/escapeFlag.test +21 -0
  88. data/lib/feed_tools/vendor/html5/testdata/tokenizer/test1.test +172 -0
  89. data/lib/feed_tools/vendor/html5/testdata/tokenizer/test2.test +129 -0
  90. data/lib/feed_tools/vendor/html5/testdata/tokenizer/test3.test +367 -0
  91. data/lib/feed_tools/vendor/html5/testdata/tokenizer/test4.test +198 -0
  92. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests1.dat +1950 -0
  93. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests2.dat +773 -0
  94. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests3.dat +270 -0
  95. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests4.dat +60 -0
  96. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests5.dat +175 -0
  97. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests6.dat +196 -0
  98. data/lib/feed_tools/vendor/html5/testdata/validator/attributes.test +1035 -0
  99. data/lib/feed_tools/vendor/html5/testdata/validator/base-href-attribute.test +787 -0
  100. data/lib/feed_tools/vendor/html5/testdata/validator/base-target-attribute.test +35 -0
  101. data/lib/feed_tools/vendor/html5/testdata/validator/blockquote-cite-attribute.test +7 -0
  102. data/lib/feed_tools/vendor/html5/testdata/validator/classattribute.test +152 -0
  103. data/lib/feed_tools/vendor/html5/testdata/validator/contenteditableattribute.test +59 -0
  104. data/lib/feed_tools/vendor/html5/testdata/validator/contextmenuattribute.test +115 -0
  105. data/lib/feed_tools/vendor/html5/testdata/validator/dirattribute.test +59 -0
  106. data/lib/feed_tools/vendor/html5/testdata/validator/draggableattribute.test +63 -0
  107. data/lib/feed_tools/vendor/html5/testdata/validator/html-xmlns-attribute.test +23 -0
  108. data/lib/feed_tools/vendor/html5/testdata/validator/idattribute.test +115 -0
  109. data/lib/feed_tools/vendor/html5/testdata/validator/inputattributes.test +2795 -0
  110. data/lib/feed_tools/vendor/html5/testdata/validator/irrelevantattribute.test +63 -0
  111. data/lib/feed_tools/vendor/html5/testdata/validator/langattribute.test +5579 -0
  112. data/lib/feed_tools/vendor/html5/testdata/validator/li-value-attribute.test +7 -0
  113. data/lib/feed_tools/vendor/html5/testdata/validator/link-href-attribute.test +7 -0
  114. data/lib/feed_tools/vendor/html5/testdata/validator/link-hreflang-attribute.test +7 -0
  115. data/lib/feed_tools/vendor/html5/testdata/validator/link-rel-attribute.test +271 -0
  116. data/lib/feed_tools/vendor/html5/testdata/validator/ol-start-attribute.test +7 -0
  117. data/lib/feed_tools/vendor/html5/testdata/validator/starttags.test +375 -0
  118. data/lib/feed_tools/vendor/html5/testdata/validator/style-scoped-attribute.test +7 -0
  119. data/lib/feed_tools/vendor/html5/testdata/validator/tabindexattribute.test +79 -0
  120. data/lib/feed_tools/vendor/html5/tests/preamble.rb +72 -0
  121. data/lib/feed_tools/vendor/html5/tests/test_encoding.rb +35 -0
  122. data/lib/feed_tools/vendor/html5/tests/test_lxp.rb +279 -0
  123. data/lib/feed_tools/vendor/html5/tests/test_parser.rb +68 -0
  124. data/lib/feed_tools/vendor/html5/tests/test_sanitizer.rb +142 -0
  125. data/lib/feed_tools/vendor/html5/tests/test_serializer.rb +68 -0
  126. data/lib/feed_tools/vendor/html5/tests/test_sniffer.rb +27 -0
  127. data/lib/feed_tools/vendor/html5/tests/test_stream.rb +62 -0
  128. data/lib/feed_tools/vendor/html5/tests/test_tokenizer.rb +94 -0
  129. data/lib/feed_tools/vendor/html5/tests/test_treewalkers.rb +135 -0
  130. data/lib/feed_tools/vendor/html5/tests/test_validator.rb +31 -0
  131. data/lib/feed_tools/vendor/html5/tests/tokenizer_test_parser.rb +63 -0
  132. data/lib/feed_tools/vendor/uri.rb +781 -0
  133. data/lib/feed_tools/version.rb +1 -1
  134. data/rakefile +27 -6
  135. data/test/unit/atom_test.rb +298 -210
  136. data/test/unit/helper_test.rb +7 -12
  137. data/test/unit/rdf_test.rb +51 -1
  138. data/test/unit/rss_test.rb +13 -3
  139. metadata +239 -116
  140. data/lib/feed_tools/vendor/htree.rb +0 -97
  141. data/lib/feed_tools/vendor/htree/container.rb +0 -10
  142. data/lib/feed_tools/vendor/htree/context.rb +0 -67
  143. data/lib/feed_tools/vendor/htree/display.rb +0 -27
  144. data/lib/feed_tools/vendor/htree/doc.rb +0 -149
  145. data/lib/feed_tools/vendor/htree/elem.rb +0 -262
  146. data/lib/feed_tools/vendor/htree/encoder.rb +0 -163
  147. data/lib/feed_tools/vendor/htree/equality.rb +0 -218
  148. data/lib/feed_tools/vendor/htree/extract_text.rb +0 -37
  149. data/lib/feed_tools/vendor/htree/fstr.rb +0 -33
  150. data/lib/feed_tools/vendor/htree/gencode.rb +0 -97
  151. data/lib/feed_tools/vendor/htree/htmlinfo.rb +0 -672
  152. data/lib/feed_tools/vendor/htree/inspect.rb +0 -108
  153. data/lib/feed_tools/vendor/htree/leaf.rb +0 -94
  154. data/lib/feed_tools/vendor/htree/loc.rb +0 -367
  155. data/lib/feed_tools/vendor/htree/modules.rb +0 -48
  156. data/lib/feed_tools/vendor/htree/name.rb +0 -124
  157. data/lib/feed_tools/vendor/htree/output.rb +0 -207
  158. data/lib/feed_tools/vendor/htree/parse.rb +0 -409
  159. data/lib/feed_tools/vendor/htree/raw_string.rb +0 -124
  160. data/lib/feed_tools/vendor/htree/regexp-util.rb +0 -15
  161. data/lib/feed_tools/vendor/htree/rexml.rb +0 -130
  162. data/lib/feed_tools/vendor/htree/scan.rb +0 -166
  163. data/lib/feed_tools/vendor/htree/tag.rb +0 -111
  164. data/lib/feed_tools/vendor/htree/template.rb +0 -909
  165. data/lib/feed_tools/vendor/htree/text.rb +0 -115
  166. data/lib/feed_tools/vendor/htree/traverse.rb +0 -465
@@ -1,108 +0,0 @@
1
- # :stopdoc:
2
- require 'pp'
3
- require 'htree/doc'
4
- require 'htree/elem'
5
- require 'htree/leaf'
6
- require 'htree/tag'
7
- require 'htree/output'
8
- require 'htree/raw_string'
9
-
10
- module HTree
11
- class Doc
12
- def pretty_print(q)
13
- q.object_group(self) { @children.each {|elt| q.breakable; q.pp elt } }
14
- end
15
- alias inspect pretty_print_inspect
16
- end
17
-
18
- class Elem
19
- def pretty_print(q)
20
- if @empty
21
- q.group(1, '{emptyelem', '}') {
22
- q.breakable; q.pp @stag
23
- }
24
- else
25
- q.group(1, "{elem", "}") {
26
- q.breakable; q.pp @stag
27
- @children.each {|elt| q.breakable; q.pp elt }
28
- if @etag
29
- q.breakable; q.pp @etag
30
- end
31
- }
32
- end
33
- end
34
- alias inspect pretty_print_inspect
35
- end
36
-
37
- module Leaf
38
- def pretty_print(q)
39
- q.group(1, '{', '}') {
40
- q.text self.class.name.sub(/.*::/,'').downcase
41
- if rs = @raw_string
42
- rs.scan(/[^\r\n]*(?:\r\n?|\n|[^\r\n]\z)/) {|line|
43
- q.breakable
44
- q.pp line
45
- }
46
- elsif self.respond_to? :display_xml
47
- q.breakable
48
- q.text self.display_xml('')
49
- end
50
- }
51
- end
52
- alias inspect pretty_print_inspect
53
- end
54
-
55
- class Name
56
- def inspect
57
- if xmlns?
58
- @local_name ? "xmlns:#{@local_name}" : "xmlns"
59
- elsif !@namespace_uri || @namespace_uri.empty?
60
- @local_name
61
- elsif @namespace_prefix
62
- "#{@namespace_prefix}{#{@namespace_uri}}#{@local_name}"
63
- elsif @namespace_prefix == false
64
- "-{#{@namespace_uri}}#{@local_name}"
65
- else
66
- "{#{@namespace_uri}}#{@local_name}"
67
- end
68
- end
69
- end
70
-
71
- class STag
72
- def pretty_print(q)
73
- q.group(1, '<', '>') {
74
- q.text @name.inspect
75
-
76
- @attributes.each {|n, t|
77
- q.breakable
78
- q.text "#{n.inspect}=\"#{t.to_attvalue_content}\""
79
- }
80
- }
81
- end
82
- alias inspect pretty_print_inspect
83
- end
84
-
85
- class ETag
86
- def pretty_print(q)
87
- q.group(1, '</', '>') {
88
- q.text @qualified_name
89
- }
90
- end
91
- alias inspect pretty_print_inspect
92
- end
93
-
94
- class BogusETag
95
- def pretty_print(q)
96
- q.group(1, '{', '}') {
97
- q.text self.class.name.sub(/.*::/,'').downcase
98
- if rs = @raw_string
99
- q.breakable
100
- q.text rs
101
- else
102
- q.text "</#{@qualified_name}>"
103
- end
104
- }
105
- end
106
- end
107
- end
108
- # :startdoc:
@@ -1,94 +0,0 @@
1
- # :stopdoc:
2
- require 'htree/modules'
3
- require 'htree/raw_string'
4
-
5
- module HTree # :nodoc:
6
- class XMLDecl # :nodoc:
7
- def initialize(version, encoding=nil, standalone=nil)
8
- init_raw_string
9
- if /\A[a-zA-Z0-9_.:-]+\z/ !~ version
10
- raise HTree::Error, "invalid version in XML declaration: #{version.inspect}"
11
- end
12
- if encoding && /\A[A-Za-z][A-Za-z0-9._-]*\z/ !~ encoding
13
- raise HTree::Error, "invalid encoding in XML declaration: #{encoding.inspect}"
14
- end
15
- unless standalone == nil || standalone == true || standalone == false
16
- raise HTree::Error, "invalid standalone document declaration in XML declaration: #{standalone.inspect}"
17
- end
18
- @version = version
19
- @encoding = encoding
20
- @standalone = standalone
21
- end
22
- attr_reader :version, :encoding, :standalone
23
- end
24
-
25
- class DocType # :nodoc:
26
- def initialize(root_element_name, public_identifier=nil, system_identifier=nil)
27
- init_raw_string
28
- if public_identifier && /\A[ \x0d\x0aa-zA-Z0-9\-'()+,.\/:=?;!*\#@$_%]*\z/ !~ public_identifier
29
- raise HTree::Error, "invalid public identifier in document type declaration: #{public_identifier.inspect}"
30
- end
31
- if system_identifier && /"/ =~ system_identifier && /'/ =~ system_identifier
32
- raise HTree::Error, "invalid system identifier in document type declaration: #{system_identifier.inspect}"
33
- end
34
-
35
- @root_element_name = root_element_name
36
- @public_identifier = public_identifier
37
- @system_identifier = system_identifier
38
- end
39
- attr_reader :root_element_name, :public_identifier, :system_identifier
40
- end
41
-
42
- class ProcIns # :nodoc:
43
-
44
- class << self
45
- alias new! new
46
- end
47
-
48
-
49
- def ProcIns.new(target, content)
50
- content = content.gsub(/\?>/, '? >') if content
51
- new! target, content
52
- end
53
-
54
- def initialize(target, content) # :notnew:
55
- init_raw_string
56
- if content && /\?>/ =~ content
57
- raise HTree::Error, "invalid processing instruction content: #{content.inspect}"
58
- end
59
- @target = target
60
- @content = content
61
- end
62
- attr_reader :target, :content
63
- end
64
-
65
- class Comment # :nodoc:
66
-
67
- class << self
68
- alias new! new
69
- end
70
-
71
-
72
- def Comment.new(content)
73
- content = content.gsub(/-(-+)/) { '-' + ' -' * $1.length }.sub(/-\z/, '- ')
74
- new! content
75
- end
76
-
77
- def initialize(content) # :notnew:
78
- init_raw_string
79
- if /--/ =~ content || /-\z/ =~ content
80
- raise HTree::Error, "invalid comment content: #{content.inspect}"
81
- end
82
- @content = content
83
- end
84
- attr_reader :content
85
- end
86
-
87
- class BogusETag # :nodoc:
88
- def initialize(qualified_name)
89
- init_raw_string
90
- @etag = ETag.new(qualified_name)
91
- end
92
- end
93
- end
94
- # :startdoc:
@@ -1,367 +0,0 @@
1
- # :stopdoc:
2
- require 'htree/modules'
3
- require 'htree/elem'
4
- require 'htree/inspect'
5
-
6
- module HTree # :nodoc:
7
- module Node
8
- # creates a location object which points to self.
9
- def make_loc
10
- self.class::Loc.new(nil, nil, self)
11
- end
12
-
13
- # return self.
14
- def to_node
15
- self
16
- end
17
-
18
- # +subst+ substitutes several subtrees at once.
19
- #
20
- # t = HTree('<r><x/><y/><z/></r>')
21
- # l = t.make_loc
22
- # t2 = t.subst({
23
- # l.get_subnode(0, 'k') => 'v',
24
- # l.get_subnode(0, -1) => HTree('<a/>'),
25
- # l.get_subnode(0, 1) => nil,
26
- # l.get_subnode(0, 2, 0) => HTree('<b/>'),
27
- # })
28
- # pp t2
29
- # # =>
30
- # #<HTree::Doc
31
- # {elem <r k="v"> {emptyelem <a>} {emptyelem <x>} {elem <z> {emptyelem <b>}}}>
32
- def subst(pairs)
33
- pairs = pairs.map {|key, val|
34
- key = key.index_list(self)
35
- unless Array === val
36
- val = [val]
37
- end
38
- [key, val]
39
- }
40
-
41
- pairs_empty_key, pairs_nonempty_key =
42
- pairs.partition {|key, val| key.empty? }
43
- if !pairs_empty_key.empty?
44
- if !pairs_nonempty_key.empty?
45
- raise ArgumentError, "cannot substitute a node under substituting tree."
46
- end
47
- result = []
48
- pairs_empty_key.each {|key, val| result.concat val }
49
- result.compact!
50
- if result.length == 1
51
- return result[0]
52
- else
53
- raise ArgumentError, "cannot substitute top node by multiple nodes: #{nodes.inspect}"
54
- end
55
- end
56
- if pairs_nonempty_key.empty?
57
- return self
58
- end
59
-
60
- subst_internal(pairs)
61
- end
62
-
63
- def subst_internal(pairs) # :nodoc:
64
- subnode_pairs = {}
65
- pairs.each {|key, val|
66
- k = key.pop
67
- (subnode_pairs[k] ||= []) << [key, val]
68
- }
69
- subnode_pairs = subnode_pairs.map {|k, subpairs|
70
- s = get_subnode(k)
71
- subpairs_empty_key, subpairs_nonempty_key =
72
- subpairs.partition {|key, val| key.empty? }
73
- if !subpairs_empty_key.empty?
74
- if !subpairs_nonempty_key.empty?
75
- raise ArgumentError, "cannot substitute a node under substituting tree."
76
- end
77
- r = []
78
- subpairs_empty_key.each {|key, val| r.concat val }
79
- [k, r.compact]
80
- elsif subpairs_nonempty_key.empty?
81
- [k, s]
82
- else
83
- [k, s.subst_internal(subpairs)]
84
- end
85
- }
86
- subst_subnode(subnode_pairs)
87
- end
88
- end
89
-
90
- class Doc; def node_test_string() 'doc()' end end
91
- class Elem; def node_test_string() @stag.element_name.qualified_name end end
92
- class Text; def node_test_string() 'text()' end end
93
- class BogusETag; def node_test_string() 'bogus-etag()' end end
94
- class XMLDecl; def node_test_string() 'xml-declaration()' end end
95
- class DocType; def node_test_string() 'doctype()' end end
96
- class ProcIns; def node_test_string() 'processing-instruction()' end end
97
- class Comment; def node_test_string() 'comment()' end end
98
-
99
- module Container
100
- def find_loc_step(index)
101
- if index < 0 || @children.length <= index
102
- return "*[#{index}]"
103
- end
104
-
105
- return @loc_step_children[index].dup if defined? @loc_step_children
106
-
107
- count = {}
108
- count.default = 0
109
-
110
- steps = []
111
-
112
- @children.each {|c|
113
- node_test = c.node_test_string
114
- count[node_test] += 1
115
- steps << [node_test, count[node_test]]
116
- }
117
-
118
- @loc_step_children = []
119
- steps.each {|node_test, i|
120
- if count[node_test] == 1
121
- @loc_step_children << node_test
122
- else
123
- @loc_step_children << "#{node_test}[#{i}]"
124
- end
125
- }
126
-
127
- return @loc_step_children[index].dup
128
- end
129
- end
130
-
131
- class Elem
132
- def find_loc_step(index)
133
- return super if Integer === index
134
- if String === index
135
- index = Name.parse_attribute_name(index, DefaultContext)
136
- end
137
- unless Name === index
138
- raise TypeError, "invalid index: #{index.inspect}"
139
- end
140
- "@#{index.qualified_name}"
141
- end
142
- end
143
- end
144
-
145
- class HTree::Location # :nodoc:
146
- def initialize(parent, index, node) # :nodoc:
147
- if parent
148
- @parent = parent
149
- @index = index
150
- @node = parent.node.get_subnode(index)
151
- if !@node.equal?(node)
152
- raise ArgumentError, "unexpected node"
153
- end
154
- else
155
- @parent = nil
156
- @index = nil
157
- @node = node
158
- end
159
- if @node && self.class != @node.class::Loc
160
- raise ArgumentError, "invalid location class: #{self.class} should be #{node.class::Loc}"
161
- end
162
- @subloc = {}
163
- end
164
- attr_reader :parent, :index, :node
165
- alias to_node node
166
-
167
- # return self.
168
- def make_loc
169
- self
170
- end
171
-
172
- # +top+ returns the originator location.
173
- #
174
- # t = HTree('<a><b><c><d>')
175
- # l = t.make_loc.get_subnode(0, 0, 0, 0)
176
- # p l, l.top
177
- # # =>
178
- # #<HTree::Location: doc()/a/b/c/d>
179
- # #<HTree::Location: doc()>
180
- def top
181
- result = self
182
- while result.parent
183
- result = result.parent
184
- end
185
- result
186
- end
187
-
188
- # +subst_itself+ substitutes the node pointed by the location.
189
- # It returns the location of substituted node.
190
- #
191
- # t1 = HTree('<a><b><c><d>')
192
- # p t1
193
- # l1 = t1.make_loc.get_subnode(0, 0, 0, 0)
194
- # p l1
195
- # l2 = l1.subst_itself(HTree('<z/>'))
196
- # p l2
197
- # t2 = l2.top.to_node
198
- # p t2
199
- # # =>
200
- # #<HTree::Doc {elem <a> {elem <b> {elem <c> {emptyelem <d>}}}}>
201
- # #<HTree::Location: doc()/a/b/c/d>
202
- # #<HTree::Location: doc()/a/b/c/z>
203
- # #<HTree::Doc {elem <a> {elem <b> {elem <c> {emptyelem <z>}}}}>
204
- #
205
- def subst_itself(node)
206
- if @parent
207
- new_index = @index
208
- if !@node
209
- if Integer === @index
210
- if @index < 0
211
- new_index = 0
212
- elsif @parent.to_node.children.length < @index
213
- new_index = @parent.to_node.children.length
214
- end
215
- end
216
- end
217
- @parent.subst_itself(@parent.to_node.subst_subnode({@index=>node})).get_subnode(new_index)
218
- else
219
- node.make_loc
220
- end
221
- end
222
-
223
- # +subst+ substitutes several subtrees at once.
224
- #
225
- # t = HTree('<r><x/><y/><z/></r>')
226
- # l = t.make_loc
227
- # l2 = l.subst({
228
- # l.root.get_subnode('k') => 'v',
229
- # l.root.get_subnode(-1) => HTree('<a/>'),
230
- # l.find_element('y') => nil,
231
- # l.find_element('z').get_subnode(0) => HTree('<b/>'),
232
- # })
233
- # pp l2, l2.to_node
234
- # # =>
235
- # #<HTree::Doc::Loc: doc()>
236
- # #<HTree::Doc
237
- # {elem <r k="v"> {emptyelem <a>} {emptyelem <x>} {elem <z> {emptyelem <b>}}}>
238
- def subst(pairs)
239
- subst_itself(@node.subst(pairs))
240
- end
241
-
242
- # +loc_list+ returns an array containing from location's root to itself.
243
- #
244
- # t = HTree('<a><b><c>')
245
- # l = t.make_loc.get_subnode(0, 0, 0)
246
- # pp l, l.loc_list
247
- # # =>
248
- # #<HTree::Location: doc()/a/b/c>
249
- # [#<HTree::Location: doc()>,
250
- # #<HTree::Location: doc()/a>,
251
- # #<HTree::Location: doc()/a/b>,
252
- # #<HTree::Location: doc()/a/b/c>]
253
- #
254
- def loc_list
255
- loc = self
256
- result = [self]
257
- while loc = loc.parent
258
- result << loc
259
- end
260
- result.reverse!
261
- result
262
- end
263
-
264
- # +path+ returns the path of the location.
265
- #
266
- # l = HTree.parse("<a><b>x</b><b/><a/>").make_loc
267
- # l = l.get_subnode(0, 0, 0)
268
- # p l.path # => "doc()/a/b[1]/text()"
269
- def path
270
- result = ''
271
- loc_list.each {|loc|
272
- if parent = loc.parent
273
- result << '/' << parent.node.find_loc_step(loc.index)
274
- else
275
- result << loc.node.node_test_string
276
- end
277
- }
278
- result
279
- end
280
-
281
- def index_list(node) # :nodoc:
282
- result = []
283
- loc = self
284
- while parent = loc.parent
285
- return result if loc.to_node.equal? node
286
- result << loc.index
287
- loc = parent
288
- end
289
- return result if loc.to_node.equal? node
290
- raise ArgumentError, "the location is not under the node: #{self.path}"
291
- end
292
-
293
- def pretty_print(q)
294
- q.group(1, "#<#{self.class.name}", '>') {
295
- q.text ':'
296
- q.breakable
297
- loc_list.each {|loc|
298
- if parent = loc.parent
299
- q.text '/'
300
- q.group { q.breakable '' }
301
- q.text parent.node.find_loc_step(loc.index)
302
- else
303
- q.text loc.node.node_test_string
304
- end
305
- }
306
- }
307
- end
308
- alias inspect pretty_print_inspect
309
- end
310
-
311
- module HTree::Container::Loc # :nodoc:
312
- # +get_subnode+ returns a location object which points to a subnode
313
- # indexed by _index_.
314
- def get_subnode_internal(index) # :nodoc:
315
- return @subloc[index] if @subloc.include? index
316
- node = @node.get_subnode(index)
317
- if node
318
- @subloc[index] = node.class::Loc.new(self, index, node)
319
- else
320
- @subloc[index] = HTree::Location.new(self, index, node)
321
- end
322
- end
323
-
324
- # +subst_subnode+ returns the location which refers the substituted tree.
325
- # loc.subst_subnode(pairs) -> loc
326
- #
327
- # t = HTree('<a><b><c>')
328
- # l = t.make_loc.get_subnode(0, 0)
329
- # l = l.subst_subnode({0=>HTree('<z/>')})
330
- # pp t, l.top.to_node
331
- # # =>
332
- # #<HTree::Doc {elem <a> {elem <b> {emptyelem <c>}}}>
333
- # #<HTree::Doc {elem <a> {elem <b> {emptyelem <z>}}}>
334
- #
335
- def subst_subnode(pairs)
336
- self.subst_itself(@node.subst_subnode(pairs))
337
- end
338
-
339
- # +children+ returns an array of child locations.
340
- def children
341
- (0...@node.children.length).map {|i| get_subnode(i) }
342
- end
343
- end
344
-
345
- class HTree::Elem::Loc # :nodoc:
346
- def context() @node.context end
347
-
348
- # +element_name+ returns the name of the element name as a Name object.
349
- def element_name() @node.element_name end
350
-
351
- def empty_element?() @node.empty_element? end
352
-
353
- # +each_attribute+ iterates over each attributes.
354
- def each_attribute
355
- @node.each_attribute {|attr_name, attr_text|
356
- attr_loc = get_subnode(attr_name)
357
- yield attr_name, attr_loc
358
- }
359
- end
360
- end
361
-
362
- class HTree::Text::Loc # :nodoc:
363
- def to_s() @node.to_s end
364
- def strip() @node.strip end
365
- def empty?() @node.empty? end
366
- end
367
- # :startdoc: