yasuri 0.0.6 → 0.0.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 61a2aa3974c697ecc14b991521961ec54f3ff5c0
4
- data.tar.gz: 03cf5b02e7a646175183725d38d50a9538df7eed
3
+ metadata.gz: 18c8b6da6ca1f9d5433adc128b83ed5d5a8e353e
4
+ data.tar.gz: 07ba467f8d62982e4a8e969da42b839d8ee07664
5
5
  SHA512:
6
- metadata.gz: 2108d6b78c8704fa4d99491c1684dfd686c656d7bd039b8c80e52fa2150837958c25f37afabdcf198b10153b2f32216408735182fb0d58b3007f4808ac2226c9
7
- data.tar.gz: c956e589ab7676e844110870e18c8f842afb6fd566a5ddd4758ec9e328961ff1b485bac9eebecdac6456f7ed28a9793d9dd4dd9bc2f89dd8f9131003c6db9a5d
6
+ metadata.gz: 232a5893c4511b0ef80b34a95d58af4d1bb8683512a1cb41b7f9a6d19def75d3c825b12c383bf51bab09e12fe8bd54cd91b79b32640319b60d6185a46ed7f086
7
+ data.tar.gz: 4cb31e01b60861d13770b8b9d033cef35fe6bbdd226d11703826040840ac979fcfd98c162b18322baa8f9102fd2ed059ac0c15c277c49217feaf39173800dea6
data/README.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  Yasuri (鑢) is an easy web-scraping library for supporting "[Mechanize](https://github.com/sparklemotion/mechanize)".
4
4
 
