rusty 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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>
|