nokogiri 1.0.3 → 1.0.4

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.

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