5
+
6
+ ## Sample
7
+
8
+ https://yasuri-sample.herokuapp.com/
9
+
10
+ (source code: https://github.com/tac0x2a/yasuri-sample)
11
+
5
12
  ## Installation
6
13
 
7
14
  Add this line to your application's Gemfile:
@@ -1,3 +1,3 @@
1
1
  module Yasuri
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
data/lib/yasuri/yasuri.rb CHANGED
@@ -5,148 +5,38 @@
5
5
  require 'mechanize'
6
6
  require 'json'
7
7
 
8
- module Yasuri
9
-
10
- module Node
11
- attr_reader :url, :xpath, :name
12
-
13
- def initialize(xpath, name, children = [], opt: {})
14
- @xpath, @name, @children = xpath, name, children
15
- end
16
-
17
- def inject(agent, page)
18
- fail "#{Kernel.__method__} is not implemented."
19
- end
20
- end
21
-
22
- class TextNode
23
- include Node
24
- def initialize(xpath, name, children = [], truncate_regexp: nil, opt: {})
25
- super(xpath, name, children)
26
- @truncate_regexp = truncate_regexp
27
- end
28
- def inject(agent, page, retry_count = 5)
29
- node = page.search(@xpath)
30
- text = node.text.to_s
31
-
32
- text = text[@truncate_regexp, 0] if @truncate_regexp
33
-
34
- text.to_s
35
- end
36
- end
37
-
38
- class StructNode
39
- include Node
40
- def inject(agent, page, retry_count = 5)
41
- sub_tags = page.search(@xpath)
42
- sub_tags.map do |sub_tag|
43
- child_results_kv = @children.map do |child_node|
44
- [child_node.name, child_node.inject(agent, sub_tag, retry_count)]
45
- end
46
- Hash[child_results_kv]
47
- end
48
- end
49
- end
50
-
51
- class LinksNode
52
- include Node
53
- def inject(agent, page, retry_count = 5)
54
- links = page.search(@xpath) || [] # links expected
55
- links.map do |link|
56
- link_button = Mechanize::Page::Link.new(link, agent, page)
57
- child_page = Yasuri.with_retry(retry_count) { link_button.click }
58
-
59
- child_results_kv = @children.map do |child_node|
60
- [child_node.name, child_node.inject(agent, child_page, retry_count)]
61
- end
62
-
63
- Hash[child_results_kv]
64
- end # each named child node
65
- end
66
- end
67
-
68
- class PaginateNode
69
- include Node
70
-
71
- def initialize(xpath, name, children = [], limit: nil, opt: {})
72
- super(xpath, name, children)
73
- @limit = limit || opt["limit"] || Float::MAX
74
- end
75
-
76
- def inject(agent, page, retry_count = 5)
8
+ require_relative 'yasuri_node'
9
+ require_relative 'yasuri_text_node'
10
+ require_relative 'yasuri_struct_node'
11
+ require_relative 'yasuri_paginate_node'
12
+ require_relative 'yasuri_links_node'
13
+ require_relative 'yasuri_node_generator'
77
14
 
78
- child_results = []
79
- while page
80
- child_results_kv = @children.map do |child_node|
81
- [child_node.name, child_node.inject(agent, page, retry_count)]
82
- end
83
- child_results << Hash[child_results_kv]
84
-
85
- link = page.search(@xpath).first
86
- break if link == nil
87
-
88
- link_button = Mechanize::Page::Link.new(link, agent, page)
89
- page = Yasuri.with_retry(retry_count) { link_button.click }
90
- break if (@limit -= 1) <= 0
91
- end
92
-
93
- child_results
94
- end
95
- end
96
-
97
- class NodeGenerator
98
- def gen_recursive(&block)
99
- @nodes = []
100
- instance_eval(&block)
101
- @nodes
102
- end
103
-
104
- def method_missing(name, *args, &block)
105
- node = NodeGenerator.gen(name, *args, &block)
106
- raise "Undefined Node Name '#{name}'" if node == nil
107
- @nodes << node
108
- end
109
-
110
- def self.gen(name, *args, &block)
111
- xpath, opt = *args
112
- children = Yasuri::NodeGenerator.new.gen_recursive(&block) if block_given?
113
-
114
- case name
115
- when /^text_(.+)$/
116
- truncate_regexp = opt
117
- Yasuri::TextNode.new(xpath, $1, truncate_regexp)
118
- when /^struct_(.+)$/
119
- Yasuri::StructNode.new(xpath, $1, children || [])
120
- when /^links_(.+)$/
121
- Yasuri::LinksNode.new(xpath, $1, children || [])
122
- when /^pages_(.+)$/
123
- xpath, limit = *args
124
- limit = limit || Float::MAX
125
- Yasuri::PaginateNode.new(xpath, $1, children || [], limit: limit)
126
- else
127
- nil
128
- end
129
- end # of self.gen(name, *args, &block)
130
- end # of class NodeGenerator
15
+ module Yasuri
131
16
 
132
17
  def self.json2tree(json_string)
133
18
  json = JSON.parse(json_string)
134
19
  Yasuri.hash2node(json)
135
20
  end
136
21
 
22
+ def self.tree2json(node)
23
+ Yasuri.node2hash(node).to_json
24
+ end
25
+
137
26
  def self.method_missing(name, *args, &block)
138
27
  generated = Yasuri::NodeGenerator.gen(name, *args, &block)
139
28
  generated || super(name, args)
140
29
  end
141
30
 
142
-
143
31
  private
144
32
  Text2Node = {
145
- "text" => TextNode,
146
- "struct" => StructNode,
147
- "links" => LinksNode,
148
- "pages" => PaginateNode
33
+ "text" => Yasuri::TextNode,
34
+ "struct" => Yasuri::StructNode,
35
+ "links" => Yasuri::LinksNode,
36
+ "pages" => Yasuri::PaginateNode
149
37
  }
38
+ Node2Text = Text2Node.invert
39
+
150
40
  ReservedKeys = %w|node name path children|
151
41
  def self.hash2node(node_h)
152
42
  node, name, path, children = ReservedKeys.map do |key|
@@ -162,6 +52,27 @@ module Yasuri
162
52
  klass ? klass.new(path, name, childnodes, opt: opt) : nil
163
53
  end
164
54
 
55
+ def self.node2hash(node)
56
+ json = JSON.parse("{}")
57
+ return json if node.nil?
58
+
59
+ klass = node.class
60
+ klass_str = Node2Text[klass]
61
+
62
+ json["node"] = klass_str
63
+ json["name"] = node.name
64
+ json["path"] = node.xpath
65
+
66
+ children = node.children.map{|c| Yasuri.node2hash(c)}
67
+ json["children"] = children if not children.empty?
68
+
69
+ node.opts.each do |key,value|
70
+ json[key] = value if not value.nil?
71
+ end
72
+
73
+ json
74
+ end
75
+
165
76
  def self.with_retry(retry_count = 5)
166
77
  begin
167
78
  return yield() if block_given?
@@ -0,0 +1,23 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'yasuri_node'
5
+
6
+ module Yasuri
7
+ class LinksNode
8
+ include Node
9
+ def inject(agent, page, retry_count = 5)
10
+ links = page.search(@xpath) || [] # links expected
11
+ links.map do |link|
12
+ link_button = Mechanize::Page::Link.new(link, agent, page)
13
+ child_page = Yasuri.with_retry(retry_count) { link_button.click }
14
+
15
+ child_results_kv = @children.map do |child_node|
16
+ [child_node.name, child_node.inject(agent, child_page, retry_count)]
17
+ end
18
+
19
+ Hash[child_results_kv]
20
+ end # each named child node
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'yasuri_node'
5
+
6
+ module Yasuri
7
+ module Node
8
+ attr_reader :url, :xpath, :name, :children
9
+
10
+ def initialize(xpath, name, children = [], opt: {})
11
+ @xpath, @name, @children = xpath, name, children
12
+ end
13
+
14
+ def inject(agent, page)
15
+ fail "#{Kernel.__method__} is not implemented."
16
+ end
17
+ def opts
18
+ {}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'yasuri_node'
5
+ require_relative 'yasuri_text_node'
6
+ require_relative 'yasuri_struct_node'
7
+ require_relative 'yasuri_links_node'
8
+ require_relative 'yasuri_paginate_node'
9
+
10
+ module Yasuri
11
+ class NodeGenerator
12
+ def gen_recursive(&block)
13
+ @nodes = []
14
+ instance_eval(&block)
15
+ @nodes
16
+ end
17
+
18
+ def method_missing(name, *args, &block)
19
+ node = NodeGenerator.gen(name, *args, &block)
20
+ raise "Undefined Node Name '#{name}'" if node == nil
21
+ @nodes << node
22
+ end
23
+
24
+ def self.gen(name, *args, &block)
25
+ xpath, opt = *args
26
+ opt = [opt].flatten.compact
27
+ children = Yasuri::NodeGenerator.new.gen_recursive(&block) if block_given?
28
+
29
+ case name
30
+ when /^text_(.+)$/
31
+ truncate, dummy = *opt
32
+ Yasuri::TextNode.new(xpath, $1, children || [], truncate: truncate)
33
+ when /^struct_(.+)$/
34
+ Yasuri::StructNode.new(xpath, $1, children || [])
35
+ when /^links_(.+)$/
36
+ Yasuri::LinksNode.new(xpath, $1, children || [])
37
+ when /^pages_(.+)$/
38
+ limit, dummy = *opt
39
+ limit = limit || Float::MAX
40
+ Yasuri::PaginateNode.new(xpath, $1, children || [], limit: limit)
41
+ else
42
+ nil
43
+ end
44
+ end # of self.gen(name, *args, &block)
45
+ end # of class NodeGenerator
46
+ end
@@ -0,0 +1,39 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'yasuri_node'
5
+
6
+ module Yasuri
7
+ class PaginateNode
8
+ include Node
9
+
10
+ def initialize(xpath, name, children = [], limit: nil, opt: {})
11
+ super(xpath, name, children)
12
+ @limit = limit || opt["limit"]
13
+ end
14
+
15
+ def inject(agent, page, retry_count = 5)
16
+
17
+ child_results = []
18
+ limit = @limit.nil? ? Float::MAX : @limit
19
+ while page
20
+ child_results_kv = @children.map do |child_node|
21
+ [child_node.name, child_node.inject(agent, page, retry_count)]
22
+ end
23
+ child_results << Hash[child_results_kv]
24
+
25
+ link = page.search(@xpath).first
26
+ break if link == nil
27
+
28
+ link_button = Mechanize::Page::Link.new(link, agent, page)
29
+ page = Yasuri.with_retry(retry_count) { link_button.click }
30
+ break if (limit -= 1) <= 0
31
+ end
32
+
33
+ child_results
34
+ end
35
+ def opts
36
+ {limit:@limit}
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'yasuri_node'
5
+
6
+ module Yasuri
7
+ class StructNode
8
+ include Node
9
+ def inject(agent, page, retry_count = 5)
10
+ sub_tags = page.search(@xpath)
11
+ sub_tags.map do |sub_tag|
12
+ child_results_kv = @children.map do |child_node|
13
+ [child_node.name, child_node.inject(agent, sub_tag, retry_count)]
14
+ end
15
+ Hash[child_results_kv]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'yasuri_node'
5
+
6
+ module Yasuri
7
+ class TextNode
8
+ include Node
9
+ def initialize(xpath, name, children = [], truncate: nil, opt: {})
10
+ super(xpath, name, children)
11
+
12
+ truncate_opt = opt["truncate"] #str
13
+ truncate_opt = Regexp.new(truncate_opt) if not truncate_opt.nil? # regexp or nil
14
+
15
+ @truncate = truncate || truncate_opt || nil # regexp or nil
16
+
17
+ @truncate = Regexp.new(@truncate.to_s) if not @truncate.nil?
18
+
19
+ end
20
+ def inject(agent, page, retry_count = 5)
21
+ node = page.search(@xpath)
22
+ text = node.text.to_s
23
+
24
+ text = text[@truncate, 0] if @truncate
25
+
26
+ text.to_s
27
+ end
28
+ def opts
29
+ {truncate:@truncate}
30
+ end
31
+ end
32
+ end
data/spec/spec_helper.rb CHANGED
@@ -29,3 +29,9 @@ SimpleCov.start
29
29
 
30
30
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
31
31
  require 'yasuri'
32
+
33
+ def compare_generated_vs_original(generated, original, page)
34
+ expected = original.inject(@agent, page)
35
+ actual = generated.inject(@agent, page)
36
+ expect(actual).to match expected
37
+ end
@@ -0,0 +1,94 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'spec_helper'
5
+
6
+ #########
7
+ # Links #
8
+ #########
9
+ describe 'Yasuri' do
10
+ include_context 'httpserver'
11
+
12
+ describe '::LinksNode' do
13
+ before do
14
+ @agent = Mechanize.new
15
+ @uri = uri
16
+ @index_page = @agent.get(@uri)
17
+ end
18
+
19
+ it 'scrape links' do
20
+ root_node = Yasuri::LinksNode.new('/html/body/a', "root", [
21
+ Yasuri::TextNode.new('/html/body/p', "content"),
22
+ ])
23
+
24
+ actual = root_node.inject(@agent, @index_page)
25
+ expected = [
26
+ {"content" => "Child 01 page."},
27
+ {"content" => "Child 02 page."},
28
+ {"content" => "Child 03 page."},
29
+ ]
30
+ expect(actual).to match expected
31
+ end
32
+
33
+ it 'return empty set if no match node' do
34
+ missing_xpath = '/html/body/b'
35
+ root_node = Yasuri::LinksNode.new(missing_xpath, "root", [
36
+ Yasuri::TextNode.new('/html/body/p', "content"),
37
+ ])
38
+
39
+ actual = root_node.inject(@agent, @index_page)
40
+ expect(actual).to be_empty
41
+ end
42
+
43
+ it 'scrape links, recursive' do
44
+ root_node = Yasuri::LinksNode.new('/html/body/a', "root", [
45
+ Yasuri::TextNode.new('/html/body/p', "content"),
46
+ Yasuri::LinksNode.new('/html/body/ul/li/a', "sub_link", [
47
+ Yasuri::TextNode.new('/html/head/title', "sub_page_title"),
48
+ ]),
49
+ ])
50
+ actual = root_node.inject(@agent, @index_page)
51
+ expected = [
52
+ {"content" => "Child 01 page.",
53
+ "sub_link" => [{"sub_page_title" => "Child 01 SubPage Test"},
54
+ {"sub_page_title" => "Child 02 SubPage Test"}],},
55
+ {"content" => "Child 02 page.",
56
+ "sub_link" => [],},
57
+ {"content" => "Child 03 page.",
58
+ "sub_link" => [{"sub_page_title" => "Child 03 SubPage Test"}],},
59
+ ]
60
+ expect(actual).to match expected
61
+ end
62
+ it 'can be defined by DSL, return single LinkNode title' do
63
+ generated = Yasuri.links_title '/html/body/a'
64
+ original = Yasuri::LinksNode.new('/html/body/a', "title")
65
+ compare_generated_vs_original(generated, original, @index_page)
66
+ end
67
+ it 'can be defined by DSL, return nested contents under link' do
68
+ generated = Yasuri.links_title '/html/body/a' do
69
+ text_name '/html/body/p'
70
+ end
71
+ original = Yasuri::LinksNode.new('/html/body/a', "root", [
72
+ Yasuri::TextNode.new('/html/body/p', "name"),
73
+ ])
74
+ compare_generated_vs_original(generated, original, @index_page)
75
+ end
76
+
77
+ it 'can be defined by DSL, return recursive links node' do
78
+ generated = Yasuri.links_root '/html/body/a' do
79
+ text_content '/html/body/p'
80
+ links_sub_link '/html/body/ul/li/a' do
81
+ text_sub_page_title '/html/head/title'
82
+ end
83
+ end
84
+
85
+ original = Yasuri::LinksNode.new('/html/body/a', "root", [
86
+ Yasuri::TextNode.new('/html/body/p', "content"),
87
+ Yasuri::LinksNode.new('/html/body/ul/li/a', "sub_link", [
88
+ Yasuri::TextNode.new('/html/head/title', "sub_page_title"),
89
+ ]),
90
+ ])
91
+ compare_generated_vs_original(generated, original, @index_page)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,11 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'spec_helper'
5
+
6
+ ########
7
+ # Node #
8
+ ########
9
+ describe 'Yasuri' do
10
+
11
+ end
@@ -0,0 +1,85 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'spec_helper'
5
+
6
+ ############
7
+ # Paginate #
8
+ ############
9
+ describe 'Yasuri' do
10
+ include_context 'httpserver'
11
+
12
+ describe '::PaginateNode' do
13
+ before do
14
+ @agent = Mechanize.new
15
+ @uri = uri + "/pagination/page01.html"
16
+ @page = @agent.get(@uri)
17
+ end
18
+
19
+ it "scrape each paginated pages" do
20
+ root_node = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
21
+ Yasuri::TextNode.new('/html/body/p', "content"),
22
+ ])
23
+ actual = root_node.inject(@agent, @page)
24
+ expected = [
25
+ {"content" => "PaginationTest01"},
26
+ {"content" => "PaginationTest02"},
27
+ {"content" => "PaginationTest03"},
28
+ {"content" => "PaginationTest04"},
29
+ ]
30
+ expect(actual).to match expected
31
+ end
32
+
33
+ it "scrape each paginated pages limited" do
34
+ root_node = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
35
+ Yasuri::TextNode.new('/html/body/p', "content"),
36
+ ], limit:3)
37
+ actual = root_node.inject(@agent, @page)
38
+ expected = [
39
+ {"content" => "PaginationTest01"},
40
+ {"content" => "PaginationTest02"},
41
+ {"content" => "PaginationTest03"},
42
+ ]
43
+ expect(actual).to match expected
44
+ end
45
+
46
+ it 'return first content if paginate link node is not found' do
47
+ missing_xpath = "/html/body/nav/span/b[@class='next']"
48
+ root_node = Yasuri::PaginateNode.new(missing_xpath, "root", [
49
+ Yasuri::TextNode.new('/html/body/p', "content"),
50
+ ])
51
+ actual = root_node.inject(@agent, @page)
52
+ expected = [ {"content" => "PaginationTest01"}, ]
53
+ expect(actual).to match_array expected
54
+ end
55
+
56
+ it 'return empty hashes if content node is not found' do
57
+ root_node = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
58
+ Yasuri::TextNode.new('/html/body/hoge', "content"),
59
+ ])
60
+ actual = root_node.inject(@agent, @page)
61
+ expected = [ {"content" => ""}, {"content" => ""}, {"content" => ""}, {"content" => ""},]
62
+ expect(actual).to match_array expected
63
+ end
64
+
65
+ it 'can be defined by DSL, return single PaginateNode content' do
66
+ generated = Yasuri.pages_next "/html/body/nav/span/a[@class='next']" do
67
+ text_content '/html/body/p'
68
+ end
69
+ original = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
70
+ Yasuri::TextNode.new('/html/body/p', "content"),
71
+ ])
72
+ compare_generated_vs_original(generated, original, @page)
73
+ end
74
+
75
+ it 'can be defined by DSL, return single PaginateNode content limited' do
76
+ generated = Yasuri.pages_next "/html/body/nav/span/a[@class='next']", 2 do
77
+ text_content '/html/body/p'
78
+ end
79
+ original = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
80
+ Yasuri::TextNode.new('/html/body/p', "content"),
81
+ ], limit: 2)
82
+ compare_generated_vs_original(generated, original, @page)
83
+ end
84
+ end
85
+ end
data/spec/yasuri_spec.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  # Author:: TAC (tac@tac42.net)
4
4
 
