rusty 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +44 -0
- data/LICENSE.BSD +26 -0
- data/README.md +200 -0
- data/Rakefile +13 -0
- data/bin/watchr +27 -0
- data/lib/rusty.rb +16 -0
- data/lib/rusty/callback_binding.rb +71 -0
- data/lib/rusty/dx.rb +127 -0
- data/lib/rusty/helpers.rb +40 -0
- data/lib/rusty/nokogiri_ext.rb +101 -0
- data/lib/rusty/rule_set.rb +124 -0
- data/lib/rusty/scope.rb +29 -0
- data/lib/rusty/selector.rb +87 -0
- data/lib/rusty/version.rb +8 -0
- data/rusty.gemspec +24 -0
- data/tasks/rdoc.rake +18 -0
- data/tasks/test.rake +8 -0
- data/test/helper.rb +17 -0
- data/test/test_dx.rb +62 -0
- data/test/test_helper.rb +39 -0
- data/test/test_nokogiri_ext.rb +73 -0
- data/test/test_rss_example.rb +58 -0
- data/test/test_rule_set.rb +65 -0
- data/test/test_scope.rb +97 -0
- data/test/test_selector.rb +48 -0
- metadata +103 -0
data/test/helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup(:default, :development)
|
3
|
+
|
4
|
+
require 'ruby-debug'
|
5
|
+
require 'simplecov'
|
6
|
+
require 'test/unit'
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter "test/helper.rb"
|
9
|
+
end
|
10
|
+
|
11
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
require 'rusty'
|
14
|
+
|
15
|
+
class Test::Unit::TestCase
|
16
|
+
end
|
17
|
+
|
data/test/test_dx.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative './helper'
|
2
|
+
|
3
|
+
class TestDx < Test::Unit::TestCase
|
4
|
+
def dx
|
5
|
+
@dx ||= Rusty::DX.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_as_nil
|
9
|
+
assert_false dx.list?
|
10
|
+
assert_false dx.dict?
|
11
|
+
|
12
|
+
assert_equal(nil, dx.to_ruby)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_inspect
|
16
|
+
assert_equal("<nil>", dx.inspect)
|
17
|
+
dx << 1
|
18
|
+
assert_equal("<[1]>", dx.inspect)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_as_list
|
22
|
+
dx[3] # turn dx into a list, but still with no content
|
23
|
+
assert_true dx.list?
|
24
|
+
assert_false dx.dict?
|
25
|
+
|
26
|
+
assert_raise(ArgumentError) {
|
27
|
+
dx.foo = "bar"
|
28
|
+
}
|
29
|
+
|
30
|
+
assert_equal([], dx.to_ruby)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_create_list_by_pushing
|
34
|
+
dx << 1
|
35
|
+
assert_equal([1], dx.to_ruby)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_create_dict_by_key
|
39
|
+
dx.key?(:a)
|
40
|
+
assert_equal({}, dx.to_ruby)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_as_dict
|
44
|
+
dx.abc
|
45
|
+
dx.abc
|
46
|
+
assert_true dx.dict?
|
47
|
+
assert_false dx.list?
|
48
|
+
|
49
|
+
dx[2] = "bar"
|
50
|
+
assert_true dx.dict?
|
51
|
+
assert_false dx.list?
|
52
|
+
|
53
|
+
assert_equal({"abc"=>nil, 2=>"bar"}, dx.to_ruby)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_undefined_method
|
57
|
+
assert_raise(NoMethodError) {
|
58
|
+
dx.abc(1,2,3)
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative './helper'
|
3
|
+
|
4
|
+
class TestSelector < Test::Unit::TestCase
|
5
|
+
module RuleSet
|
6
|
+
extend Rusty::RuleSet
|
7
|
+
|
8
|
+
helper do
|
9
|
+
def a_helper_method
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
helper Rusty::Helpers::Text
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_helpers
|
17
|
+
assert_equal 2, RuleSet.helpers.length
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_callback_binding_klass_includes_helpers
|
21
|
+
callback_binding_klass = RuleSet.send(:callback_binding_klass)
|
22
|
+
assert callback_binding_klass.public_instance_methods.include?(:a_helper_method)
|
23
|
+
assert callback_binding_klass.public_instance_methods.include?(:text) # from Rusty::Helpers::Text
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_text
|
27
|
+
helper = {}.extend(Rusty::Helpers::Text)
|
28
|
+
data = File.read(__FILE__).split(/__END__\n/).last
|
29
|
+
|
30
|
+
document = Nokogiri.XML(data)
|
31
|
+
|
32
|
+
assert_equal "1hr 40min - Rated Ohne Altersbeschränkung", helper.text(document.css("top span").first)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
__END__
|
37
|
+
<top>
|
38
|
+
<span>‎1hr 40min‎‎ - Rated Ohne Altersbeschränkung‎</span>
|
39
|
+
</top>
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative './helper'
|
2
|
+
|
3
|
+
class TestNokogiriExt < Test::Unit::TestCase
|
4
|
+
def data
|
5
|
+
@data ||= File.read(__FILE__).split(/__END__\n/).last
|
6
|
+
end
|
7
|
+
|
8
|
+
def document
|
9
|
+
@document ||= begin
|
10
|
+
Nokogiri.HTML(data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def node(selector)
|
15
|
+
document.css(selector).first
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_classes
|
19
|
+
body = node("body")
|
20
|
+
assert_equal %w(a b d), body.classes
|
21
|
+
assert_true body.has_class?("a")
|
22
|
+
assert_true body.has_class?("b")
|
23
|
+
assert_false body.has_class?("c")
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_attributes_hash
|
27
|
+
assert_equal({"id" => "anchor", "foo" => "bar"}, node("a").attributes_hash)
|
28
|
+
assert_equal({}, node("div").attributes_hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_parents
|
32
|
+
assert_equal ["html", "body", "div", "p", "a"], node("a").self_and_parents.map(&:name)
|
33
|
+
assert_equal ["html", "body", "div", "p"], node("a").parents.map(&:name)
|
34
|
+
|
35
|
+
assert_equal ["html"], node("html").self_and_parents.map(&:name)
|
36
|
+
assert_equal [], node("html").parents.map(&:name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_simplified_name
|
40
|
+
assert_equal "body.a.b.d", node("body").simplified_name
|
41
|
+
assert_equal "a#anchor", node("a").simplified_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_meta_encoding
|
45
|
+
html4 = "<html><head><meta http-equiv='content-type' content='text/html; charset=UTF-8'></head></html>"
|
46
|
+
html5 = "<html><head><meta charset='UTF-8'></head></html>"
|
47
|
+
|
48
|
+
assert_equal "UTF-8", Nokogiri.HTML(html4).meta_encoding
|
49
|
+
assert_equal "UTF-8", Nokogiri.HTML(html5).meta_encoding
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_with_meta_encoding
|
53
|
+
doc = Nokogiri::HTML.with_meta_encoding data
|
54
|
+
assert_equal("iso-8859-1", doc.meta_encoding)
|
55
|
+
assert_equal("iso-8859-1", doc.encoding)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
__END__
|
60
|
+
<html>
|
61
|
+
<head>
|
62
|
+
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
63
|
+
</head>
|
64
|
+
<body class="a b d">
|
65
|
+
<div>
|
66
|
+
<p>
|
67
|
+
</p>
|
68
|
+
<p>
|
69
|
+
<a id="anchor" foo="bar"></a>
|
70
|
+
</p>
|
71
|
+
</div>
|
72
|
+
</body>
|
73
|
+
</html>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative './helper'
|
2
|
+
|
3
|
+
class TestRssExample < Test::Unit::TestCase
|
4
|
+
module SimleRSS
|
5
|
+
extend Rusty::RuleSet
|
6
|
+
helper Rusty::Helpers::Text
|
7
|
+
|
8
|
+
on "*" do end
|
9
|
+
on "rss channel *" do rss[node.name] = text(node) end
|
10
|
+
on "rss channel item" do rss.items << item end
|
11
|
+
on "rss channel item *" do item[node.name] = text(node) end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_rss
|
15
|
+
raw = File.read(__FILE__).split(/__END__\n/).last
|
16
|
+
data = SimleRSS.transform! Nokogiri.XML(raw)
|
17
|
+
|
18
|
+
expected = {
|
19
|
+
"rss"=> {
|
20
|
+
"title"=>"The Title",
|
21
|
+
"link"=>"http://the.link",
|
22
|
+
"description"=>"The description",
|
23
|
+
"language"=>"de-de",
|
24
|
+
"pubDate"=>"1363099965",
|
25
|
+
"items"=>[
|
26
|
+
{"title"=>"Item 1", "description"=>"description 1", "link"=>"http://the.first.link"},
|
27
|
+
{"title"=>"Item 2", "description"=>"description 2", "link"=>"http://the.second.link"}
|
28
|
+
]
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
assert_equal(expected, data.to_ruby)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
__END__
|
36
|
+
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
37
|
+
<rss version="2.0">
|
38
|
+
<channel>
|
39
|
+
|
40
|
+
<title>The Title</title>
|
41
|
+
<link>http://the.link</link>
|
42
|
+
<description>The description</description>
|
43
|
+
<language>de-de</language>
|
44
|
+
<pubDate>1363099965</pubDate>
|
45
|
+
|
46
|
+
<item>
|
47
|
+
<title>Item 1</title>
|
48
|
+
<description> description 1 </description>
|
49
|
+
<link>http://the.first.link</link>
|
50
|
+
</item>
|
51
|
+
|
52
|
+
<item>
|
53
|
+
<title>Item 2</title>
|
54
|
+
<description> description 2 </description>
|
55
|
+
<link>http://the.second.link</link>
|
56
|
+
</item>
|
57
|
+
</channel>
|
58
|
+
</rss>
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative './helper'
|
2
|
+
|
3
|
+
class TestRuleSet < Test::Unit::TestCase
|
4
|
+
module RuleSet
|
5
|
+
extend Rusty::RuleSet
|
6
|
+
def self.rules_for_mode(mode); super; end
|
7
|
+
|
8
|
+
helper do
|
9
|
+
def a_helper_method
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
on "foo", "bar" do end
|
14
|
+
after "foo", "baz" do end
|
15
|
+
after "html, js, erlang" do end
|
16
|
+
|
17
|
+
on "p a" do end
|
18
|
+
on "a" do end
|
19
|
+
on ".a" do end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_registration
|
23
|
+
assert_equal ["foo", "bar", "p a", "a", ".a"], RuleSet.rules_for_mode(:on).keys
|
24
|
+
assert_equal %w(foo baz html js erlang), RuleSet.rules_for_mode(:after).keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_best_rule
|
28
|
+
assert_equal "p a", RuleSet.best_rule(:on, node("a")).selector.name
|
29
|
+
assert_equal ".a", RuleSet.best_rule(:on, node("body")).selector.name
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def data
|
36
|
+
@data ||= File.read(__FILE__).split(/__END__\n/).last
|
37
|
+
end
|
38
|
+
|
39
|
+
def document
|
40
|
+
@document ||= begin
|
41
|
+
Nokogiri.HTML(data)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def node(selector)
|
46
|
+
document.css(selector).first
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
__END__
|
52
|
+
<html>
|
53
|
+
<head>
|
54
|
+
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
55
|
+
</head>
|
56
|
+
<body class="a b d">
|
57
|
+
<div>
|
58
|
+
<p>
|
59
|
+
</p>
|
60
|
+
<p>
|
61
|
+
<a id="anchor" foo="bar"></a>
|
62
|
+
</p>
|
63
|
+
</div>
|
64
|
+
</body>
|
65
|
+
</html>
|
data/test/test_scope.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require_relative './helper'
|
2
|
+
|
3
|
+
class TestScope < Test::Unit::TestCase
|
4
|
+
def test_scope_names
|
5
|
+
named = Rusty::Scope.new node("named")
|
6
|
+
assert named.has_name?("named")
|
7
|
+
assert named.has_name?("x")
|
8
|
+
assert !named.has_name?("a")
|
9
|
+
|
10
|
+
top = Rusty::Scope.new node("top")
|
11
|
+
assert top.has_name?("top")
|
12
|
+
assert top.has_name?("document")
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_one_step_delegation
|
16
|
+
top = Rusty::Scope.new node("top")
|
17
|
+
mid = Rusty::Scope.new node("mid"), top
|
18
|
+
bot = Rusty::Scope.new node("bot"), mid
|
19
|
+
|
20
|
+
e_top = Rusty::CallbackBinding.new(top)
|
21
|
+
e_mid = Rusty::CallbackBinding.new(mid)
|
22
|
+
e_bot = Rusty::CallbackBinding.new(bot)
|
23
|
+
|
24
|
+
object_ids = []
|
25
|
+
|
26
|
+
# one step delegation
|
27
|
+
#
|
28
|
+
# When evaluated in the context of e_mid, top must refer to
|
29
|
+
# the top Rusty::Scope
|
30
|
+
e_mid.instance_eval do
|
31
|
+
object_ids = [top.object_id]
|
32
|
+
end
|
33
|
+
assert_equal(object_ids, [top.object_id])
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_two_step_delegation
|
37
|
+
top = Rusty::Scope.new node("top")
|
38
|
+
mid = Rusty::Scope.new node("mid"), top
|
39
|
+
bot = Rusty::Scope.new node("bot"), mid
|
40
|
+
|
41
|
+
e_top = Rusty::CallbackBinding.new(top)
|
42
|
+
e_mid = Rusty::CallbackBinding.new(mid)
|
43
|
+
e_bot = Rusty::CallbackBinding.new(bot)
|
44
|
+
|
45
|
+
# When evaluated in the context of e_bot,
|
46
|
+
# top must refer to the top Rusty::Scope
|
47
|
+
# mid must refer to the mid Rusty::Scope
|
48
|
+
# and mid.top must not refer to the top Rusty::Scope,
|
49
|
+
# but to a DX which belongs to mid.
|
50
|
+
object_ids = []
|
51
|
+
dx = nil
|
52
|
+
e_bot.instance_eval do
|
53
|
+
object_ids = [top.object_id, mid.object_id]
|
54
|
+
dx = mid.top
|
55
|
+
end
|
56
|
+
assert_equal(object_ids, [top.object_id, mid.object_id])
|
57
|
+
assert_kind_of(Rusty::DX, dx)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_setting_attributes
|
61
|
+
top = Rusty::Scope.new node("top")
|
62
|
+
mid = Rusty::Scope.new node("mid"), top
|
63
|
+
bot = Rusty::Scope.new node("bot"), mid
|
64
|
+
|
65
|
+
e_bot = Rusty::CallbackBinding.new(bot)
|
66
|
+
|
67
|
+
e_bot.instance_eval do
|
68
|
+
top.name = "top"
|
69
|
+
mid.name = "mid"
|
70
|
+
mid.top.name = "mid.top"
|
71
|
+
bot.name = "bot"
|
72
|
+
end
|
73
|
+
|
74
|
+
assert_equal({"name" => "top"}, top.to_ruby)
|
75
|
+
assert_equal({"name" => "mid", "top" => {"name" => "mid.top"}}, mid.to_ruby)
|
76
|
+
assert_equal({"name" => "bot"}, bot.to_ruby)
|
77
|
+
end
|
78
|
+
|
79
|
+
def document
|
80
|
+
@document ||= begin
|
81
|
+
data = File.read(__FILE__).split(/__END__\n/).last
|
82
|
+
Nokogiri.XML(data)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def node(name)
|
87
|
+
document.css(name).first
|
88
|
+
end
|
89
|
+
end
|
90
|
+
__END__
|
91
|
+
<top>
|
92
|
+
<mid>
|
93
|
+
<bot>
|
94
|
+
</bot>
|
95
|
+
</mid>
|
96
|
+
<named class="x y z"></named>
|
97
|
+
</top>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative './helper'
|
2
|
+
|
3
|
+
class TestSelector < Test::Unit::TestCase
|
4
|
+
DATA = File.read(__FILE__).split(/__END__\n/).last
|
5
|
+
|
6
|
+
def __test_selectors(klass)
|
7
|
+
document = Nokogiri.XML(DATA)
|
8
|
+
|
9
|
+
# test matching selectors
|
10
|
+
assert klass.new("foo").match?(document.css("foo").first)
|
11
|
+
assert klass.new("fi fa foo").match?(document.css("foo").first)
|
12
|
+
assert klass.new("fi bar").match?(document.css("bar").first)
|
13
|
+
assert klass.new("fi bar").match?(document.css("bar").last)
|
14
|
+
|
15
|
+
# test non-matching selectors
|
16
|
+
assert_false klass.new("foox").match?(document.css("foo").first)
|
17
|
+
assert_false klass.new("fix fa foo").match?(document.css("foo").first)
|
18
|
+
assert_false klass.new("fix fa foo").match?(nil)
|
19
|
+
|
20
|
+
# test "*" special case
|
21
|
+
assert klass.new("*").match?(document.css("bar").last)
|
22
|
+
assert_false klass.new("*").match?(nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_css
|
26
|
+
__test_selectors Rusty::Selector::CSS
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_cached_ss
|
30
|
+
__test_selectors Rusty::Selector::CachedCSS
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_weight
|
34
|
+
assert Rusty::Selector::CSS.new("*").weight < Rusty::Selector::CSS.new("a").weight
|
35
|
+
assert Rusty::Selector::CSS.new("a b c").weight < Rusty::Selector::CSS.new("a b c d").weight
|
36
|
+
assert Rusty::Selector::CSS.new("a b c").weight < Rusty::Selector::CSS.new("#a").weight
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
__END__
|
41
|
+
<fi>
|
42
|
+
<fa>
|
43
|
+
<foo>
|
44
|
+
<bar>baz</bar>
|
45
|
+
<bar>baz</bar>
|
46
|
+
</foo>
|
47
|
+
</fa>
|
48
|
+
</fi>
|