nokogiri 1.1.1-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of nokogiri might be problematic. Click here for more details.
- data/History.ja.txt +99 -0
- data/History.txt +99 -0
- data/Manifest.txt +141 -0
- data/README.ja.txt +100 -0
- data/README.txt +109 -0
- data/Rakefile +354 -0
- data/ext/nokogiri/extconf.rb +93 -0
- data/ext/nokogiri/html_document.c +86 -0
- data/ext/nokogiri/html_document.h +10 -0
- data/ext/nokogiri/html_sax_parser.c +36 -0
- data/ext/nokogiri/html_sax_parser.h +11 -0
- data/ext/nokogiri/native.c +41 -0
- data/ext/nokogiri/native.h +50 -0
- data/ext/nokogiri/xml_cdata.c +44 -0
- data/ext/nokogiri/xml_cdata.h +9 -0
- data/ext/nokogiri/xml_comment.c +42 -0
- data/ext/nokogiri/xml_comment.h +9 -0
- data/ext/nokogiri/xml_document.c +206 -0
- data/ext/nokogiri/xml_document.h +10 -0
- data/ext/nokogiri/xml_dtd.c +121 -0
- data/ext/nokogiri/xml_dtd.h +8 -0
- data/ext/nokogiri/xml_io.c +17 -0
- data/ext/nokogiri/xml_io.h +9 -0
- data/ext/nokogiri/xml_node.c +727 -0
- data/ext/nokogiri/xml_node.h +13 -0
- data/ext/nokogiri/xml_node_set.c +118 -0
- data/ext/nokogiri/xml_node_set.h +9 -0
- data/ext/nokogiri/xml_reader.c +465 -0
- data/ext/nokogiri/xml_reader.h +10 -0
- data/ext/nokogiri/xml_sax_parser.c +201 -0
- data/ext/nokogiri/xml_sax_parser.h +10 -0
- data/ext/nokogiri/xml_syntax_error.c +199 -0
- data/ext/nokogiri/xml_syntax_error.h +11 -0
- data/ext/nokogiri/xml_text.c +40 -0
- data/ext/nokogiri/xml_text.h +9 -0
- data/ext/nokogiri/xml_xpath.c +53 -0
- data/ext/nokogiri/xml_xpath.h +11 -0
- data/ext/nokogiri/xml_xpath_context.c +214 -0
- data/ext/nokogiri/xml_xpath_context.h +9 -0
- data/ext/nokogiri/xslt_stylesheet.c +123 -0
- data/ext/nokogiri/xslt_stylesheet.h +9 -0
- data/lib/action-nokogiri.rb +30 -0
- data/lib/nokogiri.rb +72 -0
- data/lib/nokogiri/css.rb +25 -0
- data/lib/nokogiri/css/generated_parser.rb +721 -0
- data/lib/nokogiri/css/generated_tokenizer.rb +159 -0
- data/lib/nokogiri/css/node.rb +97 -0
- data/lib/nokogiri/css/parser.rb +64 -0
- data/lib/nokogiri/css/parser.y +216 -0
- data/lib/nokogiri/css/syntax_error.rb +6 -0
- data/lib/nokogiri/css/tokenizer.rb +9 -0
- data/lib/nokogiri/css/tokenizer.rex +63 -0
- data/lib/nokogiri/css/xpath_visitor.rb +168 -0
- data/lib/nokogiri/decorators.rb +2 -0
- data/lib/nokogiri/decorators/hpricot.rb +3 -0
- data/lib/nokogiri/decorators/hpricot/node.rb +56 -0
- data/lib/nokogiri/decorators/hpricot/node_set.rb +54 -0
- data/lib/nokogiri/decorators/hpricot/xpath_visitor.rb +28 -0
- data/lib/nokogiri/decorators/slop.rb +31 -0
- data/lib/nokogiri/hpricot.rb +51 -0
- data/lib/nokogiri/html.rb +105 -0
- data/lib/nokogiri/html/builder.rb +9 -0
- data/lib/nokogiri/html/document.rb +9 -0
- data/lib/nokogiri/html/sax/parser.rb +21 -0
- data/lib/nokogiri/version.rb +3 -0
- data/lib/nokogiri/xml.rb +83 -0
- data/lib/nokogiri/xml/after_handler.rb +18 -0
- data/lib/nokogiri/xml/attr.rb +10 -0
- data/lib/nokogiri/xml/before_handler.rb +33 -0
- data/lib/nokogiri/xml/builder.rb +84 -0
- data/lib/nokogiri/xml/cdata.rb +9 -0
- data/lib/nokogiri/xml/comment.rb +6 -0
- data/lib/nokogiri/xml/document.rb +55 -0
- data/lib/nokogiri/xml/dtd.rb +6 -0
- data/lib/nokogiri/xml/element.rb +6 -0
- data/lib/nokogiri/xml/entity_declaration.rb +9 -0
- data/lib/nokogiri/xml/node.rb +333 -0
- data/lib/nokogiri/xml/node_set.rb +197 -0
- data/lib/nokogiri/xml/notation.rb +6 -0
- data/lib/nokogiri/xml/reader.rb +20 -0
- data/lib/nokogiri/xml/sax.rb +9 -0
- data/lib/nokogiri/xml/sax/document.rb +59 -0
- data/lib/nokogiri/xml/sax/parser.rb +37 -0
- data/lib/nokogiri/xml/syntax_error.rb +21 -0
- data/lib/nokogiri/xml/text.rb +6 -0
- data/lib/nokogiri/xml/xpath.rb +10 -0
- data/lib/nokogiri/xml/xpath/syntax_error.rb +8 -0
- data/lib/nokogiri/xml/xpath_context.rb +14 -0
- data/lib/nokogiri/xslt.rb +28 -0
- data/lib/nokogiri/xslt/stylesheet.rb +6 -0
- data/test/css/test_nthiness.rb +159 -0
- data/test/css/test_parser.rb +237 -0
- data/test/css/test_tokenizer.rb +162 -0
- data/test/css/test_xpath_visitor.rb +64 -0
- data/test/files/dont_hurt_em_why.xml +422 -0
- data/test/files/exslt.xml +8 -0
- data/test/files/exslt.xslt +35 -0
- data/test/files/staff.xml +59 -0
- data/test/files/staff.xslt +32 -0
- data/test/files/tlm.html +850 -0
- data/test/helper.rb +78 -0
- data/test/hpricot/files/basic.xhtml +17 -0
- data/test/hpricot/files/boingboing.html +2266 -0
- data/test/hpricot/files/cy0.html +3653 -0
- data/test/hpricot/files/immob.html +400 -0
- data/test/hpricot/files/pace_application.html +1320 -0
- data/test/hpricot/files/tenderlove.html +16 -0
- data/test/hpricot/files/uswebgen.html +220 -0
- data/test/hpricot/files/utf8.html +1054 -0
- data/test/hpricot/files/week9.html +1723 -0
- data/test/hpricot/files/why.xml +19 -0
- data/test/hpricot/load_files.rb +11 -0
- data/test/hpricot/test_alter.rb +67 -0
- data/test/hpricot/test_builder.rb +27 -0
- data/test/hpricot/test_parser.rb +426 -0
- data/test/hpricot/test_paths.rb +15 -0
- data/test/hpricot/test_preserved.rb +77 -0
- data/test/hpricot/test_xml.rb +30 -0
- data/test/html/sax/test_parser.rb +27 -0
- data/test/html/test_builder.rb +89 -0
- data/test/html/test_document.rb +150 -0
- data/test/html/test_node.rb +21 -0
- data/test/test_convert_xpath.rb +185 -0
- data/test/test_css_cache.rb +57 -0
- data/test/test_gc.rb +15 -0
- data/test/test_memory_leak.rb +38 -0
- data/test/test_nokogiri.rb +97 -0
- data/test/test_reader.rb +222 -0
- data/test/test_xslt_transforms.rb +93 -0
- data/test/xml/sax/test_parser.rb +95 -0
- data/test/xml/test_attr.rb +15 -0
- data/test/xml/test_builder.rb +16 -0
- data/test/xml/test_cdata.rb +18 -0
- data/test/xml/test_comment.rb +16 -0
- data/test/xml/test_document.rb +195 -0
- data/test/xml/test_dtd.rb +43 -0
- data/test/xml/test_node.rb +394 -0
- data/test/xml/test_node_set.rb +143 -0
- data/test/xml/test_text.rb +13 -0
- data/test/xml/test_xpath.rb +105 -0
- data/vendor/hoe.rb +1020 -0
- metadata +233 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
|
2
|
+
|
3
|
+
module Nokogiri
|
4
|
+
module XML
|
5
|
+
class TestNodeSet < Nokogiri::TestCase
|
6
|
+
def setup
|
7
|
+
@xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_length_size
|
11
|
+
assert node_set = @xml.search('//employee')
|
12
|
+
assert_equal node_set.length, node_set.size
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_to_xml
|
16
|
+
assert node_set = @xml.search('//employee')
|
17
|
+
assert node_set.to_xml
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_inner_html
|
21
|
+
doc = Nokogiri::HTML(<<-eohtml)
|
22
|
+
<html>
|
23
|
+
<body>
|
24
|
+
<div>
|
25
|
+
<a>one</a>
|
26
|
+
</div>
|
27
|
+
<div>
|
28
|
+
<a>two</a>
|
29
|
+
</div>
|
30
|
+
</body>
|
31
|
+
</html>
|
32
|
+
eohtml
|
33
|
+
assert html = doc.css('div').inner_html
|
34
|
+
assert_match '<a>', html
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_at
|
38
|
+
assert node_set = @xml.search('//employee')
|
39
|
+
assert_equal node_set.first, node_set.at(0)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_to_ary
|
43
|
+
assert node_set = @xml.search('//employee')
|
44
|
+
foo = []
|
45
|
+
foo += node_set
|
46
|
+
assert_equal node_set.length, foo.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_push
|
50
|
+
node = Nokogiri::XML::Node.new('foo', @xml)
|
51
|
+
node.content = 'bar'
|
52
|
+
|
53
|
+
assert node_set = @xml.search('//employee')
|
54
|
+
node_set.push(node)
|
55
|
+
|
56
|
+
assert node_set.include?(node)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_unlink
|
60
|
+
xml = Nokogiri::XML.parse(<<-eoxml)
|
61
|
+
<root>
|
62
|
+
<a class='foo bar'>Bar</a>
|
63
|
+
<a class='bar foo'>Bar</a>
|
64
|
+
<a class='bar'>Bar</a>
|
65
|
+
<a>Hello world</a>
|
66
|
+
<a class='baz bar foo'>Bar</a>
|
67
|
+
<a class='bazbarfoo'>Awesome</a>
|
68
|
+
<a class='bazbar'>Awesome</a>
|
69
|
+
</root>
|
70
|
+
eoxml
|
71
|
+
set = xml.xpath('//a')
|
72
|
+
set.unlink
|
73
|
+
set.each do |node|
|
74
|
+
assert !node.parent
|
75
|
+
# assert !node.document # ugh. libxml doesn't clear node->doc pointer, due to xmlDict implementation.
|
76
|
+
assert !node.previous_sibling
|
77
|
+
assert !node.next_sibling
|
78
|
+
end
|
79
|
+
assert_no_match(/Hello world/, xml.to_s)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_nodeset_search_takes_namespace
|
83
|
+
@xml = Nokogiri::XML.parse(<<-eoxml)
|
84
|
+
<root>
|
85
|
+
<car xmlns:part="http://general-motors.com/">
|
86
|
+
<part:tire>Michelin Model XGV</part:tire>
|
87
|
+
</car>
|
88
|
+
<bicycle xmlns:part="http://schwinn.com/">
|
89
|
+
<part:tire>I'm a bicycle tire!</part:tire>
|
90
|
+
</bicycle>
|
91
|
+
</root>
|
92
|
+
eoxml
|
93
|
+
set = @xml/'root'
|
94
|
+
assert_equal 1, set.length
|
95
|
+
bike_tire = set.search('//bike:tire', 'bike' => "http://schwinn.com/")
|
96
|
+
assert_equal 1, bike_tire.length
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_new_nodeset
|
100
|
+
node_set = Nokogiri::XML::NodeSet.new(@xml)
|
101
|
+
assert_equal(0, node_set.length)
|
102
|
+
node = Nokogiri::XML::Node.new('form', @xml)
|
103
|
+
node_set << node
|
104
|
+
assert_equal(1, node_set.length)
|
105
|
+
assert_equal(node, node_set.last)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_search_on_nodeset
|
109
|
+
assert node_set = @xml.search('//employee')
|
110
|
+
assert sub_set = node_set.search('.//name')
|
111
|
+
assert_equal(node_set.length, sub_set.length)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_negative_index_works
|
115
|
+
assert node_set = @xml.search('//employee')
|
116
|
+
assert_equal node_set.last, node_set[-1]
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_large_negative_index_returns_nil
|
120
|
+
assert node_set = @xml.search('//employee')
|
121
|
+
assert_nil(node_set[-1 * (node_set.length + 1)])
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_node_set_fetches_private_data
|
125
|
+
assert node_set = @xml.search('//employee')
|
126
|
+
|
127
|
+
set = node_set
|
128
|
+
assert_equal(set[0], set[0])
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_node_set_returns_0
|
132
|
+
assert node_set = @xml.search('//asdkfjhasdlkfjhaldskfh')
|
133
|
+
assert_equal(0, node_set.length)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_wrap
|
137
|
+
employees = (@xml/"//employee").wrap("<wrapper/>")
|
138
|
+
assert_equal 'wrapper', employees[0].parent.name
|
139
|
+
assert_equal 'employee', @xml.search("//wrapper").first.children[0].name
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
|
2
|
+
|
3
|
+
module Nokogiri
|
4
|
+
module XML
|
5
|
+
class TestText < Nokogiri::TestCase
|
6
|
+
def test_new
|
7
|
+
node = Text.new('hello world', Document.new)
|
8
|
+
assert node
|
9
|
+
assert_equal('hello world', node.content)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', "helper"))
|
2
|
+
|
3
|
+
module Nokogiri
|
4
|
+
module XML
|
5
|
+
class TestXPath < Nokogiri::TestCase
|
6
|
+
def setup
|
7
|
+
@xml = Nokogiri::XML.parse(File.read(XML_FILE), XML_FILE)
|
8
|
+
|
9
|
+
@handler = Class.new {
|
10
|
+
attr_reader :things
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@things = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def thing thing
|
17
|
+
@things << thing
|
18
|
+
thing
|
19
|
+
end
|
20
|
+
|
21
|
+
def returns_array node_set
|
22
|
+
@things << node_set.to_a
|
23
|
+
node_set.to_a
|
24
|
+
end
|
25
|
+
|
26
|
+
def my_filter set, attribute, value
|
27
|
+
set.find_all { |x| x[attribute] == value }
|
28
|
+
end
|
29
|
+
}.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_css_search_uses_custom_selectors_with_arguments
|
33
|
+
set = @xml.css('employee > address:my_filter("domestic", "Yes")', @handler)
|
34
|
+
assert set.length > 0
|
35
|
+
set.each do |node|
|
36
|
+
assert_equal 'Yes', node['domestic']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_css_search_uses_custom_selectors
|
41
|
+
set = @xml.xpath('//employee')
|
42
|
+
css_set = @xml.css('employee:thing()', @handler)
|
43
|
+
assert_equal(set.length, @handler.things.length)
|
44
|
+
assert_equal(set.to_a, @handler.things.flatten)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_pass_self_to_function
|
48
|
+
set = @xml.xpath('//employee/address[my_filter(., "domestic", "Yes")]', @handler)
|
49
|
+
assert set.length > 0
|
50
|
+
set.each do |node|
|
51
|
+
assert_equal 'Yes', node['domestic']
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_custom_xpath_function_gets_strings
|
56
|
+
set = @xml.xpath('//employee')
|
57
|
+
@xml.xpath('//employee[thing("asdf")]', @handler)
|
58
|
+
assert_equal(set.length, @handler.things.length)
|
59
|
+
assert_equal(['asdf'] * set.length, @handler.things)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_custom_xpath_gets_true_booleans
|
63
|
+
set = @xml.xpath('//employee')
|
64
|
+
@xml.xpath('//employee[thing(true())]', @handler)
|
65
|
+
assert_equal(set.length, @handler.things.length)
|
66
|
+
assert_equal([true] * set.length, @handler.things)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_custom_xpath_gets_false_booleans
|
70
|
+
set = @xml.xpath('//employee')
|
71
|
+
@xml.xpath('//employee[thing(false())]', @handler)
|
72
|
+
assert_equal(set.length, @handler.things.length)
|
73
|
+
assert_equal([false] * set.length, @handler.things)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_custom_xpath_gets_numbers
|
77
|
+
set = @xml.xpath('//employee')
|
78
|
+
@xml.xpath('//employee[thing(10)]', @handler)
|
79
|
+
assert_equal(set.length, @handler.things.length)
|
80
|
+
assert_equal([10] * set.length, @handler.things)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_custom_xpath_gets_node_sets
|
84
|
+
set = @xml.xpath('//employee/name')
|
85
|
+
@xml.xpath('//employee[thing(name)]', @handler)
|
86
|
+
assert_equal(set.length, @handler.things.length)
|
87
|
+
assert_equal(set.to_a, @handler.things.flatten)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_custom_xpath_gets_node_sets_and_returns_array
|
91
|
+
set = @xml.xpath('//employee/name')
|
92
|
+
@xml.xpath('//employee[returns_array(name)]', @handler)
|
93
|
+
assert_equal(set.length, @handler.things.length)
|
94
|
+
assert_equal(set.to_a, @handler.things.flatten)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_code_that_invokes_OP_RESET_inside_libxml2
|
98
|
+
doc = "<html><body id='foo'><foo>hi</foo></body></html>"
|
99
|
+
xpath = 'id("foo")//foo'
|
100
|
+
nokogiri = Nokogiri::HTML.parse(doc)
|
101
|
+
tree = nokogiri.xpath(xpath)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/vendor/hoe.rb
ADDED
@@ -0,0 +1,1020 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
require 'rake/testtask'
|
8
|
+
require 'rbconfig'
|
9
|
+
require 'uri'
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'rubyforge'
|
13
|
+
RUBYFORGE = true
|
14
|
+
rescue LoadError
|
15
|
+
RUBYFORGE = false
|
16
|
+
class RubyForge
|
17
|
+
VERSION = 'awesome'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'yaml'
|
22
|
+
|
23
|
+
begin
|
24
|
+
gem 'rdoc'
|
25
|
+
rescue LoadError
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# hoe - a tool to help rake
|
30
|
+
#
|
31
|
+
# Hoe is a simple rake/rubygems helper for project Rakefiles. It
|
32
|
+
# generates all the usual tasks for projects including rdoc generation,
|
33
|
+
# testing, packaging, and deployment.
|
34
|
+
#
|
35
|
+
# == Using Hoe
|
36
|
+
#
|
37
|
+
# === Basics
|
38
|
+
#
|
39
|
+
# Use this as a minimal starting point:
|
40
|
+
#
|
41
|
+
# require 'hoe'
|
42
|
+
#
|
43
|
+
# Hoe.new("project_name", '1.0.0') do |p|
|
44
|
+
# p.rubyforge_name = "rf_project"
|
45
|
+
# # add other details here
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# # add other tasks here
|
49
|
+
#
|
50
|
+
# === Tasks Provided:
|
51
|
+
#
|
52
|
+
# announce:: Create news email file and post to rubyforge.
|
53
|
+
# audit:: Run ZenTest against the package.
|
54
|
+
# check_manifest:: Verify the manifest.
|
55
|
+
# clean:: Clean up all the extras.
|
56
|
+
# config_hoe:: Create a fresh ~/.hoerc file.
|
57
|
+
# debug_gem:: Show information about the gem.
|
58
|
+
# default:: Run the default tasks.
|
59
|
+
# deps:email:: Print a contact list for gems dependent on this gem
|
60
|
+
# deps:fetch:: Fetch all the dependent gems of this gem into tarballs
|
61
|
+
# deps:list:: List all the dependent gems of this gem
|
62
|
+
# docs:: Build the docs HTML Files
|
63
|
+
# email:: Generate email announcement file.
|
64
|
+
# gem:: Build the gem file hoe-1.8.0.gem
|
65
|
+
# generate_key:: Generate a key for signing your gems.
|
66
|
+
# install_gem:: Install the package as a gem.
|
67
|
+
# multi:: Run the test suite using multiruby.
|
68
|
+
# package:: Build all the packages
|
69
|
+
# post_blog:: Post announcement to blog.
|
70
|
+
# post_news:: Post announcement to rubyforge.
|
71
|
+
# publish_docs:: Publish RDoc to RubyForge.
|
72
|
+
# release:: Package and upload the release to rubyforge.
|
73
|
+
# ridocs:: Generate ri locally for testing.
|
74
|
+
# tasks:: Generate a list of tasks for doco.
|
75
|
+
# test:: Run the test suite.
|
76
|
+
# test_deps:: Show which test files fail when run alone.
|
77
|
+
#
|
78
|
+
# === Extra Configuration Options:
|
79
|
+
#
|
80
|
+
# Run +config_hoe+ to generate a new ~/.hoerc file. The file is a
|
81
|
+
# YAML formatted config file with the following settings:
|
82
|
+
#
|
83
|
+
# exclude:: A regular expression of files to exclude from
|
84
|
+
# +check_manifest+.
|
85
|
+
# publish_on_announce:: Run +publish_docs+ when you run +release+.
|
86
|
+
# signing_key_file:: Signs your gems with this private key.
|
87
|
+
# signing_cert_file:: Signs your gem with this certificate.
|
88
|
+
# blogs:: An array of hashes of blog settings.
|
89
|
+
#
|
90
|
+
# Run +config_hoe+ and see ~/.hoerc for examples.
|
91
|
+
#
|
92
|
+
# === Signing Gems:
|
93
|
+
#
|
94
|
+
# Run the 'generate_key' task. This will:
|
95
|
+
#
|
96
|
+
# 1. Configure your ~/.hoerc.
|
97
|
+
# 2. Generate a signing key and certificate.
|
98
|
+
# 3. Install the private key and public certificate files into ~/.gem.
|
99
|
+
# 4. Upload the certificate to RubyForge.
|
100
|
+
#
|
101
|
+
# Hoe will now generate signed gems when the package task is run. If you have
|
102
|
+
# multiple machines you build gems on, be sure to install your key and
|
103
|
+
# certificate on each machine.
|
104
|
+
#
|
105
|
+
# Keep your private key secret! Keep your private key safe!
|
106
|
+
#
|
107
|
+
# To make sure your gems are signed run:
|
108
|
+
#
|
109
|
+
# rake package; tar tf pkg/yourproject-1.2.3.gem
|
110
|
+
#
|
111
|
+
# If your gem is signed you will see:
|
112
|
+
#
|
113
|
+
# data.tar.gz
|
114
|
+
# data.tar.gz.sig
|
115
|
+
# metadata.gz
|
116
|
+
# metadata.gz.sig
|
117
|
+
#
|
118
|
+
# === Platform awareness
|
119
|
+
#
|
120
|
+
# Hoe allows bundling of pre-compiled extensions in the +package+ task.
|
121
|
+
#
|
122
|
+
# To create a package for your current platform:
|
123
|
+
#
|
124
|
+
# rake package INLINE=1
|
125
|
+
#
|
126
|
+
# This will force Hoe analize your +Inline+ already compiled
|
127
|
+
# extensions and include them in your gem.
|
128
|
+
#
|
129
|
+
# If somehow you need to force a specific platform:
|
130
|
+
#
|
131
|
+
# rake package INLINE=1 FORCE_PLATFORM=mswin32
|
132
|
+
#
|
133
|
+
# This will set the +Gem::Specification+ platform to the one indicated in
|
134
|
+
# +FORCE_PLATFORM+ (instead of default Gem::Platform::CURRENT)
|
135
|
+
#
|
136
|
+
|
137
|
+
class Hoe
|
138
|
+
VERSION = '1.8.2'
|
139
|
+
GEMURL = URI.parse 'http://gems.rubyforge.org' # for namespace :deps below
|
140
|
+
|
141
|
+
ruby_prefix = Config::CONFIG['prefix']
|
142
|
+
sitelibdir = Config::CONFIG['sitelibdir']
|
143
|
+
|
144
|
+
##
|
145
|
+
# Used to specify a custom install location (for rake install).
|
146
|
+
|
147
|
+
PREFIX = ENV['PREFIX'] || ruby_prefix
|
148
|
+
|
149
|
+
##
|
150
|
+
# Used to add extra flags to RUBY_FLAGS.
|
151
|
+
|
152
|
+
RUBY_DEBUG = ENV['RUBY_DEBUG']
|
153
|
+
|
154
|
+
default_ruby_flags = "-w -I#{%w(lib ext bin test).join(File::PATH_SEPARATOR)}" +
|
155
|
+
(RUBY_DEBUG ? " #{RUBY_DEBUG}" : '')
|
156
|
+
|
157
|
+
##
|
158
|
+
# Used to specify flags to ruby [has smart default].
|
159
|
+
|
160
|
+
RUBY_FLAGS = ENV['RUBY_FLAGS'] || default_ruby_flags
|
161
|
+
|
162
|
+
##
|
163
|
+
# Used to add flags to test_unit (e.g., -n test_borked).
|
164
|
+
|
165
|
+
FILTER = ENV['FILTER'] # for tests (eg FILTER="-n test_blah")
|
166
|
+
|
167
|
+
# :stopdoc:
|
168
|
+
|
169
|
+
RUBYLIB = if PREFIX == ruby_prefix then
|
170
|
+
sitelibdir
|
171
|
+
else
|
172
|
+
File.join(PREFIX, sitelibdir[ruby_prefix.size..-1])
|
173
|
+
end
|
174
|
+
|
175
|
+
DLEXT = Config::CONFIG['DLEXT']
|
176
|
+
|
177
|
+
WINDOZE = /mswin|mingw/ =~ RUBY_PLATFORM unless defined? WINDOZE
|
178
|
+
|
179
|
+
DIFF = if WINDOZE
|
180
|
+
'diff.exe'
|
181
|
+
else
|
182
|
+
if system("gdiff", __FILE__, __FILE__)
|
183
|
+
'gdiff' # solaris and kin suck
|
184
|
+
else
|
185
|
+
'diff'
|
186
|
+
end
|
187
|
+
end unless defined? DIFF
|
188
|
+
|
189
|
+
# :startdoc:
|
190
|
+
|
191
|
+
##
|
192
|
+
# *Recommended*: The author(s) of the package. (can be array)
|
193
|
+
# Really. Set this or we'll tease you.
|
194
|
+
|
195
|
+
attr_accessor :author
|
196
|
+
|
197
|
+
##
|
198
|
+
# Populated automatically from the manifest. List of executables.
|
199
|
+
|
200
|
+
attr_accessor :bin_files # :nodoc:
|
201
|
+
|
202
|
+
##
|
203
|
+
# *Optional*: An array of the project's blog categories. Defaults to project name.
|
204
|
+
|
205
|
+
attr_accessor :blog_categories
|
206
|
+
|
207
|
+
##
|
208
|
+
# Optional: A description of the release's latest changes. Auto-populates.
|
209
|
+
|
210
|
+
attr_accessor :changes
|
211
|
+
|
212
|
+
##
|
213
|
+
# Optional: An array of file patterns to delete on clean.
|
214
|
+
|
215
|
+
attr_accessor :clean_globs
|
216
|
+
|
217
|
+
##
|
218
|
+
# Optional: A description of the project. Auto-populates.
|
219
|
+
|
220
|
+
attr_accessor :description
|
221
|
+
|
222
|
+
##
|
223
|
+
# Optional: What sections from the readme to use for auto-description. Defaults to %w(description).
|
224
|
+
|
225
|
+
attr_accessor :description_sections
|
226
|
+
|
227
|
+
##
|
228
|
+
# *Recommended*: The author's email address(es). (can be array)
|
229
|
+
|
230
|
+
attr_accessor :email
|
231
|
+
|
232
|
+
##
|
233
|
+
# Optional: An array of rubygem dependencies.
|
234
|
+
|
235
|
+
attr_accessor :extra_deps
|
236
|
+
|
237
|
+
##
|
238
|
+
# Optional: An array of rubygem developer dependencies.
|
239
|
+
|
240
|
+
attr_accessor :extra_dev_deps
|
241
|
+
|
242
|
+
##
|
243
|
+
# Populated automatically from the manifest. List of library files.
|
244
|
+
|
245
|
+
attr_accessor :lib_files # :nodoc:
|
246
|
+
|
247
|
+
##
|
248
|
+
# Optional: Array of incompatible versions for multiruby filtering. Used as a regex.
|
249
|
+
|
250
|
+
attr_accessor :multiruby_skip
|
251
|
+
|
252
|
+
##
|
253
|
+
# *MANDATORY*: The name of the release.
|
254
|
+
|
255
|
+
attr_accessor :name
|
256
|
+
|
257
|
+
##
|
258
|
+
# Optional: Should package create a tarball? [default: true]
|
259
|
+
|
260
|
+
attr_accessor :need_tar
|
261
|
+
|
262
|
+
##
|
263
|
+
# Optional: Should package create a zipfile? [default: false]
|
264
|
+
|
265
|
+
attr_accessor :need_zip
|
266
|
+
|
267
|
+
##
|
268
|
+
# Optional: A post-install message to be displayed when gem is installed.
|
269
|
+
|
270
|
+
attr_accessor :post_install_message
|
271
|
+
|
272
|
+
##
|
273
|
+
# Optional: A regexp to match documentation files against the manifest.
|
274
|
+
|
275
|
+
attr_accessor :rdoc_pattern
|
276
|
+
|
277
|
+
##
|
278
|
+
# Optional: Name of RDoc destination directory on Rubyforge. [default: +name+]
|
279
|
+
|
280
|
+
attr_accessor :remote_rdoc_dir
|
281
|
+
|
282
|
+
##
|
283
|
+
# Optional: Flags for RDoc rsync. [default: "-av --delete"]
|
284
|
+
|
285
|
+
attr_accessor :rsync_args
|
286
|
+
|
287
|
+
##
|
288
|
+
# Optional: The name of the rubyforge project. [default: name.downcase]
|
289
|
+
|
290
|
+
attr_accessor :rubyforge_name
|
291
|
+
|
292
|
+
##
|
293
|
+
# The Gem::Specification.
|
294
|
+
|
295
|
+
attr_accessor :spec # :nodoc:
|
296
|
+
|
297
|
+
##
|
298
|
+
# Optional: A hash of extra values to set in the gemspec. Value may be a proc.
|
299
|
+
|
300
|
+
attr_accessor :spec_extras
|
301
|
+
|
302
|
+
##
|
303
|
+
# Optional: A short summary of the project. Auto-populates.
|
304
|
+
|
305
|
+
attr_accessor :summary
|
306
|
+
|
307
|
+
##
|
308
|
+
# Optional: Number of sentences from description for summary. Defaults to 1.
|
309
|
+
|
310
|
+
attr_accessor :summary_sentences
|
311
|
+
|
312
|
+
##
|
313
|
+
# Populated automatically from the manifest. List of tests.
|
314
|
+
|
315
|
+
attr_accessor :test_files # :nodoc:
|
316
|
+
|
317
|
+
##
|
318
|
+
# Optional: An array of test file patterns [default: test/**/test_*.rb]
|
319
|
+
|
320
|
+
attr_accessor :test_globs
|
321
|
+
|
322
|
+
##
|
323
|
+
# Optional: What test library to require [default: test/unit]
|
324
|
+
|
325
|
+
attr_accessor :testlib
|
326
|
+
|
327
|
+
##
|
328
|
+
# Optional: The url(s) of the project. (can be array). Auto-populates.
|
329
|
+
|
330
|
+
attr_accessor :url
|
331
|
+
|
332
|
+
##
|
333
|
+
# *MANDATORY*: The version. Don't hardcode! use a constant in the project.
|
334
|
+
|
335
|
+
attr_accessor :version
|
336
|
+
|
337
|
+
##
|
338
|
+
# Add extra dirs to both $: and RUBY_FLAGS (for test runs)
|
339
|
+
|
340
|
+
def self.add_include_dirs(*dirs)
|
341
|
+
dirs = dirs.flatten
|
342
|
+
$:.unshift(*dirs)
|
343
|
+
s = File::PATH_SEPARATOR
|
344
|
+
Hoe::RUBY_FLAGS.sub!(/-I/, "-I#{dirs.join(s)}#{s}")
|
345
|
+
end
|
346
|
+
|
347
|
+
def normalize_deps deps
|
348
|
+
Array(deps).map { |o| String === o ? [o] : o }
|
349
|
+
end
|
350
|
+
|
351
|
+
def missing name
|
352
|
+
warn "** #{name} is missing or in the wrong format for auto-intuiting."
|
353
|
+
warn " run `sow blah` and look at its text files"
|
354
|
+
end
|
355
|
+
|
356
|
+
def initialize(name, version) # :nodoc:
|
357
|
+
self.name = name
|
358
|
+
self.version = version
|
359
|
+
|
360
|
+
# Defaults
|
361
|
+
self.author = []
|
362
|
+
self.clean_globs = %w(diff diff.txt email.txt ri deps .source_index
|
363
|
+
*.gem *~ **/*~ *.rbc **/*.rbc)
|
364
|
+
self.description_sections = %w(description)
|
365
|
+
self.blog_categories = [name]
|
366
|
+
self.email = []
|
367
|
+
self.extra_deps = []
|
368
|
+
self.extra_dev_deps = []
|
369
|
+
self.multiruby_skip = []
|
370
|
+
self.need_tar = true
|
371
|
+
self.need_zip = false
|
372
|
+
self.rdoc_pattern = /^(lib|bin|ext)|txt$/
|
373
|
+
self.remote_rdoc_dir = name
|
374
|
+
self.rsync_args = '-av --delete'
|
375
|
+
self.rubyforge_name = name.downcase
|
376
|
+
self.spec_extras = {}
|
377
|
+
self.summary_sentences = 1
|
378
|
+
self.test_globs = ['test/**/test_*.rb']
|
379
|
+
self.testlib = 'test/unit'
|
380
|
+
self.post_install_message = nil
|
381
|
+
|
382
|
+
yield self if block_given?
|
383
|
+
|
384
|
+
# Intuit values:
|
385
|
+
|
386
|
+
readme = File.read("README.txt").split(/^(=+ .*)$/)[1..-1] rescue ''
|
387
|
+
begin
|
388
|
+
unless readme.empty? then
|
389
|
+
sections = readme.map { |s|
|
390
|
+
s =~ /^=/ ? s.strip.downcase.chomp(':').split.last : s.strip
|
391
|
+
}
|
392
|
+
sections = Hash[*sections]
|
393
|
+
desc = sections.values_at(*description_sections).join("\n\n")
|
394
|
+
summ = desc.split(/\.\s+/).first(summary_sentences).join(". ")
|
395
|
+
|
396
|
+
self.description ||= desc
|
397
|
+
self.summary ||= summ
|
398
|
+
self.url ||= readme[1].gsub(/^\* /, '').split(/\n/).grep(/\S+/)
|
399
|
+
else
|
400
|
+
missing 'README.txt'
|
401
|
+
end
|
402
|
+
end if RUBYFORGE
|
403
|
+
|
404
|
+
self.changes ||= begin
|
405
|
+
h = File.read("History.txt")
|
406
|
+
h.split(/^(===.*)/)[1..2].join.strip
|
407
|
+
rescue
|
408
|
+
missing 'History.txt'
|
409
|
+
''
|
410
|
+
end
|
411
|
+
|
412
|
+
%w(email author).each do |field|
|
413
|
+
value = self.send(field)
|
414
|
+
if value.nil? or value.empty? then
|
415
|
+
if Time.now < Time.local(2008, 4, 1) then
|
416
|
+
warn "Hoe #{field} value not set - Fix by 2008-04-01!"
|
417
|
+
self.send "#{field}=", "doofus"
|
418
|
+
else
|
419
|
+
abort "Hoe #{field} value not set. aborting"
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
hoe_deps = {
|
425
|
+
'rake' => ">= #{RAKEVERSION}",
|
426
|
+
'rubyforge' => ">= #{::RubyForge::VERSION}",
|
427
|
+
}
|
428
|
+
|
429
|
+
self.extra_deps = normalize_deps extra_deps
|
430
|
+
self.extra_dev_deps = normalize_deps extra_dev_deps
|
431
|
+
|
432
|
+
define_tasks
|
433
|
+
end
|
434
|
+
|
435
|
+
def developer name, email
|
436
|
+
self.author << name
|
437
|
+
self.email << email
|
438
|
+
end
|
439
|
+
|
440
|
+
def with_config # :nodoc:
|
441
|
+
rc = File.expand_path("~/.hoerc")
|
442
|
+
exists = File.exist? rc
|
443
|
+
config = exists ? YAML.load_file(rc) : {}
|
444
|
+
yield(config, rc)
|
445
|
+
end
|
446
|
+
|
447
|
+
def define_tasks # :nodoc:
|
448
|
+
desc 'Run the default tasks.'
|
449
|
+
task :default => :test
|
450
|
+
|
451
|
+
Rake::TestTask.new do |t|
|
452
|
+
%w[ ext lib bin test ].each do |dir|
|
453
|
+
t.libs << dir
|
454
|
+
end
|
455
|
+
t.test_files = FileList['test/**/test_*.rb'] +
|
456
|
+
FileList['test/**/*_test.rb']
|
457
|
+
t.verbose = true
|
458
|
+
t.warning = true
|
459
|
+
end
|
460
|
+
|
461
|
+
desc 'Show which test files fail when run alone.'
|
462
|
+
task :test_deps do
|
463
|
+
tests = Dir["test/**/test_*.rb"] + Dir["test/**/*_test.rb"]
|
464
|
+
|
465
|
+
paths = ['bin', 'lib', 'test'].join(File::PATH_SEPARATOR)
|
466
|
+
null_dev = WINDOZE ? '> NUL 2>&1' : '&> /dev/null'
|
467
|
+
|
468
|
+
tests.each do |test|
|
469
|
+
if not system "ruby -I#{paths} #{test} #{null_dev}" then
|
470
|
+
puts "Dependency Issues: #{test}"
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
desc 'Run the test suite using multiruby.'
|
476
|
+
task :multi do
|
477
|
+
sh "multiruby -S rake clean test"
|
478
|
+
end
|
479
|
+
|
480
|
+
############################################################
|
481
|
+
# Packaging and Installing
|
482
|
+
|
483
|
+
signing_key = nil
|
484
|
+
cert_chain = []
|
485
|
+
|
486
|
+
with_config do |config, path|
|
487
|
+
break unless config['signing_key_file'] and config['signing_cert_file']
|
488
|
+
key_file = File.expand_path config['signing_key_file'].to_s
|
489
|
+
signing_key = key_file if File.exist? key_file
|
490
|
+
|
491
|
+
cert_file = File.expand_path config['signing_cert_file'].to_s
|
492
|
+
cert_chain << cert_file if File.exist? cert_file
|
493
|
+
end
|
494
|
+
|
495
|
+
self.spec = Gem::Specification.new do |s|
|
496
|
+
s.name = name
|
497
|
+
s.version = version
|
498
|
+
s.summary = summary
|
499
|
+
case author
|
500
|
+
when Array
|
501
|
+
s.authors = author
|
502
|
+
else
|
503
|
+
s.author = author
|
504
|
+
end
|
505
|
+
s.email = email
|
506
|
+
s.homepage = Array(url).first
|
507
|
+
s.rubyforge_project = rubyforge_name
|
508
|
+
|
509
|
+
s.description = description
|
510
|
+
|
511
|
+
extra_deps.each do |dep|
|
512
|
+
s.add_dependency(*dep)
|
513
|
+
end
|
514
|
+
|
515
|
+
extra_dev_deps.each do |dep|
|
516
|
+
s.add_development_dependency(*dep)
|
517
|
+
end
|
518
|
+
|
519
|
+
s.files = File.read("Manifest.txt").delete("\r").split(/\n/)
|
520
|
+
s.executables = s.files.grep(/^bin/) { |f| File.basename(f) }
|
521
|
+
|
522
|
+
s.bindir = "bin"
|
523
|
+
dirs = Dir['{lib,ext}']
|
524
|
+
s.require_paths = dirs unless dirs.empty?
|
525
|
+
|
526
|
+
s.rdoc_options = ['--main', 'README.txt']
|
527
|
+
s.extra_rdoc_files = s.files.grep(/txt$/)
|
528
|
+
s.has_rdoc = true
|
529
|
+
|
530
|
+
s.post_install_message = post_install_message
|
531
|
+
|
532
|
+
if test ?f, "test/test_all.rb" then
|
533
|
+
s.test_file = "test/test_all.rb"
|
534
|
+
else
|
535
|
+
s.test_files = Dir[*test_globs]
|
536
|
+
end
|
537
|
+
|
538
|
+
if signing_key and cert_chain then
|
539
|
+
s.signing_key = signing_key
|
540
|
+
s.cert_chain = cert_chain
|
541
|
+
end
|
542
|
+
|
543
|
+
############################################################
|
544
|
+
# Allow automatic inclusion of compiled extensions
|
545
|
+
if ENV['INLINE'] then
|
546
|
+
s.platform = ENV['FORCE_PLATFORM'] || Gem::Platform::CURRENT
|
547
|
+
|
548
|
+
# Try collecting Inline extensions for +name+
|
549
|
+
if defined?(Inline) then
|
550
|
+
directory 'lib/inline'
|
551
|
+
|
552
|
+
Inline.registered_inline_classes.each do |cls|
|
553
|
+
name = cls.name # TODO: what about X::Y::Z?
|
554
|
+
# name of the extension is CamelCase
|
555
|
+
alternate_name = if name =~ /[A-Z]/ then
|
556
|
+
name.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '')
|
557
|
+
elsif name =~ /_/ then
|
558
|
+
name.capitalize.gsub(/_([a-z])/) { $1.upcase }
|
559
|
+
end
|
560
|
+
|
561
|
+
extensions = Dir.chdir(Inline::directory) {
|
562
|
+
Dir["Inline_{#{name},#{alternate_name}}_*.#{DLEXT}"]
|
563
|
+
}
|
564
|
+
|
565
|
+
extensions.each do |ext|
|
566
|
+
# add the inlined extension to the spec files
|
567
|
+
s.files += ["lib/inline/#{ext}"]
|
568
|
+
|
569
|
+
# include the file in the tasks
|
570
|
+
file "lib/inline/#{ext}" => ["lib/inline"] do
|
571
|
+
cp File.join(Inline::directory, ext), "lib/inline"
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
# Do any extra stuff the user wants
|
579
|
+
spec_extras.each do |msg, val|
|
580
|
+
case val
|
581
|
+
when Proc
|
582
|
+
val.call(s.send(msg))
|
583
|
+
else
|
584
|
+
s.send "#{msg}=", val
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
desc 'Show information about the gem.'
|
590
|
+
task :debug_gem do
|
591
|
+
puts spec.to_ruby
|
592
|
+
end
|
593
|
+
|
594
|
+
self.lib_files = spec.files.grep(/^(lib|ext)/)
|
595
|
+
self.bin_files = spec.files.grep(/^bin/)
|
596
|
+
self.test_files = spec.files.grep(/^test/)
|
597
|
+
|
598
|
+
Rake::GemPackageTask.new spec do |pkg|
|
599
|
+
pkg.need_tar = @need_tar
|
600
|
+
pkg.need_zip = @need_zip
|
601
|
+
end
|
602
|
+
|
603
|
+
desc 'Install the package as a gem.'
|
604
|
+
task :install_gem => [:clean, :package] do
|
605
|
+
gem = Dir['pkg/*.gem'].first
|
606
|
+
sh "#{'sudo ' unless WINDOZE}gem install --local #{gem}"
|
607
|
+
end
|
608
|
+
|
609
|
+
desc 'Package and upload the release to rubyforge.'
|
610
|
+
task :release => [:clean, :package] do |t|
|
611
|
+
v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
|
612
|
+
abort "Versions don't match #{v} vs #{version}" if v != version
|
613
|
+
pkg = "pkg/#{name}-#{version}"
|
614
|
+
|
615
|
+
if $DEBUG then
|
616
|
+
puts "release_id = rf.add_release #{rubyforge_name.inspect}, #{name.inspect}, #{version.inspect}, \"#{pkg}.tgz\""
|
617
|
+
puts "rf.add_file #{rubyforge_name.inspect}, #{name.inspect}, release_id, \"#{pkg}.gem\""
|
618
|
+
end
|
619
|
+
|
620
|
+
rf = RubyForge.new.configure
|
621
|
+
puts "Logging in"
|
622
|
+
rf.login
|
623
|
+
|
624
|
+
c = rf.userconfig
|
625
|
+
c["release_notes"] = description if description
|
626
|
+
c["release_changes"] = changes if changes
|
627
|
+
c["preformatted"] = true
|
628
|
+
|
629
|
+
files = [(@need_tar ? "#{pkg}.tgz" : nil),
|
630
|
+
(@need_zip ? "#{pkg}.zip" : nil),
|
631
|
+
"#{pkg}.gem"].compact
|
632
|
+
|
633
|
+
puts "Releasing #{name} v. #{version}"
|
634
|
+
rf.add_release rubyforge_name, name, version, *files
|
635
|
+
end
|
636
|
+
|
637
|
+
############################################################
|
638
|
+
# Doco
|
639
|
+
|
640
|
+
Rake::RDocTask.new(:docs) do |rd|
|
641
|
+
rd.main = "README.txt"
|
642
|
+
rd.rdoc_dir = 'doc'
|
643
|
+
files = spec.files.grep(rdoc_pattern)
|
644
|
+
files -= ['Manifest.txt']
|
645
|
+
rd.rdoc_files.push(*files)
|
646
|
+
|
647
|
+
title = "#{name}-#{version} Documentation"
|
648
|
+
title = "#{rubyforge_name}'s " + title if rubyforge_name != name
|
649
|
+
|
650
|
+
rd.options << "-t #{title}"
|
651
|
+
end
|
652
|
+
|
653
|
+
desc 'Generate ri locally for testing.'
|
654
|
+
task :ridocs => :clean do
|
655
|
+
sh %q{ rdoc --ri -o ri . }
|
656
|
+
end
|
657
|
+
|
658
|
+
desc 'Publish RDoc to RubyForge.'
|
659
|
+
task :publish_docs => [:clean, :docs] do
|
660
|
+
config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
661
|
+
host = "#{config["username"]}@rubyforge.org"
|
662
|
+
|
663
|
+
remote_dir = "/var/www/gforge-projects/#{rubyforge_name}/#{remote_rdoc_dir}"
|
664
|
+
local_dir = 'doc'
|
665
|
+
|
666
|
+
sh %{rsync #{rsync_args} #{local_dir}/ #{host}:#{remote_dir}}
|
667
|
+
end
|
668
|
+
|
669
|
+
# no doco for this one
|
670
|
+
task :publish_on_announce do
|
671
|
+
with_config do |config, _|
|
672
|
+
Rake::Task['publish_docs'].invoke if config["publish_on_announce"]
|
673
|
+
end
|
674
|
+
end
|
675
|
+
|
676
|
+
############################################################
|
677
|
+
# Dependencies:
|
678
|
+
|
679
|
+
namespace :deps do
|
680
|
+
require 'zlib' # HACK for rubygems 1.3.0
|
681
|
+
require 'rubygems/remote_fetcher'
|
682
|
+
|
683
|
+
@@index = nil
|
684
|
+
|
685
|
+
def self.get_source_index
|
686
|
+
return @@index if @@index
|
687
|
+
|
688
|
+
dump = unless File.exist? '.source_index' then
|
689
|
+
url = GEMURL + "Marshal.#{Gem.marshal_version}.Z"
|
690
|
+
dump = Gem::RemoteFetcher.fetcher.fetch_path url
|
691
|
+
dump = Gem.inflate dump
|
692
|
+
open '.source_index', 'wb' do |io| io.write dump end
|
693
|
+
dump
|
694
|
+
else
|
695
|
+
open '.source_index', 'rb' do |io| io.read end
|
696
|
+
end
|
697
|
+
|
698
|
+
@@index = Marshal.load dump
|
699
|
+
end
|
700
|
+
|
701
|
+
def self.get_latest_gems
|
702
|
+
@@cache ||= get_source_index.latest_specs
|
703
|
+
end
|
704
|
+
|
705
|
+
def self.get_gems_by_name
|
706
|
+
@@by_name ||= Hash[*get_latest_gems.map { |gem|
|
707
|
+
[gem.name, gem, gem.full_name, gem]
|
708
|
+
}.flatten]
|
709
|
+
end
|
710
|
+
|
711
|
+
def self.dependent_upon name
|
712
|
+
get_latest_gems.find_all { |gem|
|
713
|
+
gem.dependencies.any? { |dep| dep.name == name }
|
714
|
+
}
|
715
|
+
end
|
716
|
+
|
717
|
+
|
718
|
+
desc "List all the dependent gems of this gem"
|
719
|
+
task :list do
|
720
|
+
gems = self.get_gems_by_name
|
721
|
+
gem = gems[self.name]
|
722
|
+
|
723
|
+
abort "Couldn't find gem: #{self.name}" unless gem
|
724
|
+
|
725
|
+
deps = self.dependent_upon self.name
|
726
|
+
max = deps.map { |s| s.full_name.size }.max
|
727
|
+
|
728
|
+
puts " dependents:"
|
729
|
+
unless deps.empty? then
|
730
|
+
deps.sort_by { |spec| spec.full_name }.each do |spec|
|
731
|
+
vers = spec.dependencies.find {|s| s.name == name }.requirement_list
|
732
|
+
puts " %-*s - %s" % [max, spec.full_name, vers.join(", ")]
|
733
|
+
end
|
734
|
+
else
|
735
|
+
puts " none"
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
739
|
+
desc "Print a contact list for gems dependent on this gem"
|
740
|
+
task :email do
|
741
|
+
gems = self.get_gems_by_name
|
742
|
+
gem = gems[self.name]
|
743
|
+
|
744
|
+
abort "Couldn't find gem: #{self.name}" unless gem
|
745
|
+
|
746
|
+
deps = self.dependent_upon self.name
|
747
|
+
|
748
|
+
email = deps.map { |s| s.email }.flatten.sort.uniq
|
749
|
+
email = email.map { |s| s.split(/,\s*/) }.flatten.sort.uniq
|
750
|
+
|
751
|
+
email.map! { |s| # don't you people realize how easy this is?
|
752
|
+
s.gsub(/ at | _at_ |\s*(atmark|@nospam@|-at?-|@at?@|<at?>|\[at?\]|\(at?\))\s*/i, '@').gsub(/\s*(dot|\[d(ot)?\]|\.dot\.)\s*/i, '.').gsub(/\s+com$/, '.com')
|
753
|
+
}
|
754
|
+
|
755
|
+
bad, good = email.partition { |e| e !~ /^[\w.+-]+\@[\w.+-]+$/ }
|
756
|
+
|
757
|
+
warn "Rejecting #{bad.size} email. I couldn't unmunge them." unless
|
758
|
+
bad.empty?
|
759
|
+
|
760
|
+
puts good.join(", ")
|
761
|
+
end
|
762
|
+
|
763
|
+
desc "Fetch all the dependent gems of this gem into tarballs"
|
764
|
+
task :fetch do
|
765
|
+
gems = self.get_gems_by_name
|
766
|
+
gem = gems[self.name]
|
767
|
+
deps = self.dependent_upon self.name
|
768
|
+
|
769
|
+
mkdir "deps" unless File.directory? "deps"
|
770
|
+
Dir.chdir "deps" do
|
771
|
+
begin
|
772
|
+
deps.sort_by { |spec| spec.full_name }.each do |spec|
|
773
|
+
full_name = spec.full_name
|
774
|
+
tgz_name = "#{full_name}.tgz"
|
775
|
+
gem_name = "#{full_name}.gem"
|
776
|
+
|
777
|
+
next if File.exist? tgz_name
|
778
|
+
FileUtils.rm_rf [full_name, gem_name]
|
779
|
+
|
780
|
+
begin
|
781
|
+
warn "downloading #{full_name}"
|
782
|
+
Gem::RemoteFetcher.fetcher.download(spec, GEMURL, Dir.pwd)
|
783
|
+
FileUtils.mv "cache/#{gem_name}", '.'
|
784
|
+
rescue Gem::RemoteFetcher::FetchError
|
785
|
+
warn " failed"
|
786
|
+
next
|
787
|
+
end
|
788
|
+
|
789
|
+
warn "converting #{gem_name} to tarball"
|
790
|
+
|
791
|
+
system "gem unpack #{gem_name} 2> /dev/null"
|
792
|
+
system "gem spec -l #{gem_name} > #{full_name}/gemspec.rb"
|
793
|
+
system "tar zmcf #{tgz_name} #{full_name}"
|
794
|
+
FileUtils.rm_rf [full_name, gem_name, "cache"]
|
795
|
+
end
|
796
|
+
ensure
|
797
|
+
FileUtils.rm_rf "cache"
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
801
|
+
end
|
802
|
+
|
803
|
+
############################################################
|
804
|
+
# Misc/Maintenance:
|
805
|
+
|
806
|
+
desc 'Run ZenTest against the package.'
|
807
|
+
task :audit do
|
808
|
+
libs = %w(lib test ext).join(File::PATH_SEPARATOR)
|
809
|
+
sh "zentest -I=#{libs} #{spec.files.grep(/^(lib|test)/).join(' ')}"
|
810
|
+
end
|
811
|
+
|
812
|
+
desc 'Clean up all the extras.'
|
813
|
+
task :clean => [ :clobber_docs, :clobber_package ] do
|
814
|
+
clean_globs.each do |pattern|
|
815
|
+
files = Dir[pattern]
|
816
|
+
rm_rf files, :verbose => true unless files.empty?
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
desc 'Create a fresh ~/.hoerc file.'
|
821
|
+
task :config_hoe do
|
822
|
+
with_config do |config, path|
|
823
|
+
default_config = {
|
824
|
+
"exclude" => /tmp$|CVS|\.svn/,
|
825
|
+
"publish_on_announce" => false,
|
826
|
+
"signing_key_file" => "~/.gem/gem-private_key.pem",
|
827
|
+
"signing_cert_file" => "~/.gem/gem-public_cert.pem",
|
828
|
+
"blogs" => [ {
|
829
|
+
"user" => "user",
|
830
|
+
"url" => "url",
|
831
|
+
"extra_headers" => {
|
832
|
+
"mt_convert_breaks" => "markdown"
|
833
|
+
},
|
834
|
+
"blog_id" => "blog_id",
|
835
|
+
"password"=>"password",
|
836
|
+
} ],
|
837
|
+
}
|
838
|
+
File.open(path, "w") do |f|
|
839
|
+
YAML.dump(default_config.merge(config), f)
|
840
|
+
end
|
841
|
+
|
842
|
+
editor = ENV['EDITOR'] || 'vi'
|
843
|
+
system "#{editor} #{path}" if ENV['SHOW_EDITOR'] != 'no'
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
847
|
+
desc 'Generate email announcement file.'
|
848
|
+
task :email do
|
849
|
+
require 'rubyforge'
|
850
|
+
subject, title, body, urls = announcement
|
851
|
+
|
852
|
+
File.open("email.txt", "w") do |mail|
|
853
|
+
mail.puts "Subject: [ANN] #{subject}"
|
854
|
+
mail.puts
|
855
|
+
mail.puts title
|
856
|
+
mail.puts
|
857
|
+
mail.puts urls
|
858
|
+
mail.puts
|
859
|
+
mail.puts body
|
860
|
+
mail.puts
|
861
|
+
mail.puts urls
|
862
|
+
end
|
863
|
+
puts "Created email.txt"
|
864
|
+
end
|
865
|
+
|
866
|
+
desc 'Post announcement to blog.'
|
867
|
+
task :post_blog do
|
868
|
+
require 'xmlrpc/client'
|
869
|
+
|
870
|
+
with_config do |config, path|
|
871
|
+
break unless config['blogs']
|
872
|
+
|
873
|
+
subject, title, body, urls = announcement
|
874
|
+
body += "\n\n#{urls}"
|
875
|
+
|
876
|
+
config['blogs'].each do |site|
|
877
|
+
server = XMLRPC::Client.new2(site['url'])
|
878
|
+
content = site['extra_headers'].merge(:title => title,
|
879
|
+
:description => body,
|
880
|
+
:categories => blog_categories)
|
881
|
+
|
882
|
+
result = server.call('metaWeblog.newPost',
|
883
|
+
site['blog_id'],
|
884
|
+
site['user'],
|
885
|
+
site['password'],
|
886
|
+
content,
|
887
|
+
true)
|
888
|
+
end
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
892
|
+
desc 'Post announcement to rubyforge.'
|
893
|
+
task :post_news do
|
894
|
+
require 'rubyforge'
|
895
|
+
subject, title, body, urls = announcement
|
896
|
+
|
897
|
+
rf = RubyForge.new.configure
|
898
|
+
rf.login
|
899
|
+
rf.post_news(rubyforge_name, subject, "#{title}\n\n#{body}")
|
900
|
+
puts "Posted to rubyforge"
|
901
|
+
end
|
902
|
+
|
903
|
+
desc 'Create news email file and post to rubyforge.'
|
904
|
+
task :announce => [:email, :post_news, :post_blog, :publish_on_announce ]
|
905
|
+
|
906
|
+
desc 'Verify the manifest.'
|
907
|
+
task :check_manifest => :clean do
|
908
|
+
f = "Manifest.tmp"
|
909
|
+
require 'find'
|
910
|
+
files = []
|
911
|
+
with_config do |config, _|
|
912
|
+
exclusions = config["exclude"]
|
913
|
+
abort "exclude entry missing from .hoerc. Aborting." if exclusions.nil?
|
914
|
+
Find.find '.' do |path|
|
915
|
+
next unless File.file? path
|
916
|
+
next if path =~ exclusions
|
917
|
+
files << path[2..-1]
|
918
|
+
end
|
919
|
+
files = files.sort.join "\n"
|
920
|
+
File.open f, 'w' do |fp| fp.puts files end
|
921
|
+
system "#{DIFF} -du Manifest.txt #{f}"
|
922
|
+
rm f
|
923
|
+
end
|
924
|
+
end
|
925
|
+
|
926
|
+
desc 'Generate a key for signing your gems.'
|
927
|
+
task :generate_key do
|
928
|
+
email = spec.email
|
929
|
+
abort "No email in your gemspec" if email.nil? or email.empty?
|
930
|
+
|
931
|
+
key_file = with_config { |config, _| config['signing_key_file'] }
|
932
|
+
cert_file = with_config { |config, _| config['signing_cert_file'] }
|
933
|
+
|
934
|
+
if key_file.nil? or cert_file.nil? then
|
935
|
+
ENV['SHOW_EDITOR'] ||= 'no'
|
936
|
+
Rake::Task['config_hoe'].invoke
|
937
|
+
|
938
|
+
key_file = with_config { |config, _| config['signing_key_file'] }
|
939
|
+
cert_file = with_config { |config, _| config['signing_cert_file'] }
|
940
|
+
end
|
941
|
+
|
942
|
+
key_file = File.expand_path key_file
|
943
|
+
cert_file = File.expand_path cert_file
|
944
|
+
|
945
|
+
unless File.exist? key_file or File.exist? cert_file then
|
946
|
+
sh "gem cert --build #{email}"
|
947
|
+
mv "gem-private_key.pem", key_file, :verbose => true
|
948
|
+
mv "gem-public_cert.pem", cert_file, :verbose => true
|
949
|
+
|
950
|
+
puts "Installed key and certificate."
|
951
|
+
|
952
|
+
rf = RubyForge.new.configure
|
953
|
+
rf.login
|
954
|
+
|
955
|
+
cert_package = "#{rubyforge_name}-certificates"
|
956
|
+
|
957
|
+
begin
|
958
|
+
rf.lookup 'package', cert_package
|
959
|
+
rescue
|
960
|
+
rf.create_package rubyforge_name, cert_package
|
961
|
+
end
|
962
|
+
|
963
|
+
begin
|
964
|
+
rf.lookup('release', cert_package)['certificates']
|
965
|
+
rf.add_file rubyforge_name, cert_package, 'certificates', cert_file
|
966
|
+
rescue
|
967
|
+
rf.add_release rubyforge_name, cert_package, 'certificates', cert_file
|
968
|
+
end
|
969
|
+
|
970
|
+
puts "Uploaded certificate to release \"certificates\" in package #{cert_package}"
|
971
|
+
else
|
972
|
+
puts "Keys already exist."
|
973
|
+
end
|
974
|
+
end
|
975
|
+
|
976
|
+
end # end define
|
977
|
+
|
978
|
+
def announcement # :nodoc:
|
979
|
+
changes = self.changes.rdoc_to_markdown
|
980
|
+
subject = "#{name} #{version} Released"
|
981
|
+
title = "#{name} version #{version} has been released!"
|
982
|
+
body = "#{description}\n\nChanges:\n\n#{changes}".rdoc_to_markdown
|
983
|
+
urls = Array(url).map { |s| "* <#{s.strip.rdoc_to_markdown}>" }.join("\n")
|
984
|
+
|
985
|
+
return subject, title, body, urls
|
986
|
+
end
|
987
|
+
|
988
|
+
##
|
989
|
+
# Reads a file at +path+ and spits out an array of the +paragraphs+ specified.
|
990
|
+
#
|
991
|
+
# changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
992
|
+
# summary, *description = p.paragraphs_of('README.txt', 3, 3..8)
|
993
|
+
|
994
|
+
def paragraphs_of(path, *paragraphs)
|
995
|
+
File.read(path).delete("\r").split(/\n\n+/).values_at(*paragraphs)
|
996
|
+
end
|
997
|
+
end
|
998
|
+
|
999
|
+
# :enddoc:
|
1000
|
+
|
1001
|
+
class ::Rake::SshDirPublisher # :nodoc:
|
1002
|
+
attr_reader :host, :remote_dir, :local_dir
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
class String
|
1006
|
+
def rdoc_to_markdown
|
1007
|
+
self.gsub(/^mailto:/, '').gsub(/^(=+)/) { "#" * $1.size }
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
if $0 == __FILE__ then
|
1012
|
+
out = `rake -T | egrep -v "redocs|repackage|clobber|trunk"`
|
1013
|
+
if ARGV.empty? then
|
1014
|
+
# # default:: Run the default tasks.
|
1015
|
+
puts out.gsub(/(\s*)\#/, '::\1').gsub(/^rake /, '# ')
|
1016
|
+
else
|
1017
|
+
# * default - Run the default tasks.
|
1018
|
+
puts out.gsub(/\#/, '-').gsub(/^rake /, '* ')
|
1019
|
+
end
|
1020
|
+
end
|