5
5
  require_relative 'spec_helper'
6
+ require_relative 'yasuri_text_node_spec'
6
7
 
7
8
  #require_relative '../lib/yasuri/yasuri'
8
9
 
@@ -15,323 +16,6 @@ describe 'Yasuri' do
15
16
  @index_page = @agent.get(@uri)
16
17
  end
17
18
 
18
- ########
19
- # Node #
20
- ########
21
- def compare_generated_vs_original(generated, original, page = @index_page)
22
- expected = original.inject(@agent, page)
23
- actual = generated.inject(@agent, page)
24
- expect(actual).to match expected
25
- end
26
-
27
- ########
28
- # Text #
29
- ########
30
- describe '::TextNode' do
31
- before { @node = Yasuri::TextNode.new('/html/body/p[1]', "title") }
32
-
33
- it 'scrape text text <p>Hello,Yasuri</p>' do
34
- actual = @node.inject(@agent, @index_page)
35
- expect(actual).to eq "Hello,Yasuri"
36
- end
37
-
38
- it 'return empty text if no match node' do
39
- no_match_node = Yasuri::TextNode.new('/html/body/no_match_node', "title")
40
- actual = no_match_node.inject(@agent, @index_page)
41
- expect(actual).to be_empty
42
- end
43
-
44
- it 'fail with invalid xpath' do
45
- invalid_xpath = '/html/body/no_match_node['
46
- node = Yasuri::TextNode.new(invalid_xpath, "title")
47
- expect { node.inject(@agent, @index_page) }.to raise_error
48
- end
49
-
50
- it "can be defined by DSL, return single TextNode title" do
51
- generated = Yasuri.text_title '/html/body/p[1]'
52
- original = Yasuri::TextNode.new('/html/body/p[1]', "title")
53
- compare_generated_vs_original(generated, original)
54
- end
55
-
56
- it "can be truncated with regexp" do
57
- node = Yasuri.text_title '/html/body/p[1]', truncate_regexp:/^[^,]+/
58
- actual = node.inject(@agent, @index_page)
59
- expect(actual).to eq "Hello"
60
- end
61
-
62
- it "can be truncated with regexp" do
63
- node = Yasuri.text_title '/html/body/p[1]', truncate_regexp:/[^,]+$/
64
- actual = node.inject(@agent, @index_page)
65
- expect(actual).to eq "Yasuri"
66
- end
67
-
68
- it "return empty string if truncated with no match to regexp" do
69
- node = Yasuri.text_title '/html/body/p[1]', truncate_regexp:/^hoge/
70
- actual = node.inject(@agent, @index_page)
71
- expect(actual).to be_empty
72
- end
73
- end
74
-
75
- ##########
76
- # Struct #
77
- ##########
78
- describe '::StructNode' do
79
- before do
80
- @page = @agent.get(@uri + "/structual_text.html")
81
- @table_1996 = [
82
- { "title" => "The Perfect Insider",
83
- "pub_date" => "1996/4/5" },
84
- { "title" => "Doctors in Isolated Room",
85
- "pub_date" => "1996/7/5" },
86
- { "title" => "Mathematical Goodbye",
87
- "pub_date" => "1996/9/5" },
88
- ]
89
- @table_1997 = [
90
- { "title" => "Jack the Poetical Private",
91
- "pub_date" => "1997/1/5" },
92
- { "title" => "Who Inside",
93
- "pub_date" => "1997/4/5" },
94
- { "title" => "Illusion Acts Like Magic",
95
- "pub_date" => "1997/10/5" },
96
- ]
97
- @table_1998 = [
98
- { "title" => "Replaceable Summer",
99
- "pub_date" => "1998/1/7" },
100
- { "title" => "Switch Back",
101
- "pub_date" => "1998/4/5" },
102
- { "title" => "Numerical Models",
103
- "pub_date" => "1998/7/5" },
104
- { "title" => "The Perfect Outsider",
105
- "pub_date" => "1998/10/5" },
106
- ]
107
- @all_tables = [
108
- {"table" => @table_1996},
109
- {"table" => @table_1997},
110
- {"table" => @table_1998},
111
- ]
112
- end
113
- it 'scrape single table contents' do
114
- node = Yasuri::StructNode.new('/html/body/table[1]/tr', "table", [
115
- Yasuri::TextNode.new('./td[1]', "title"),
116
- Yasuri::TextNode.new('./td[2]', "pub_date"),
117
- ])
118
- expected = @table_1996
119
- actual = node.inject(@agent, @page)
120
- expect(actual).to match expected
121
- end
122
-
123
- it 'return empty text if no match node' do
124
- no_match_xpath = '/html/body/table[1]/t'
125
- node = Yasuri::StructNode.new(no_match_xpath, "table", [
126
- Yasuri::TextNode.new('./td[1]', "title")
127
- ])
128
- actual = node.inject(@agent, @page)
129
- expect(actual).to be_empty
130
- end
131
-
132
- it 'fail with invalid xpath' do
133
- invalid_xpath = '/html/body/table[1]/table[1]/tr['
134
- node = Yasuri::StructNode.new(invalid_xpath, "table", [
135
- Yasuri::TextNode.new('./td[1]', "title")
136
- ])
137
- expect { node.inject(@agent, @page) }.to raise_error
138
- end
139
-
140
- it 'fail with invalid xpath in children' do
141
- invalid_xpath = './td[1]['
142
- node = Yasuri::StructNode.new('/html/body/table[1]/tr', "table", [
143
- Yasuri::TextNode.new(invalid_xpath, "title"),
144
- Yasuri::TextNode.new('./td[2]', "pub_date"),
145
- ])
146
- expect { node.inject(@agent, @page) }.to raise_error
147
- end
148
-
149
- it 'scrape all tables' do
150
- node = Yasuri::StructNode.new('/html/body/table', "tables", [
151
- Yasuri::StructNode.new('./tr', "table", [
152
- Yasuri::TextNode.new('./td[1]', "title"),
153
- Yasuri::TextNode.new('./td[2]', "pub_date"),
154
- ])
155
- ])
156
- expected = @all_tables
157
- actual = node.inject(@agent, @page)
158
- expect(actual).to match expected
159
- end
160
-
161
- it 'can be defined by DSL, scrape all tables' do
162
- generated = Yasuri.struct_tables '/html/body/table' do
163
- struct_table './tr' do
164
- text_title './td[1]'
165
- text_pub_date './td[2]'
166
- end
167
- end
168
- original = Yasuri::StructNode.new('/html/body/table', "tables", [
169
- Yasuri::StructNode.new('./tr', "table", [
170
- Yasuri::TextNode.new('./td[1]', "title"),
171
- Yasuri::TextNode.new('./td[2]', "pub_date"),
172
- ])
173
- ])
174
- compare_generated_vs_original(generated, original)
175
- end
176
- end
177
-
178
- #########
179
- # Links #
180
- #########
181
- describe '::LinksNode' do
182
- it 'scrape links' do
183
- root_node = Yasuri::LinksNode.new('/html/body/a', "root", [
184
- Yasuri::TextNode.new('/html/body/p', "content"),
185
- ])
186
-
187
- actual = root_node.inject(@agent, @index_page)
188
- expected = [
189
- {"content" => "Child 01 page."},
190
- {"content" => "Child 02 page."},
191
- {"content" => "Child 03 page."},
192
- ]
193
- expect(actual).to match expected
194
- end
195
-
196
- it 'return empty set if no match node' do
197
- missing_xpath = '/html/body/b'
198
- root_node = Yasuri::LinksNode.new(missing_xpath, "root", [
199
- Yasuri::TextNode.new('/html/body/p', "content"),
200
- ])
201
-
202
- actual = root_node.inject(@agent, @index_page)
203
- expect(actual).to be_empty
204
- end
205
-
206
- it 'scrape links, recursive' do
207
- root_node = Yasuri::LinksNode.new('/html/body/a', "root", [
208
- Yasuri::TextNode.new('/html/body/p', "content"),
209
- Yasuri::LinksNode.new('/html/body/ul/li/a', "sub_link", [
210
- Yasuri::TextNode.new('/html/head/title', "sub_page_title"),
211
- ]),
212
- ])
213
- actual = root_node.inject(@agent, @index_page)
214
- expected = [
215
- {"content" => "Child 01 page.",
216
- "sub_link" => [{"sub_page_title" => "Child 01 SubPage Test"},
217
- {"sub_page_title" => "Child 02 SubPage Test"}],},
218
- {"content" => "Child 02 page.",
219
- "sub_link" => [],},
220
- {"content" => "Child 03 page.",
221
- "sub_link" => [{"sub_page_title" => "Child 03 SubPage Test"}],},
222
- ]
223
- expect(actual).to match expected
224
- end
225
- it 'can be defined by DSL, return single LinkNode title' do
226
- generated = Yasuri.links_title '/html/body/a'
227
- original = Yasuri::LinksNode.new('/html/body/a', "title")
228
- compare_generated_vs_original(generated, original)
229
- end
230
- it 'can be defined by DSL, return nested contents under link' do
231
- generated = Yasuri.links_title '/html/body/a' do
232
- text_name '/html/body/p'
233
- end
234
- original = Yasuri::LinksNode.new('/html/body/a', "root", [
235
- Yasuri::TextNode.new('/html/body/p', "name"),
236
- ])
237
- compare_generated_vs_original(generated, original)
238
- end
239
-
240
- it 'can be defined by DSL, return recursive links node' do
241
- generated = Yasuri.links_root '/html/body/a' do
242
- text_content '/html/body/p'
243
- links_sub_link '/html/body/ul/li/a' do
244
- text_sub_page_title '/html/head/title'
245
- end
246
- end
247
-
248
- original = Yasuri::LinksNode.new('/html/body/a', "root", [
249
- Yasuri::TextNode.new('/html/body/p', "content"),
250
- Yasuri::LinksNode.new('/html/body/ul/li/a', "sub_link", [
251
- Yasuri::TextNode.new('/html/head/title', "sub_page_title"),
252
- ]),
253
- ])
254
- compare_generated_vs_original(generated, original)
255
- end
256
- end
257
-
258
- ############
259
- # Paginate #
260
- ############
261
- describe '::PaginateNode' do
262
- before do
263
- @uri += "/pagination/page01.html"
264
- @page = @agent.get(@uri)
265
- end
266
-
267
- it "scrape each paginated pages" do
268
- root_node = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
269
- Yasuri::TextNode.new('/html/body/p', "content"),
270
- ])
271
- actual = root_node.inject(@agent, @page)
272
- expected = [
273
- {"content" => "PaginationTest01"},
274
- {"content" => "PaginationTest02"},
275
- {"content" => "PaginationTest03"},
276
- {"content" => "PaginationTest04"},
277
- ]
278
- expect(actual).to match expected
279
- end
280
-
281
- it "scrape each paginated pages limited" do
282
- root_node = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
283
- Yasuri::TextNode.new('/html/body/p', "content"),
284
- ], limit:3)
285
- actual = root_node.inject(@agent, @page)
286
- expected = [
287
- {"content" => "PaginationTest01"},
288
- {"content" => "PaginationTest02"},
289
- {"content" => "PaginationTest03"},
290
- ]
291
- expect(actual).to match expected
292
- end
293
-
294
-
295
- it 'return first content if paginate link node is not found' do
296
- missing_xpath = "/html/body/nav/span/b[@class='next']"
297
- root_node = Yasuri::PaginateNode.new(missing_xpath, "root", [
298
- Yasuri::TextNode.new('/html/body/p', "content"),
299
- ])
300
- actual = root_node.inject(@agent, @page)
301
- expected = [ {"content" => "PaginationTest01"}, ]
302
- expect(actual).to match_array expected
303
- end
304
-
305
- it 'return empty hashes if content node is not found' do
306
- root_node = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
307
- Yasuri::TextNode.new('/html/body/hoge', "content"),
308
- ])
309
- actual = root_node.inject(@agent, @page)
310
- expected = [ {"content" => ""}, {"content" => ""}, {"content" => ""}, {"content" => ""},]
311
- expect(actual).to match_array expected
312
- end
313
-
314
- it 'can be defined by DSL, return single PaginateNode content' do
315
- generated = Yasuri.pages_next "/html/body/nav/span/a[@class='next']" do
316
- text_content '/html/body/p'
317
- end
318
- original = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
319
- Yasuri::TextNode.new('/html/body/p', "content"),
320
- ])
321
- compare_generated_vs_original(generated, original, @page)
322
- end
323
-
324
- it 'can be defined by DSL, return single PaginateNode content limited' do
325
- generated = Yasuri.pages_next "/html/body/nav/span/a[@class='next']", 2 do
326
- text_content '/html/body/p'
327
- end
328
- original = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
329
- Yasuri::TextNode.new('/html/body/p', "content"),
330
- ], limit: 2)
331
- compare_generated_vs_original(generated, original, @page)
332
- end
333
- end
334
-
335
19
  #############
336
20
  # json2tree #
337
21
  #############
@@ -348,9 +32,21 @@ describe 'Yasuri' do
348
32
  }|
349
33
  generated = Yasuri.json2tree(src)
350
34
  original = Yasuri::TextNode.new('/html/body/p[1]', "content")
351
- compare_generated_vs_original(generated, original)
35
+ compare_generated_vs_original(generated, original, @index_page)
36
+ end
37
+
38
+ it "return TextNode with truncate_regexp" do
39
+ src = %q| { "node" : "text",
40
+ "name" : "content",
41
+ "path" : "/html/body/p[1]",
42
+ "truncate" : "^[^,]+"
43
+ }|
44
+ generated = Yasuri.json2tree(src)
45
+ original = Yasuri::TextNode.new('/html/body/p[1]', "content", truncate:/^[^,]+/)
46
+ compare_generated_vs_original(generated, original, @index_page)
352
47
  end
353
48
 
49
+
354
50
  it "return LinksNode/TextNode" do
355
51
  src = %q| { "node" : "links",
356
52
  "name" : "root",
@@ -364,7 +60,8 @@ describe 'Yasuri' do
364
60
  original = Yasuri::LinksNode.new('/html/body/a', "root", [
365
61
  Yasuri::TextNode.new('/html/body/p', "content"),
366
62
  ])
367
- compare_generated_vs_original(generated, original)
63
+
64
+ compare_generated_vs_original(generated, original, @index_page)
368
65
  end
369
66
 
370
67
  it "return PaginateNode/TextNode" do
@@ -437,6 +134,83 @@ describe 'Yasuri' do
437
134
  end
438
135
  end
439
136
 
137
+ #############
138
+ # tree2json #
139
+ #############
140
+ describe '.tree2json' do
141
+ it "return empty json" do
142
+ json = Yasuri.tree2json(nil)
143
+ expect(json).to match "{}"
144
+ end
145
+
146
+ it "return text node" do
147
+ node = Yasuri::TextNode.new("/html/head/title", "title")
148
+ json = Yasuri.tree2json(node)
149
+ expected_str = %q| { "node": "text",
150
+ "name": "title",
151
+ "path": "/html/head/title"
152
+ } |
153
+ expected = JSON.parse(expected_str)
154
+ actual = JSON.parse(json)
155
+ expect(actual).to match expected
156
+ end
157
+
158
+ it "return text node with truncate_regexp" do
159
+ node = Yasuri::TextNode.new("/html/head/title", "title", truncate:/^[^,]+/)
160
+ json = Yasuri.tree2json(node)
161
+ expected_str = %q| { "node": "text",
162
+ "name": "title",
163
+ "path": "/html/head/title",
164
+ "truncate": "^[^,]+"
165
+ } |
166
+ expected = Yasuri.tree2json(Yasuri.json2tree(expected_str))
167
+ actual = Yasuri.tree2json(Yasuri.json2tree(json))
168
+ expect(actual).to match expected
169
+ end
170
+
171
+ it "return LinksNode/TextNode" do
172
+ tree = Yasuri::LinksNode.new('/html/body/a', "root", [
173
+ Yasuri::TextNode.new('/html/body/p', "content"),
174
+ ])
175
+ json = Yasuri.tree2json(tree)
176
+ expected_src = %q| { "node" : "links",
177
+ "name" : "root",
178
+ "path" : "/html/body/a",
179
+ "children" : [ { "node" : "text",
180
+ "name" : "content",
181
+ "path" : "/html/body/p"
182
+ } ]
183
+ }|
184
+ expected = JSON.parse(expected_src)
185
+ actual = JSON.parse(json)
186
+ expect(actual).to match expected
187
+ end
188
+
189
+ it "return PaginateNode/TextNode with limit" do
190
+ tree = Yasuri::PaginateNode.new("/html/body/nav/span/a[@class='next']", "root", [
191
+ Yasuri::TextNode.new('/html/body/p', "content"),
192
+ ], limit:10)
193
+
194
+ json = Yasuri.tree2json(tree)
195
+ expected_src = %q| { "node" : "pages",
196
+ "name" : "root",
197
+ "path" : "/html/body/nav/span/a[@class='next']",
198
+ "limit" : 10,
199
+ "children" : [ { "node" : "text",
200
+ "name" : "content",
201
+ "path" : "/html/body/p"
202
+ } ]
203
+ }|
204
+ expected = JSON.parse(expected_src)
205
+ actual = JSON.parse(json)
206
+ expect(actual).to match expected
207
+ end
208
+
209
+
210
+
211
+ end
212
+
213
+
440
214
  it 'has a version number' do
441
215
  expect(Yasuri::VERSION).not_to be nil
442
216
  end
@@ -0,0 +1,114 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'spec_helper'
5
+
6
+ ##########
7
+ # Struct #
8
+ ##########
9
+ describe 'Yasuri' do
10
+ include_context 'httpserver'
11
+
12
+ describe '::StructNode' do
13
+ before do
14
+ @agent = Mechanize.new
15
+ @page = @agent.get(uri + "/structual_text.html")
16
+
17
+ @table_1996 = [
18
+ { "title" => "The Perfect Insider",
19
+ "pub_date" => "1996/4/5" },
20
+ { "title" => "Doctors in Isolated Room",
21
+ "pub_date" => "1996/7/5" },
22
+ { "title" => "Mathematical Goodbye",
23
+ "pub_date" => "1996/9/5" },
24
+ ]
25
+ @table_1997 = [
26
+ { "title" => "Jack the Poetical Private",
27
+ "pub_date" => "1997/1/5" },
28
+ { "title" => "Who Inside",
29
+ "pub_date" => "1997/4/5" },
30
+ { "title" => "Illusion Acts Like Magic",
31
+ "pub_date" => "1997/10/5" },
32
+ ]
33
+ @table_1998 = [
34
+ { "title" => "Replaceable Summer",
35
+ "pub_date" => "1998/1/7" },
36
+ { "title" => "Switch Back",
37
+ "pub_date" => "1998/4/5" },
38
+ { "title" => "Numerical Models",
39
+ "pub_date" => "1998/7/5" },
40
+ { "title" => "The Perfect Outsider",
41
+ "pub_date" => "1998/10/5" },
42
+ ]
43
+ @all_tables = [
44
+ {"table" => @table_1996},
45
+ {"table" => @table_1997},
46
+ {"table" => @table_1998},
47
+ ]
48
+ end
49
+
50
+ it 'scrape single table contents' do
51
+ node = Yasuri::StructNode.new('/html/body/table[1]/tr', "table", [
52
+ Yasuri::TextNode.new('./td[1]', "title"),
53
+ Yasuri::TextNode.new('./td[2]', "pub_date"),
54
+ ])
55
+ expected = @table_1996
56
+ actual = node.inject(@agent, @page)
57
+ expect(actual).to match expected
58
+ end
59
+
60
+ it 'return empty text if no match node' do
61
+ no_match_xpath = '/html/body/table[1]/t'
62
+ node = Yasuri::StructNode.new(no_match_xpath, "table", [
63
+ Yasuri::TextNode.new('./td[1]', "title")
64
+ ])
65
+ actual = node.inject(@agent, @page)
66
+ expect(actual).to be_empty
67
+ end
68
+
69
+ it 'fail with invalid xpath' do
70
+ invalid_xpath = '/html/body/table[1]/table[1]/tr['
71
+ node = Yasuri::StructNode.new(invalid_xpath, "table", [
72
+ Yasuri::TextNode.new('./td[1]', "title")
73
+ ])
74
+ expect { node.inject(@agent, @page) }.to raise_error
75
+ end
76
+
77
+ it 'fail with invalid xpath in children' do
78
+ invalid_xpath = './td[1]['
79
+ node = Yasuri::StructNode.new('/html/body/table[1]/tr', "table", [
80
+ Yasuri::TextNode.new(invalid_xpath, "title"),
81
+ Yasuri::TextNode.new('./td[2]', "pub_date"),
82
+ ])
83
+ expect { node.inject(@agent, @page) }.to raise_error
84
+ end
85
+
86
+ it 'scrape all tables' do
87
+ node = Yasuri::StructNode.new('/html/body/table', "tables", [
88
+ Yasuri::StructNode.new('./tr', "table", [
89
+ Yasuri::TextNode.new('./td[1]', "title"),
90
+ Yasuri::TextNode.new('./td[2]', "pub_date"),
91
+ ])
92
+ ])
93
+ expected = @all_tables
94
+ actual = node.inject(@agent, @page)
95
+ expect(actual).to match expected
96
+ end
97
+
98
+ it 'can be defined by DSL, scrape all tables' do
99
+ generated = Yasuri.struct_tables '/html/body/table' do
100
+ struct_table './tr' do
101
+ text_title './td[1]'
102
+ text_pub_date './td[2]'
103
+ end
104
+ end
105
+ original = Yasuri::StructNode.new('/html/body/table', "tables", [
106
+ Yasuri::StructNode.new('./tr', "table", [
107
+ Yasuri::TextNode.new('./td[1]', "title"),
108
+ Yasuri::TextNode.new('./td[2]', "pub_date"),
109
+ ])
110
+ ])
111
+ compare_generated_vs_original(generated, original, @page)
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,61 @@
1
+
2
+ # Author:: TAC (tac@tac42.net)
3
+
4
+ require_relative 'spec_helper'
5
+
6
+ ########
7
+ # Text #
8
+ ########
9
+ describe 'Yasuri' do
10
+ include_context 'httpserver'
11
+
12
+ before do
13
+ @agent = Mechanize.new
14
+ @index_page = @agent.get(uri)
15
+ end
16
+
17
+ describe '::TextNode' do
18
+ before { @node = Yasuri::TextNode.new('/html/body/p[1]', "title") }
19
+
20
+ it 'scrape text text <p>Hello,Yasuri</p>' do
21
+ actual = @node.inject(@agent, @index_page)
22
+ expect(actual).to eq "Hello,Yasuri"
23
+ end
24
+
25
+ it 'return empty text if no match node' do
26
+ no_match_node = Yasuri::TextNode.new('/html/body/no_match_node', "title")
27
+ actual = no_match_node.inject(@agent, @index_page)
28
+ expect(actual).to be_empty
29
+ end
30
+
31
+ it 'fail with invalid xpath' do
32
+ invalid_xpath = '/html/body/no_match_node['
33
+ node = Yasuri::TextNode.new(invalid_xpath, "title")
34
+ expect { node.inject(@agent, @index_page) }.to raise_error
35
+ end
36
+
37
+ it "can be defined by DSL, return single TextNode title" do
38
+ generated = Yasuri.text_title '/html/body/p[1]'
39
+ original = Yasuri::TextNode.new('/html/body/p[1]', "title")
40
+ compare_generated_vs_original(generated, original, @index_page)
41
+ end
42
+
43
+ it "can be truncated with regexp" do
44
+ node = Yasuri.text_title '/html/body/p[1]', /^[^,]+/
45
+ actual = node.inject(@agent, @index_page)
46
+ expect(actual).to eq "Hello"
47
+ end
48
+
49
+ it "can be truncated with regexp" do
50
+ node = Yasuri.text_title '/html/body/p[1]', /[^,]+$/
51
+ actual = node.inject(@agent, @index_page)
52
+ expect(actual).to eq "Yasuri"
53
+ end
54
+
55
+ it "return empty string if truncated with no match to regexp" do
56
+ node = Yasuri.text_title '/html/body/p[1]', /^hoge/
57
+ actual = node.inject(@agent, @index_page)
58
+ expect(actual).to be_empty
59
+ end
60
+ end
61
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yasuri
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - TAC
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-24 00:00:00.000000000 Z
11
+ date: 2015-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -155,6 +155,12 @@ files:
155
155
  - lib/yasuri.rb
156
156
  - lib/yasuri/version.rb
157
157
  - lib/yasuri/yasuri.rb
158
+ - lib/yasuri/yasuri_links_node.rb
159
+ - lib/yasuri/yasuri_node.rb
160
+ - lib/yasuri/yasuri_node_generator.rb
161
+ - lib/yasuri/yasuri_paginate_node.rb
162
+ - lib/yasuri/yasuri_struct_node.rb
163
+ - lib/yasuri/yasuri_text_node.rb
158
164
  - spec/htdocs/child01.html
159
165
  - spec/htdocs/child01_sub.html
160
166
  - spec/htdocs/child02.html
@@ -169,7 +175,12 @@ files:
169
175
  - spec/htdocs/structual_text.html
170
176
  - spec/servers/httpserver.rb
171
177
  - spec/spec_helper.rb
178
+ - spec/yasuri_links_node_spec.rb
179
+ - spec/yasuri_node_spec.rb
180
+ - spec/yasuri_paginate_node_spec.rb
172
181
  - spec/yasuri_spec.rb
182
+ - spec/yasuri_struct_node_spec.rb
183
+ - spec/yasuri_text_node_spec.rb
173
184
  - yasuri.gemspec
174
185
  homepage: https://github.com/tac0x2a/yasuri
175
186
  licenses:
@@ -210,4 +221,9 @@ test_files:
210
221
  - spec/htdocs/structual_text.html
211
222
  - spec/servers/httpserver.rb
212
223
  - spec/spec_helper.rb
224
+ - spec/yasuri_links_node_spec.rb
225
+ - spec/yasuri_node_spec.rb
226
+ - spec/yasuri_paginate_node_spec.rb
213
227
  - spec/yasuri_spec.rb
228
+ - spec/yasuri_struct_node_spec.rb
229
+ - spec/yasuri_text_node_spec.rb