summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorser <ser@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-09-22 12:40:45 +0000
committerser <ser@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-09-22 12:40:45 +0000
commit8b53e39a2e6110cf13827c88673a3ef2667991cf (patch)
treeeb1fa37c2a675b75c295285b5c313ba05801c2b8 /lib
parentf36a3f0ea57aa05a72cb58937c7a737455a98a38 (diff)
downloadruby-rexml_adds_tests.tar.gz
Second merge from trunk.rexml_adds_tests
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/rexml_adds_tests@19455 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/cmath.rb66
-rw-r--r--lib/complex.rb18
-rw-r--r--lib/csv.rb706
-rw-r--r--lib/mathn.rb14
-rw-r--r--lib/matrix.rb20
-rw-r--r--lib/open-uri.rb12
-rw-r--r--lib/optparse.rb9
-rw-r--r--lib/pathname.rb10
-rw-r--r--lib/pp.rb54
-rw-r--r--lib/prettyprint.rb14
-rwxr-xr-xlib/rake.rb642
-rw-r--r--lib/rake/loaders/makefile.rb23
-rw-r--r--lib/rake/packagetask.rb1
-rw-r--r--lib/rake/rdoctask.rb14
-rw-r--r--lib/rake/tasklib.rb11
-rw-r--r--lib/rake/testtask.rb7
-rw-r--r--lib/resolv.rb40
-rw-r--r--lib/time.rb38
-rw-r--r--lib/tsort.rb20
-rw-r--r--lib/uri/common.rb2
20 files changed, 1205 insertions, 516 deletions
diff --git a/lib/cmath.rb b/lib/cmath.rb
index 55995879e6..d293401804 100644
--- a/lib/cmath.rb
+++ b/lib/cmath.rb
@@ -25,17 +25,17 @@ module CMath
alias atanh! atanh
def exp(z)
- if z.scalar?
+ if z.real?
exp!(z)
else
- Complex(exp!(z.real) * cos!(z.image),
- exp!(z.real) * sin!(z.image))
+ Complex(exp!(z.real) * cos!(z.imag),
+ exp!(z.real) * sin!(z.imag))
end
end
def log(*args)
z, b = args
- if z.scalar? and z >= 0 and (b.nil? or b >= 0)
+ if z.real? and z >= 0 and (b.nil? or b >= 0)
log!(*args)
else
r, theta = z.polar
@@ -48,7 +48,7 @@ module CMath
end
def log10(z)
- if z.scalar?
+ if z.real?
log10!(z)
else
log(z) / log!(10)
@@ -56,14 +56,14 @@ module CMath
end
def sqrt(z)
- if z.scalar?
+ if z.real?
if z >= 0
sqrt!(z)
else
- Complex(0,sqrt!(-z))
+ Complex(0, sqrt!(-z))
end
else
- if z.image < 0
+ if z.imag < 0
sqrt(z.conjugate).conjugate
else
r = z.abs
@@ -74,25 +74,25 @@ module CMath
end
def sin(z)
- if z.scalar?
+ if z.real?
sin!(z)
else
- Complex(sin!(z.real) * cosh!(z.image),
- cos!(z.real) * sinh!(z.image))
+ Complex(sin!(z.real) * cosh!(z.imag),
+ cos!(z.real) * sinh!(z.imag))
end
end
def cos(z)
- if z.scalar?
+ if z.real?
cos!(z)
else
- Complex(cos!(z.real) * cosh!(z.image),
- -sin!(z.real) * sinh!(z.image))
+ Complex(cos!(z.real) * cosh!(z.imag),
+ -sin!(z.real) * sinh!(z.imag))
end
end
def tan(z)
- if z.scalar?
+ if z.real?
tan!(z)
else
sin(z)/cos(z)
@@ -100,25 +100,25 @@ module CMath
end
def sinh(z)
- if z.scalar?
+ if z.real?
sinh!(z)
else
- Complex(sinh!(z.real) * cos!(z.image),
- cosh!(z.real) * sin!(z.image))
+ Complex(sinh!(z.real) * cos!(z.imag),
+ cosh!(z.real) * sin!(z.imag))
end
end
def cosh(z)
- if z.scalar?
+ if z.real?
cosh!(z)
else
- Complex(cosh!(z.real) * cos!(z.image),
- sinh!(z.real) * sin!(z.image))
+ Complex(cosh!(z.real) * cos!(z.imag),
+ sinh!(z.real) * sin!(z.imag))
end
end
def tanh(z)
- if z.scalar?
+ if z.real?
tanh!(z)
else
sinh(z) / cosh(z)
@@ -126,39 +126,39 @@ module CMath
end
def asin(z)
- if z.scalar? and z >= -1 and z <= 1
+ if z.real? and z >= -1 and z <= 1
asin!(z)
else
- -1.0.im * log(1.0.im * z + sqrt(1.0 - z * z))
+ Complex(0, -1.0) * log(Complex(0, 1.0) * z + sqrt(1.0 - z * z))
end
end
def acos(z)
- if z.scalar? and z >= -1 and z <= 1
+ if z.real? and z >= -1 and z <= 1
acos!(z)
else
- -1.0.im * log(z + 1.0.im * sqrt(1.0 - z * z))
+ Complex(0, -1.0) * log(z + Complex(0, 1.0) * sqrt(1.0 - z * z))
end
end
def atan(z)
- if z.scalar?
+ if z.real?
atan!(z)
else
- 1.0.im * log((1.0.im + z) / (1.0.im - z)) / 2.0
+ Complex(0, 1.0) * log((Complex(0, 1.0) + z) / (Complex(0, 1.0) - z)) / 2.0
end
end
def atan2(y,x)
- if y.scalar? and x.scalar?
+ if y.real? and x.real?
atan2!(y,x)
else
- -1.0.im * log((x + 1.0.im * y) / sqrt(x * x + y * y))
+ Complex(0, -1.0) * log((x + Complex(0, 1.0) * y) / sqrt(x * x + y * y))
end
end
def acosh(z)
- if z.scalar? and z >= 1
+ if z.real? and z >= 1
acosh!(z)
else
log(z + sqrt(z * z - 1.0))
@@ -166,7 +166,7 @@ module CMath
end
def asinh(z)
- if z.scalar?
+ if z.real?
asinh!(z)
else
log(z + sqrt(1.0 + z * z))
@@ -174,7 +174,7 @@ module CMath
end
def atanh(z)
- if z.scalar? and z >= -1 and z <= 1
+ if z.real? and z >= -1 and z <= 1
atanh!(z)
else
log((1.0 + z) / (1.0 - z)) / 2.0
diff --git a/lib/complex.rb b/lib/complex.rb
index 1845f30b1f..301879143f 100644
--- a/lib/complex.rb
+++ b/lib/complex.rb
@@ -1,10 +1,24 @@
require 'cmath'
-Object.instance_eval{remove_const :Math}
-Math = CMath
+unless defined?(Math.exp!)
+ Object.instance_eval{remove_const :Math}
+ Math = CMath
+end
def Complex.generic? (other)
other.kind_of?(Integer) ||
other.kind_of?(Float) ||
other.kind_of?(Rational)
end
+
+class Complex
+
+ alias image imag
+
+end
+
+class Numeric
+
+ def im() Complex(0, self) end
+
+end
diff --git a/lib/csv.rb b/lib/csv.rb
index f60d5b1cb0..dccee6cbe8 100644
--- a/lib/csv.rb
+++ b/lib/csv.rb
@@ -1,5 +1,5 @@
-#!/usr/local/bin/ruby -w
-
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
# = csv.rb -- CSV Reading and Writing
#
# Created by James Edward Gray II on 2005-10-31.
@@ -37,6 +37,7 @@
#
# === CSV Parsing
#
+# * This parser is m17n aware. See CSV for full details.
# * This library has a stricter parser and will throw MalformedCSVErrors on
# problematic data.
# * This library has a less liberal idea of a line ending than CSV. What you
@@ -91,7 +92,6 @@
require "forwardable"
require "English"
-require "enumerator"
require "date"
require "stringio"
@@ -130,7 +130,7 @@ require "stringio"
#
# === To a File
#
-# CSV.open("path/to/file.csv", "w") do |csv|
+# CSV.open("path/to/file.csv", "wb") do |csv|
# csv << ["row", "of", "CSV", "data"]
# csv << ["another", "row"]
# # ...
@@ -155,9 +155,51 @@ require "stringio"
# CSV(csv = "") { |csv_str| csv_str << %w{my data here} } # to a String
# CSV($stderr) { |csv_err| csv_err << %w{my data here} } # to $stderr
#
+# == CSV and Character Encodings (M17n or Multilingualization)
+#
+# This new CSV parser is m17n savvy. The parser works in the Encoding of the IO
+# or String object being read from or written to. Your data is never transcoded
+# (unless you ask Ruby to transcode it for you) and will literally be parsed in
+# the Encoding it is in. Thus CSV will return Arrays or Rows of Strings in the
+# Encoding of your data. This is accomplished by transcoding the parser itself
+# into your Encoding.
+#
+# Some transcoding must take place, of course, to accomplish this multiencoding
+# support. For example, <tt>:col_sep</tt>, <tt>:row_sep</tt>, and
+# <tt>:quote_char</tt> must be transcoded to match your data. Hopefully this
+# makes the entire process feel transparent, since CSV's defaults should just
+# magically work for you data. However, you can set these values manually in
+# the target Encoding to avoid the translation.
+#
+# It's also important to note that while all of CSV's core parser is now
+# Encoding agnostic, some features are not. For example, the built-in
+# converters will try to transcode data to UTF-8 before making conversions.
+# Again, you can provide custom converters that are aware of your Encodings to
+# avoid this translation. It's just too hard for me to support native
+# conversions in all of Ruby's Encodings.
+#
+# Anyway, the practical side of this is simple: make sure IO and String objects
+# passed into CSV have the proper Encoding set and everything should just work.
+# CSV methods that allow you to open IO objects (CSV::foreach(), CSV::open(),
+# CSV::read(), and CSV::readlines()) do allow you to specify the Encoding.
+#
+# One minor exception comes when generating CSV into a String with an Encoding
+# that is not ASCII compatible. There's no existing data for CSV to use to
+# prepare itself and thus you will probably need to manually specify the desired
+# Encoding for most of those cases. It will try to guess using the fields in a
+# row of output though, when using CSV::generate_line() or Array#to_csv().
+#
+# I try to point out any other Encoding issues in the documentation of methods
+# as they come up.
+#
+# This has been tested to the best of my ability with all non-"dummy" Encodings
+# Ruby ships with. However, it is brave new code and may have some bugs.
+# Please feel free to {report}[mailto:james@grayproductions.net] any issues you
+# find with it.
+#
class CSV
# The version of the installed library.
- VERSION = "2.0.0".freeze
+ VERSION = "2.4.0".freeze
#
# A CSV::Row is part Array and part Hash. It retains an order for the fields
@@ -188,9 +230,9 @@ class CSV
# handle extra headers or fields
@row = if headers.size > fields.size
- headers.each_with_index.map { |header, i| [header, fields[i]] }
+ headers.zip(fields)
else
- fields.each_with_index.map { |field, i| [headers[i], field] }
+ fields.zip(headers).map { |pair| pair.reverse }
end
end
@@ -444,6 +486,17 @@ class CSV
fields.to_csv(options)
end
alias_method :to_s, :to_csv
+
+ # A summary of fields, by header, in an ASCII-8BIT String.
+ def inspect
+ str = ["#<", self.class.to_s]
+ each do |header, field|
+ str << " " << (header.is_a?(Symbol) ? header.to_s : header.inspect) <<
+ ":" << field.inspect
+ end
+ str << ">"
+ str.map { |s| s.encode("ASCII-8BIT") }.join
+ end
end
#
@@ -775,6 +828,11 @@ class CSV
end.join
end
alias_method :to_s, :to_csv
+
+ # Shows the mode and size of this table in a US-ASCII String.
+ def inspect
+ "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>"
+ end
end
# The error thrown when the parser encounters illegal CSV formatting.
@@ -799,6 +857,10 @@ class CSV
DateTimeMatcher =
/ \A(?: (\w+,?\s+)?\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},?\s+\d{2,4} |
\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2} )\z /x
+
+ # The encoding used by all converters.
+ ConverterEncoding = Encoding.find("UTF-8")
+
#
# This Hash holds the built-in converters of CSV that can be accessed by name.
# You can select Converters with CSV.convert() or through the +options+ Hash
@@ -813,20 +875,38 @@ class CSV
# <b><tt>:all</tt></b>:: All built-in converters. A combination of
# <tt>:date_time</tt> and <tt>:numeric</tt>.
#
+ # All built-in converters transcode field data to UTF-8 before attempting a
+ # conversion. If your data cannot be transcoded to UTF-8 the conversion will
+ # fail and the field will remain unchanged.
+ #
# This Hash is intentionally left unfrozen and users should feel free to add
# values to it that can be accessed by all CSV objects.
#
# To add a combo field, the value should be an Array of names. Combo fields
# can be nested with other combo fields.
#
- Converters = { :integer => lambda { |f| Integer(f) rescue f },
- :float => lambda { |f| Float(f) rescue f },
+ Converters = { :integer => lambda { |f|
+ Integer(f.encode(ConverterEncoding)) rescue f
+ },
+ :float => lambda { |f|
+ Float(f.encode(ConverterEncoding)) rescue f
+ },
:numeric => [:integer, :float],
:date => lambda { |f|
- f =~ DateMatcher ? (Date.parse(f) rescue f) : f
+ begin
+ e = f.encode(ConverterEncoding)
+ e =~ DateMatcher ? Date.parse(e) : f
+ rescue # encoding conversion or date parse errors
+ f
+ end
},
:date_time => lambda { |f|
- f =~ DateTimeMatcher ? (DateTime.parse(f) rescue f) : f
+ begin
+ e = f.encode(ConverterEncoding)
+ e =~ DateTimeMatcher ? DateTime.parse(e) : f
+ rescue # encoding conversion or date parse errors
+ f
+ end
},
:all => [:date_time, :numeric] }
@@ -840,6 +920,10 @@ class CSV
# replaced with underscores, non-word characters
# are dropped, and finally to_sym() is called.
#
+ # All built-in header converters transcode header data to UTF-8 before
+ # attempting a conversion. If your data cannot be transcoded to UTF-8 the
+ # conversion will fail and the header will remain unchanged.
+ #
# This Hash is intetionally left unfrozen and users should feel free to add
# values to it that can be accessed by all CSV objects.
#
@@ -847,9 +931,10 @@ class CSV
# can be nested with other combo fields.
#
HeaderConverters = {
- :downcase => lambda { |h| h.downcase },
+ :downcase => lambda { |h| h.encode(ConverterEncoding).downcase },
:symbol => lambda { |h|
- h.downcase.tr(" ", "_").delete("^a-z0-9_").to_sym
+ h.encode(ConverterEncoding).
+ downcase.tr(" ", "_").delete("^a-z0-9_").to_sym
}
}
@@ -859,6 +944,7 @@ class CSV
# <b><tt>:col_sep</tt></b>:: <tt>","</tt>
# <b><tt>:row_sep</tt></b>:: <tt>:auto</tt>
# <b><tt>:quote_char</tt></b>:: <tt>'"'</tt>
+ # <b><tt>:field_size_limit</tt></b>:: +nil+
# <b><tt>:converters</tt></b>:: +nil+
# <b><tt>:unconverted_fields</tt></b>:: +nil+
# <b><tt>:headers</tt></b>:: +false+
@@ -870,6 +956,7 @@ class CSV
DEFAULT_OPTIONS = { :col_sep => ",",
:row_sep => :auto,
:quote_char => '"',
+ :field_size_limit => nil,
:converters => nil,
:unconverted_fields => nil,
:headers => false,
@@ -879,6 +966,31 @@ class CSV
:force_quotes => false }.freeze
#
+ # This method will return a CSV instance, just like CSV::new(), but the
+ # instance will be cached and returned for all future calls to this method for
+ # the same +data+ object (tested by Object#object_id()) with the same
+ # +options+.
+ #
+ # If a block is given, the instance is passed to the block and the return
+ # value becomes the return value of the block.
+ #
+ def self.instance(data = $stdout, options = Hash.new)
+ # create a _signature_ for this method call, data object and options
+ sig = [data.object_id] +
+ options.values_at(*DEFAULT_OPTIONS.keys.sort_by { |sym| sym.to_s })
+
+ # fetch or create the instance for this signature
+ @@instances ||= Hash.new
+ instance = (@@instances[sig] ||= new(data, options))
+
+ if block_given?
+ yield instance # run block, if given, returning result
+ else
+ instance # or return the instance
+ end
+ end
+
+ #
# This method allows you to serialize an Array of Ruby objects to a String or
# File of CSV data. This is not as powerful as Marshal or YAML, but perhaps
# useful for spreadsheet and database interaction.
@@ -959,6 +1071,53 @@ class CSV
end
#
+ # This method is the reading counterpart to CSV::dump(). See that method for
+ # a detailed description of the process.
+ #
+ # You can customize loading by adding a class method called csv_load() which
+ # will be passed a Hash of meta information, an Array of headers, and an Array
+ # of fields for the object the method is expected to return.
+ #
+ # Remember that all fields will be Strings after this load. If you need
+ # something else, use +options+ to setup converters or provide a custom
+ # csv_load() implementation.
+ #
+ def self.load(io_or_str, options = Hash.new)
+ csv = new(io_or_str, options)
+
+ # load meta information
+ meta = Hash[*csv.shift]
+ cls = meta["class".encode(csv.encoding)].split("::".encode(csv.encoding)).
+ inject(Object) do |c, const|
+ c.const_get(const)
+ end
+
+ # load headers
+ headers = csv.shift
+
+ # unserialize each object stored in the file
+ results = csv.inject(Array.new) do |all, row|
+ begin
+ obj = cls.csv_load(meta, headers, row)
+ rescue NoMethodError
+ obj = cls.allocate
+ headers.zip(row) do |name, value|
+ if name[0] == ?@
+ obj.instance_variable_set(name, value)
+ else
+ obj.send(name, value)
+ end
+ end
+ end
+ all << obj
+ end
+
+ csv.close unless io_or_str.is_a? String
+
+ results
+ end
+
+ #
# :call-seq:
# filter( options = Hash.new ) { |row| ... }
# filter( input, options = Hash.new ) { |row| ... }
@@ -1014,10 +1173,20 @@ class CSV
# pass a +path+ and any +options+ you wish to set for the read. Each row of
# file will be passed to the provided +block+ in turn.
#
- # The +options+ parameter can be anything CSV::new() understands.
+ # The +options+ parameter can be anything CSV::new() understands. This method
+ # also understands an additional <tt>:encoding</tt> parameter that you can use
+ # to specify the Encoding of the data in the file to be read. You must provide
+ # this unless your data is in Encoding::default_external(). CSV will use this
+ # to deterime how to parse the data. You may provide a second Encoding to
+ # have the data transcoded as it is read. For example,
+ # <tt>:encoding => "UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the
+ # file but transcode it to UTF-8 before CSV parses it.
#
def self.foreach(path, options = Hash.new, &block)
- open(path, options) do |csv|
+ encoding = options.delete(:encoding)
+ mode = "rb"
+ mode << ":#{encoding}" if encoding
+ open(path, mode, options) do |csv|
csv.each(&block)
end
end
@@ -1035,7 +1204,10 @@ class CSV
# Note that a passed String *is* modfied by this method. Call dup() before
# passing if you need a new String.
#
- # The +options+ parameter can be anthing CSV::new() understands.
+ # The +options+ parameter can be anthing CSV::new() understands. This method
+ # understands an additional <tt>:encoding</tt> parameter when not passed a
+ # String to set the base Encoding for the output. CSV needs this hint if you
+ # plan to output non-ASCII compatible data.
#
def self.generate(*args)
# add a default empty String, if none was given
@@ -1044,7 +1216,10 @@ class CSV
io.seek(0, IO::SEEK_END)
args.unshift(io)
else
- args.unshift("")
+ encoding = args.last.is_a?(Hash) ? args.last.delete(:encoding) : nil
+ str = ""
+ str.encode!(encoding) if encoding
+ args.unshift(str)
end
csv = new(*args) # wrap
yield csv # yield for appending
@@ -1055,97 +1230,40 @@ class CSV
# This method is a shortcut for converting a single row (Array) into a CSV
# String.
#
- # The +options+ parameter can be anthing CSV::new() understands.
+ # The +options+ parameter can be anthing CSV::new() understands. This method
+ # understands an additional <tt>:encoding</tt> parameter to set the base
+ # Encoding for the output. This method will try to guess your Encoding from
+ # the first non-+nil+ field in +row+, if possible, but you may need to use
+ # this parameter as a backup plan.
#
# The <tt>:row_sep</tt> +option+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>
# (<tt>$/</tt>) when calling this method.
#
def self.generate_line(row, options = Hash.new)
- options = {:row_sep => $INPUT_RECORD_SEPARATOR}.merge(options)
- (new("", options) << row).string
- end
-
- #
- # This method will return a CSV instance, just like CSV::new(), but the
- # instance will be cached and returned for all future calls to this method for
- # the same +data+ object (tested by Object#object_id()) with the same
- # +options+.
- #
- # If a block is given, the instance is passed to the block and the return
- # value becomes the return value of the block.
- #
- def self.instance(data = $stdout, options = Hash.new)
- # create a _signature_ for this method call, data object and options
- sig = [data.object_id] +
- options.values_at(*DEFAULT_OPTIONS.keys.sort_by { |sym| sym.to_s })
-
- # fetch or create the instance for this signature
- @@instances ||= Hash.new
- instance = (@@instances[sig] ||= new(data, options))
-
- if block_given?
- yield instance # run block, if given, returning result
- else
- instance # or return the instance
- end
- end
-
- #
- # This method is the reading counterpart to CSV::dump(). See that method for
- # a detailed description of the process.
- #
- # You can customize loading by adding a class method called csv_load() which
- # will be passed a Hash of meta information, an Array of headers, and an Array
- # of fields for the object the method is expected to return.
- #
- # Remember that all fields will be Strings after this load. If you need
- # something else, use +options+ to setup converters or provide a custom
- # csv_load() implementation.
- #
- def self.load(io_or_str, options = Hash.new)
- csv = new(io_or_str, options)
-
- # load meta information
- meta = Hash[*csv.shift]
- cls = meta["class"].split("::").inject(Object) do |c, const|
- c.const_get(const)
- end
-
- # load headers
- headers = csv.shift
-
- # unserialize each object stored in the file
- results = csv.inject(Array.new) do |all, row|
- begin
- obj = cls.csv_load(meta, headers, row)
- rescue NoMethodError
- obj = cls.allocate
- headers.zip(row) do |name, value|
- if name[0] == ?@
- obj.instance_variable_set(name, value)
- else
- obj.send(name, value)
- end
- end
- end
- all << obj
- end
-
- csv.close unless io_or_str.is_a? String
-
- results
+ options = {:row_sep => $INPUT_RECORD_SEPARATOR}.merge(options)
+ encoding = options.delete(:encoding)
+ str = ""
+ if encoding
+ str.encode!(encoding)
+ elsif field = row.find { |f| not f.nil? }
+ str.encode!(String(field).encoding)
+ end
+ (new(str, options) << row).string
end
#
# :call-seq:
- # open( filename, mode="r", options = Hash.new ) { |csv| ... }
- # open( filename, mode="r", options = Hash.new )
+ # open( filename, mode = "rb", options = Hash.new ) { |faster_csv| ... }
+ # open( filename, options = Hash.new ) { |faster_csv| ... }
+ # open( filename, mode = "rb", options = Hash.new )
+ # open( filename, options = Hash.new )
#
# This method opens an IO object, and wraps that with CSV. This is intended
# as the primary interface for writing a CSV file.
#
- # You may pass any +args+ Ruby's open() understands followed by an optional
- # Hash containing any +options+ CSV::new() understands.
+ # You must pass a +filename+ and may optionally add a +mode+ for Ruby's
+ # open(). You may also pass an optional Hash containing any +options+
+ # CSV::new() understands as the final argument.
#
# This method works like Ruby's open() call, in that it will pass a CSV object
# to a provided block and close it when the block terminates, or it will
@@ -1153,24 +1271,38 @@ class CSV
# from the Ruby 1.8 CSV library which passed rows to the block. Use
# CSV::foreach() for that behavior.)
#
- # An opened CSV object will delegate to many IO methods, for convenience. You
+ # You must provide a +mode+ with an embedded Encoding designator unless your
+ # data is in Encoding::default_external(). CSV will check the Encoding of the
+ # underlying IO object (set by the +mode+ you pass) to deterime how to parse
+ # the data. You may provide a second Encoding to have the data transcoded as
+ # it is read just as you can with a normal call to IO::open(). For example,
+ # <tt>"rb:UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the file but
+ # transcode it to UTF-8 before CSV parses it.
+ #
+ # An opened CSV object will delegate to many IO methods for convenience. You
# may call:
#
# * binmode()
+ # * binmode?()
# * close()
# * close_read()
# * close_write()
# * closed?()
# * eof()
# * eof?()
+ # * external_encoding()
# * fcntl()
# * fileno()
+ # * flock()
# * flush()
# * fsync()
+ # * internal_encoding()
# * ioctl()
# * isatty()
+ # * path()
# * pid()
# * pos()
+ # * pos=()
# * reopen()
# * seek()
# * stat()
@@ -1179,11 +1311,14 @@ class CSV
# * tell()
# * to_i()
# * to_io()
+ # * truncate()
# * tty?()
#
def self.open(*args)
# find the +options+ Hash
options = if args.last.is_a? Hash then args.pop else Hash.new end
+ # default to a binary open mode
+ args << "rb" if args.size == 1
# wrap a File opened with the remaining +args+
csv = new(File.open(*args), options)
@@ -1237,10 +1372,20 @@ class CSV
#
# Use to slurp a CSV file into an Array of Arrays. Pass the +path+ to the
- # file and any +options+ CSV::new() understands.
+ # file and any +options+ CSV::new() understands. This method also understands
+ # an additional <tt>:encoding</tt> parameter that you can use to specify the
+ # Encoding of the data in the file to be read. You must provide this unless
+ # your data is in Encoding::default_external(). CSV will use this to deterime
+ # how to parse the data. You may provide a second Encoding to have the data
+ # transcoded as it is read. For example,
+ # <tt>:encoding => "UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the
+ # file but transcode it to UTF-8 before CSV parses it.
#
def self.read(path, options = Hash.new)
- open(path, options) { |csv| csv.read }
+ encoding = options.delete(:encoding)
+ mode = "rb"
+ mode << ":#{encoding}" if encoding
+ open(path, mode, options) { |csv| csv.read }
end
# Alias for CSV::read().
@@ -1276,6 +1421,8 @@ class CSV
# Available options are:
#
# <b><tt>:col_sep</tt></b>:: The String placed between each field.
+ # This String will be transcoded into
+ # the data's Encoding before parsing.
# <b><tt>:row_sep</tt></b>:: The String appended to the end of each
# row. This can be set to the special
# <tt>:auto</tt> setting, which requests
@@ -1295,7 +1442,16 @@ class CSV
# <tt>$INPUT_RECORD_SEPARATOR</tt>
# (<tt>$/</tt>) is used. Obviously,
# discovery takes a little time. Set
- # manually if speed is important.
+ # manually if speed is important. Also
+ # note that IO objects should be opened
+ # in binary mode on Windows if this
+ # feature will be used as the
+ # line-ending translation can cause
+ # problems with resetting the document
+ # position to where it was before the
+ # read ahead. This String will be
+ # transcoded into the data's Encoding
+ # before parsing.
# <b><tt>:quote_char</tt></b>:: The character used to quote fields.
# This has to be a single character
# String. This is useful for
@@ -1304,11 +1460,31 @@ class CSV
# instead of the correct <tt>"</tt>.
# CSV will always consider a double
# sequence this character to be an
- # escaped quote.
+ # escaped quote. This String will be
+ # transcoded into the data's Encoding
+ # before parsing.
+ # <b><tt>:field_size_limit</tt></b>:: This is a maximum size CSV will read
+ # ahead looking for the closing quote
+ # for a field. (In truth, it reads to
+ # the first line ending beyond this
+ # size.) If a quote cannot be found
+ # within the limit CSV will raise a
+ # MalformedCSVError, assuming the data
+ # is faulty. You can use this limit to
+ # prevent what are effectively DoS
+ # attacks on the parser. However, this
+ # limit can cause a legitimate parse to
+ # fail and thus is set to +nil+, or off,
+ # by default.
# <b><tt>:converters</tt></b>:: An Array of names from the Converters
# Hash and/or lambdas that handle custom
# conversion. A single converter
- # doesn't have to be in an Array.
+ # doesn't have to be in an Array. All
+ # built-in converters try to transcode
+ # fields to UTF-8 before converting.
+ # The conversion will fail if the data
+ # cannot be transcoded, leaving the
+ # field unchanged.
# <b><tt>:unconverted_fields</tt></b>:: If set to +true+, an
# unconverted_fields() method will be
# added to all returned rows (Array or
@@ -1324,11 +1500,14 @@ class CSV
# headers. If set to an Array, the
# contents will be used as the headers.
# If set to a String, the String is run
- # through a call of CSV::parse_line() to
- # produce an Array of headers. This
- # setting causes CSV.shift() to return
+ # through a call of CSV::parse_line()
+ # with the same <tt>:col_sep</tt>,
+ # <tt>:row_sep</tt>, and
+ # <tt>:quote_char</tt> as this instance
+ # to produce an Array of headers. This
+ # setting causes CSV#shift() to return
# rows as CSV::Row objects instead of
- # Arrays and CSV.read() to return
+ # Arrays and CSV#read() to return
# CSV::Table objects instead of an Array
# of Arrays.
# <b><tt>:return_headers</tt></b>:: When +false+, header rows are silently
@@ -1337,10 +1516,17 @@ class CSV
# with identical headers and
# fields (save that the fields do not go
# through the converters).
+ # <b><tt>:write_headers</tt></b>:: When +true+ and <tt>:headers</tt> is
+ # set, a header row will be added to the
+ # output.
# <b><tt>:header_converters</tt></b>:: Identical in functionality to
# <tt>:converters</tt> save that the
# conversions are only made to header
- # rows.
+ # rows. All built-in converters try to
+ # transcode headers to UTF-8 before
+ # converting. The conversion will fail
+ # if the data cannot be transcoded,
+ # leaving the header unchanged.
# <b><tt>:skip_blanks</tt></b>:: When set to a +true+ value, CSV will
# skip over any rows with no content.
# <b><tt>:force_quotes</tt></b>:: When set to a +true+ value, CSV will
@@ -1356,8 +1542,24 @@ class CSV
options = DEFAULT_OPTIONS.merge(options)
# create the IO object we will read from
- @io = if data.is_a? String then StringIO.new(data) else data end
-
+ @io = if data.is_a? String then StringIO.new(data) else data end
+ # honor the IO encoding if we can, otherwise default to ASCII-8BIT
+ @encoding = if @io.respond_to? :internal_encoding
+ @io.internal_encoding || @io.external_encoding
+ elsif @io.is_a? StringIO
+ @io.string.encoding
+ end
+ @encoding ||= Encoding.default_external
+ #
+ # prepare for build safe regular expressions in the target encoding,
+ # if we can transcode the needed characters
+ #
+ @re_esc = "\\".encode(@encoding) rescue ""
+ @re_chars = %w[ \\ . [ ] - ^ $ ?
+ * + { } ( ) | #
+ \ \r \n \t \f \v ].
+ map { |s| s.encode(@encoding) rescue nil }.compact
+
init_separators(options)
init_parsers(options)
init_converters(options)
@@ -1372,6 +1574,79 @@ class CSV
end
#
+ # The encoded <tt>:col_sep</tt> used in parsing and writing. See CSV::new
+ # for details.
+ #
+ attr_reader :col_sep
+ #
+ # The encoded <tt>:row_sep</tt> used in parsing and writing. See CSV::new
+ # for details.
+ #
+ attr_reader :row_sep
+ #
+ # The encoded <tt>:quote_char</tt> used in parsing and writing. See CSV::new
+ # for details.
+ #
+ attr_reader :quote_char
+ # The limit for field size, if any. See CSV::new for details.
+ attr_reader :field_size_limit
+ #
+ # Returns the current list of converters in effect. See CSV::new for details.
+ # Built-in converters will be returned by name, while others will be returned
+ # as is.
+ #
+ def converters
+ @converters.map do |converter|
+ name = Converters.rassoc(converter)
+ name ? name.first : converter
+ end
+ end
+ #
+ # Returns +true+ if unconverted_fields() to parsed results. See CSV::new
+ # for details.
+ #
+ def unconverted_fields?() @unconverted_fields end
+ #
+ # Returns +nil+ if headers will not be used, +true+ if they will but have not
+ # yet been read, or the actual headers after they have been read. See
+ # CSV::new for details.
+ #
+ def headers
+ @headers || true if @use_headers
+ end
+ #
+ # Returns +true+ if headers will be returned as a row of results.
+ # See CSV::new for details.
+ #
+ def return_headers?() @return_headers end
+ # Returns +true+ if headers are written in output. See CSV::new for details.
+ def write_headers?() @write_headers end
+ #
+ # Returns the current list of converters in effect for headers. See CSV::new
+ # for details. Built-in converters will be returned by name, while others
+ # will be returned as is.
+ #
+ def header_converters
+ @header_converters.map do |converter|
+ name = HeaderConverters.rassoc(converter)
+ name ? name.first : converter
+ end
+ end
+ #
+ # Returns +true+ blank lines are skipped by the parser. See CSV::new
+ # for details.
+ #
+ def skip_blanks?() @skip_blanks end
+ # Returns +true+ if all output fields are quoted. See CSV::new for details.
+ def force_quotes?() @force_quotes end
+
+ #
+ # The Encoding CSV is parsing or writing in. This will be the Encoding you
+ # receive parsed data in and/or the Encoding data will be written in.
+ #
+ attr_reader :encoding
+
+ #
# The line number of the last row read from this file. Fields with nested
# line-end characters will not affect this count.
#
@@ -1380,10 +1655,12 @@ class CSV
### IO and StringIO Delegation ###
extend Forwardable
- def_delegators :@io, :binmode, :close, :close_read, :close_write, :closed?,
- :eof, :eof?, :fcntl, :fileno, :flush, :fsync, :ioctl,
- :isatty, :pid, :pos, :reopen, :seek, :stat, :string,
- :sync, :sync=, :tell, :to_i, :to_io, :tty?
+ def_delegators :@io, :binmode, :binmode?, :close, :close_read, :close_write,
+ :closed?, :eof, :eof?, :external_encoding, :fcntl,
+ :fileno, :flock, :flush, :fsync, :internal_encoding,
+ :ioctl, :isatty, :path, :pid, :pos, :pos=, :reopen,
+ :seek, :stat, :string, :sync, :sync=, :tell, :to_i,
+ :to_io, :truncate, :tty?
# Rewinds the underlying IO object and resets CSV's lineno() counter.
def rewind
@@ -1403,12 +1680,18 @@ class CSV
# The data source must be open for writing.
#
def <<(row)
+ # make sure headers have been assigned
+ if header_row? and [Array, String].include? @use_headers.class
+ parse_headers # won't read data for Array or String
+ self << @headers if @write_headers
+ end
+
# handle CSV::Row objects and Hashes
row = case row
- when self.class::Row then row.fields
- when Hash then @headers.map { |header| row[header] }
- else row
- end
+ when self.class::Row then row.fields
+ when Hash then @headers.map { |header| row[header] }
+ else row
+ end
@headers = row if header_row?
@lineno += 1
@@ -1431,7 +1714,7 @@ class CSV
#
# If you provide a block that takes one argument, it will be passed the field
# and is expected to return the converted value or the field itself. If your
- # block takes two arguments, it will also be passed a FieldInfo Struct,
+ # block takes two arguments, it will also be passed a CSV::FieldInfo Struct,
# containing details about the field. Again, the block should return a
# converted field or the field itself.
#
@@ -1445,7 +1728,7 @@ class CSV
# header_convert { |field| ... }
# header_convert { |field, field_info| ... }
#
- # Identical to CSV.convert(), but for header rows.
+ # Identical to CSV#convert(), but for header rows.
#
# Note that this method must be called before header rows are read to have any
# effect.
@@ -1526,7 +1809,7 @@ class CSV
# add another read to the line
(line += @io.gets(@row_sep)) rescue return nil
# copy the line so we can chop it up in parsing
- parse = line.dup
+ parse = line.dup
parse.sub!(@parsers[:line_end], "")
#
@@ -1566,7 +1849,7 @@ class CSV
nil # for Ruby 1.8 CSV compatibility
else
# I decided to take a strict approach to CSV parsing...
- if $2.count("\r\n").zero? # verify correctness of field...
+ if $2.count(@parsers[:return_newline]).zero? # verify correctness
$2
else
# or throw an Exception
@@ -1603,6 +1886,10 @@ class CSV
# if we're not empty?() but at eof?(), a quoted field wasn't closed...
if @io.eof?
raise MalformedCSVError, "Unclosed quoted field on line #{lineno + 1}."
+ elsif parse =~ @parsers[:bad_field]
+ raise MalformedCSVError, "Illegal quoting on line #{lineno + 1}."
+ elsif @field_size_limit and parse.length >= @field_size_limit
+ raise MalformedCSVError, "Field size exceeded on line #{lineno + 1}."
end
# otherwise, we need to loop and pull some more data to complete the row
end
@@ -1610,6 +1897,38 @@ class CSV
alias_method :gets, :shift
alias_method :readline, :shift
+ #
+ # Returns a simplified description of the key FasterCSV attributes in an
+ # ASCII-8BIT String.
+ #
+ def inspect
+ str = ["<#", self.class.to_s, " io_type:"]
+ # show type of wrapped IO
+ if @io == $stdout then str << "$stdout"
+ elsif @io == $stdin then str << "$stdin"
+ elsif @io == $stderr then str << "$stderr"
+ else str << @io.class.to_s
+ end
+ # show IO.path(), if available
+ if @io.respond_to?(:path) and (p = @io.path)
+ str << " io_path:" << p.inspect
+ end
+ # show encoding
+ str << " encoding:" << @encoding.name
+ # show other attributes
+ %w[ lineno col_sep row_sep
+ quote_char skip_blanks ].each do |attr_name|
+ if a = instance_variable_get("@#{attr_name}")
+ str << " " << attr_name << ":" << a.inspect
+ end
+ end
+ if @use_headers
+ str << " headers:" << headers.inspect
+ end
+ str << ">"
+ str.map { |s| s.encode("ASCII-8BIT") }.join
+ end
+
private
#
@@ -1624,15 +1943,18 @@ class CSV
#
def init_separators(options)
# store the selected separators
- @col_sep = options.delete(:col_sep)
- @row_sep = options.delete(:row_sep)
- @quote_char = options.delete(:quote_char)
+ @col_sep = options.delete(:col_sep).to_s.encode(@encoding)
+ @row_sep = options.delete(:row_sep) # encode after resolving :auto
+ @quote_char = options.delete(:quote_char).to_s.encode(@encoding)
if @quote_char.length != 1
raise ArgumentError, ":quote_char has to be a single character String"
end
+ #
# automatically discover row separator when requested
+ # (not fully encoding safe)
+ #
if @row_sep == :auto
if [ARGF, STDIN, STDOUT, STDERR].include?(@io) or
(defined?(Zlib) and @io.class == Zlib::GzipWriter)
@@ -1651,11 +1973,12 @@ class CSV
end
# read ahead a bit
- sample = @io.read(1024)
- sample += @io.read(1) if sample[-1..-1] == "\r" and not @io.eof?
+ sample = read_to_char(1024)
+ sample += read_to_char(1) if sample[-1..-1] == encode_str("\r") and
+ not @io.eof?
# try to find a standard separator
- if sample =~ /\r\n?|\n/
+ if sample =~ encode_re("\r\n?|\n")
@row_sep = $&
break
end
@@ -1673,14 +1996,17 @@ class CSV
end
end
end
+ @row_sep = @row_sep.to_s.encode(@encoding)
# establish quoting rules
- do_quote = lambda do |field|
+ @force_quotes = options.delete(:force_quotes)
+ do_quote = lambda do |field|
@quote_char +
String(field).gsub(@quote_char, @quote_char * 2) +
@quote_char
end
- @quote = if options.delete(:force_quotes)
+ quotable_chars = encode_str("\r\n", @col_sep, @quote_char)
+ @quote = if @force_quotes
do_quote
else
lambda do |field|
@@ -1690,7 +2016,7 @@ class CSV
field = String(field) # Stringify fields
# represent empty fields as empty quoted fields
if field.empty? or
- field.count("\r\n#{@col_sep}#{@quote_char}").nonzero?
+ field.count(quotable_chars).nonzero?
do_quote.call(field)
else
field # unquoted field
@@ -1703,27 +2029,45 @@ class CSV
# Pre-compiles parsers and stores them by name for access during reads.
def init_parsers(options)
# store the parser behaviors
- @skip_blanks = options.delete(:skip_blanks)
+ @skip_blanks = options.delete(:skip_blanks)
+ @field_size_limit = options.delete(:field_size_limit)
# prebuild Regexps for faster parsing
- esc_col_sep = Regexp.escape(@col_sep)
- esc_row_sep = Regexp.escape(@row_sep)
- esc_quote = Regexp.escape(@quote_char)
+ esc_col_sep = escape_re(@col_sep)
+ esc_row_sep = escape_re(@row_sep)
+ esc_quote = escape_re(@quote_char)
@parsers = {
- :leading_fields =>
- /\A(?:#{esc_col_sep})+/, # for empty leading fields
- :csv_row =>
- ### The Primary Parser ###
- / \G(?:^|#{esc_col_sep}) # anchor the match
- (?: #{esc_quote}( (?>[^#{esc_quote}]*) # find quoted fields
- (?> #{esc_quote*2}
- [^#{esc_quote}]* )* )#{esc_quote}
- | # ... or ...
- ([^#{esc_quote}#{esc_col_sep}]*) # unquoted fields
- )/x,
- ### End Primary Parser ###
- :line_end =>
- /#{esc_row_sep}\z/ # safer than chomp!()
+ # for empty leading fields
+ :leading_fields => encode_re("\\A(?:", esc_col_sep, ")+"),
+ # The Primary Parser
+ :csv_row => encode_re(
+ "\\G(?:\\A|", esc_col_sep, ")", # anchor the match
+ "(?:", esc_quote, # find quoted fields
+ "((?>[^", esc_quote, "]*)", # "unrolling the loop"
+ "(?>", esc_quote * 2, # double for escaping
+ "[^", esc_quote, "]*)*)",
+ esc_quote,
+ "|", # ... or ...
+ "([^", esc_quote, esc_col_sep, "]*))", # unquoted fields
+ "(?=", esc_col_sep, "|\\z)" # ensure field is ended
+ ),
+ # a test for unescaped quotes
+ :bad_field => encode_re(
+ "\\A", esc_col_sep, "?", # an optional comma
+ "(?:", esc_quote, # a quoted field
+ "(?>[^", esc_quote, "]*)", # "unrolling the loop"
+ "(?>", esc_quote * 2, # double for escaping
+ "[^", esc_quote, "]*)*",
+ esc_quote, # the closing quote
+ "[^", esc_quote, "]", # an extra character
+ "|", # ... or ...
+ "[^", esc_quote, esc_col_sep, "]+", # an unquoted field
+ esc_quote, ")" # an extra quote
+ ),
+ # safer than chomp!()
+ :line_end => encode_re(esc_row_sep, "\\z"),
+ # illegal unquoted characters
+ :return_newline => encode_str("\r\n")
}
end
@@ -1770,6 +2114,7 @@ class CSV
def init_headers(options)
@use_headers = options.delete(:headers)
@return_headers = options.delete(:return_headers)
+ @write_headers = options.delete(:write_headers)
# headers must be delayed until shift(), in case they need a row of content
@headers = nil
@@ -1812,7 +2157,7 @@ class CSV
# see if we are converting headers or fields
converters = headers ? @header_converters : @converters
- fields.each_with_index.map do |field, index| # map_with_index
+ fields.map.with_index do |field, index|
converters.each do |converter|
field = if converter.arity == 1 # straight field converter
converter[field]
@@ -1839,10 +2184,17 @@ class CSV
def parse_headers(row = nil)
if @headers.nil? # header row
@headers = case @use_headers # save headers
- when Array then @use_headers # Array of headers
- when String then self.class.parse_line(@use_headers) # CSV header String
- else row # first row headers
- end
+ # Array of headers
+ when Array then @use_headers
+ # CSV header String
+ when String
+ self.class.parse_line( @use_headers,
+ :col_sep => @col_sep,
+ :row_sep => @row_sep,
+ :quote_char => @quote_char )
+ # first row is headers
+ else row
+ end
# prepare converted and unconverted copies
row = @headers if row.nil?
@@ -1870,6 +2222,56 @@ class CSV
row.instance_eval { @unconverted_fields = fields }
row
end
+
+ #
+ # This method is an encoding safe version of Regexp::escape(). I will escape
+ # any characters that would change the meaning of a regular expression in the
+ # encoding of +str+. Regular expression characters that cannot be transcoded
+ # to the target encodign will be skipped and no escaping will be performed if
+ # a backslash cannot be transcoded.
+ #
+ def escape_re(str)
+ str.chars.map { |c| @re_chars.include?(c) ? @re_esc + c : c }.join
+ end
+
+ #
+ # Builds a regular expression in <tt>@encoding</tt>. All +chunks+ will be
+ # transcoded to that encoding.
+ #
+ def encode_re(*chunks)
+ Regexp.new(encode_str(*chunks))
+ end
+
+ #
+ # Builds a String in <tt>@encoding</tt>. All +chunks+ will be transcoded to
+ # that encoding.
+ #
+ def encode_str(*chunks)
+ chunks.map { |chunk| chunk.encode(@encoding.name) }.join
+ end
+
+ #
+ # Reads at least +bytes+ from <tt>@io</tt>, but will read on until the data
+ # read is valid in the ecoding of that data. This should ensure that it is
+ # safe to use regular expressions on the read data. The read data will be
+ # returned in <tt>@encoding</tt>.
+ #
+ def read_to_char(bytes)
+ return "" if @io.eof?
+ data = @io.read(bytes)
+ begin
+ encoded = encode_str(data)
+ raise unless encoded.valid_encoding?
+ return encoded
+ rescue # encoding error or my invalid data raise
+ if @io.eof?
+ return data
+ else
+ data += @io.read(1) until data.valid_encoding? or @io.eof?
+ retry
+ end
+ end
+ end
end
# Another name for CSV::instance().
diff --git a/lib/mathn.rb b/lib/mathn.rb
index 2af2b83da3..e918608b0d 100644
--- a/lib/mathn.rb
+++ b/lib/mathn.rb
@@ -9,11 +9,15 @@
#
#
-require "complex.rb"
-require "rational.rb"
+require "cmath.rb"
require "matrix.rb"
require "prime.rb"
+unless defined?(Math.exp!)
+ Object.instance_eval{remove_const :Math}
+ Math = CMath
+end
+
class Fixnum
remove_method :/
alias / quo
@@ -33,7 +37,7 @@ class Rational
if other.kind_of?(Rational)
other2 = other
if self < 0
- return Complex.__send__(:new!, self, 0) ** other
+ return Complex(self, 0.0) ** other
elsif other == 0
return Rational(1,1)
elsif self == 0
@@ -95,7 +99,7 @@ module Math
remove_method(:sqrt)
def sqrt(a)
if a.kind_of?(Complex)
- abs = sqrt(a.real*a.real + a.image*a.image)
+ abs = sqrt(a.real*a.real + a.imag*a.imag)
# if not abs.kind_of?(Rational)
# return a**Rational(1,2)
# end
@@ -104,7 +108,7 @@ module Math
# if !(x.kind_of?(Rational) and y.kind_of?(Rational))
# return a**Rational(1,2)
# end
- if a.image >= 0
+ if a.imag >= 0
Complex(x, y)
else
Complex(x, -y)
diff --git a/lib/matrix.rb b/lib/matrix.rb
index d31fcc5464..c672ee5198 100644
--- a/lib/matrix.rb
+++ b/lib/matrix.rb
@@ -410,17 +410,21 @@ class Matrix
other.compare_by_row_vectors(@rows)
end
- alias eql? ==
+ def eql?(other)
+ return false unless Matrix === other
+
+ other.compare_by_row_vectors(@rows, :eql?)
+ end
#
# Not really intended for general consumption.
#
- def compare_by_row_vectors(rows)
+ def compare_by_row_vectors(rows, comparison = :==)
return false unless @rows.size == rows.size
0.upto(@rows.size - 1) do
|i|
- return false unless @rows[i] == rows[i]
+ return false unless @rows[i].send(comparison, rows[i])
end
true
end
@@ -1200,13 +1204,17 @@ class Vector
other.compare_by(@elements)
end
- alias eqn? ==
+ def eql?(other)
+ return false unless Vector === other
+
+ other.compare_by(@elements, :eql?)
+ end
#
# For internal use.
#
- def compare_by(elements)
- @elements == elements
+ def compare_by(elements, comparison = :==)
+ @elements.send(comparison, elements)
end
#
diff --git a/lib/open-uri.rb b/lib/open-uri.rb
index 238d759d5f..c68aee6e97 100644
--- a/lib/open-uri.rb
+++ b/lib/open-uri.rb
@@ -542,7 +542,7 @@ module OpenURI
# :proxy => true
# :proxy => false
# :proxy => nil
- #
+ #
# If :proxy option is specified, the value should be String, URI,
# boolean or nil.
# When String or URI is given, it is treated as proxy URI.
@@ -556,7 +556,7 @@ module OpenURI
# Synopsis:
# :proxy_http_basic_authentication => ["http://proxy.foo.com:8000/", "proxy-user", "proxy-password"]
# :proxy_http_basic_authentication => [URI.parse("http://proxy.foo.com:8000/"), "proxy-user", "proxy-password"]
- #
+ #
# If :proxy option is specified, the value should be an Array with 3 elements.
# It should contain a proxy URI, a proxy user name and a proxy password.
# The proxy URI should be a String, an URI or nil.
@@ -564,7 +564,7 @@ module OpenURI
#
# If nil is given for the proxy URI, this option is just ignored.
#
- # If :proxy and :proxy_http_basic_authentication is specified,
+ # If :proxy and :proxy_http_basic_authentication is specified,
# ArgumentError is raised.
#
# [:http_basic_authentication]
@@ -579,14 +579,14 @@ module OpenURI
# [:content_length_proc]
# Synopsis:
# :content_length_proc => lambda {|content_length| ... }
- #
+ #
# If :content_length_proc option is specified, the option value procedure
# is called before actual transfer is started.
# It takes one argument which is expected content length in bytes.
- #
+ #
# If two or more transfer is done by HTTP redirection, the procedure
# is called only one for a last transfer.
- #
+ #
# When expected content length is unknown, the procedure is called with
# nil.
# It is happen when HTTP response has no Content-Length header.
diff --git a/lib/optparse.rb b/lib/optparse.rb
index 86681235a0..f457b072a9 100644
--- a/lib/optparse.rb
+++ b/lib/optparse.rb
@@ -632,7 +632,7 @@ class OptionParser
list.each do |opt|
if opt.respond_to?(:summarize) # perhaps OptionParser::Switch
opt.summarize(*args, &block)
- elsif !opt
+ elsif !opt or opt.empty?
yield("")
elsif opt.respond_to?(:each_line)
opt.each_line(&block)
@@ -1605,6 +1605,13 @@ class OptionParser
argv
end
+ def set_backtrace(array)
+ unless $DEBUG
+ array.delete_if(&%r"\A#{Regexp.quote(__FILE__)}:"o.method(:=~))
+ end
+ super(array)
+ end
+
def set_option(opt, eq)
if eq
@args[0] = opt
diff --git a/lib/pathname.rb b/lib/pathname.rb
index 7bee22b58f..45974545a6 100644
--- a/lib/pathname.rb
+++ b/lib/pathname.rb
@@ -43,7 +43,7 @@
# base = p.basename # Pathname:ruby
# dir, base = p.split # [Pathname:/usr/bin, Pathname:ruby]
# data = p.read
-# p.open { |f| _ }
+# p.open { |f| _ }
# p.each_line { |line| _ }
#
# === Example 2: Using standard Ruby
@@ -55,7 +55,7 @@
# base = File.basename(p) # "ruby"
# dir, base = File.split(p) # ["/usr/bin", "ruby"]
# data = File.read(p)
-# File.open(p) { |f| _ }
+# File.open(p) { |f| _ }
# File.foreach(p) { |line| _ }
#
# === Example 3: Special features
@@ -71,7 +71,7 @@
# p5.cleanpath # Pathname:articles
# p5.realpath # Pathname:/home/gavin/articles
# p5.children # [Pathname:/home/gavin/articles/linux, ...]
-#
+#
# == Breakdown of functionality
#
# === Core methods
@@ -452,7 +452,7 @@ class Pathname
# Returns a real (absolute) pathname of +self+ in the actual filesystem.
# The real pathname doesn't contain symlinks or useless dots.
#
- # No arguments should be given; the old behaviour is *obsoleted*.
+ # No arguments should be given; the old behaviour is *obsoleted*.
#
def realpath
path = @path
@@ -587,7 +587,7 @@ class Pathname
# p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
# p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd
#
- # This method doesn't access the file system; it is pure string manipulation.
+ # This method doesn't access the file system; it is pure string manipulation.
#
def +(other)
other = Pathname.new(other) unless Pathname === other
diff --git a/lib/pp.rb b/lib/pp.rb
index ec896b3ce5..41f51b0046 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -1,10 +1,10 @@
# == Pretty-printer for Ruby objects.
-#
+#
# = Which seems better?
-#
+#
# non-pretty-printed output by #p is:
# #<PP:0x81fedf0 @genspace=#<Proc:0x81feda0>, @group_queue=#<PrettyPrint::GroupQueue:0x81fed3c @queue=[[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], []]>, @buffer=[], @newline="\n", @group_stack=[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#<IO:0x8114ee4>>
-#
+#
# pretty-printed output by #pp is:
# #<PP:0x81fedf0
# @buffer=[],
@@ -22,17 +22,17 @@
# @newline="\n",
# @output=#<IO:0x8114ee4>,
# @output_width=2>
-#
+#
# I like the latter. If you do too, this library is for you.
-#
+#
# = Usage
-#
+#
# pp(obj)
#
# output +obj+ to +$>+ in pretty printed format.
-#
+#
# It returns +nil+.
-#
+#
# = Output Customization
# To define your customized pretty printing function for your classes,
# redefine a method #pretty_print(+pp+) in the class.
@@ -67,10 +67,10 @@ end
class PP < PrettyPrint
# Outputs +obj+ to +out+ in pretty printed format of
# +width+ columns in width.
- #
+ #
# If +out+ is omitted, +$>+ is assumed.
# If +width+ is omitted, 79 is assumed.
- #
+ #
# PP.pp returns +out+.
def PP.pp(obj, out=$>, width=79)
q = PP.new(out, width)
@@ -82,7 +82,7 @@ class PP < PrettyPrint
# Outputs +obj+ to +out+ like PP.pp but with no indent and
# newline.
- #
+ #
# PP.singleline_pp returns +out+.
def PP.singleline_pp(obj, out=$>)
q = SingleLine.new(out)
@@ -138,12 +138,12 @@ class PP < PrettyPrint
# Adds +obj+ to the pretty printing buffer
# using Object#pretty_print or Object#pretty_print_cycle.
- #
+ #
# Object#pretty_print_cycle is used when +obj+ is already
# printed, a.k.a the object reference chain has a cycle.
def pp(obj)
id = obj.object_id
-
+
if check_inspect_key(id)
group {obj.pretty_print_cycle self}
return
@@ -158,7 +158,7 @@ class PP < PrettyPrint
end
# A convenience method which is same as follows:
- #
+ #
# group(1, '#<' + obj.class.name, '>') { ... }
def object_group(obj, &block) # :yield:
group(1, '#<' + obj.class.name, '>', &block)
@@ -185,7 +185,7 @@ class PP < PrettyPrint
end
# A convenience method which is same as follows:
- #
+ #
# text ','
# breakable
def comma_breakable
@@ -195,23 +195,23 @@ class PP < PrettyPrint
# Adds a separated list.
# The list is separated by comma with breakable space, by default.
- #
+ #
# #seplist iterates the +list+ using +iter_method+.
# It yields each object to the block given for #seplist.
# The procedure +separator_proc+ is called between each yields.
- #
+ #
# If the iteration is zero times, +separator_proc+ is not called at all.
- #
+ #
# If +separator_proc+ is nil or not given,
# +lambda { comma_breakable }+ is used.
# If +iter_method+ is not given, :each is used.
- #
+ #
# For example, following 3 code fragments has similar effect.
- #
+ #
# q.seplist([1,2,3]) {|v| xxx v }
- #
+ #
# q.seplist([1,2,3], lambda { q.comma_breakable }, :each) {|v| xxx v }
- #
+ #
# xxx 1
# q.comma_breakable
# xxx 2
@@ -275,11 +275,11 @@ class PP < PrettyPrint
# A default pretty printing method for general objects.
# It calls #pretty_print_instance_variables to list instance variables.
- #
+ #
# If +self+ has a customized (redefined) #inspect method,
# the result of self.inspect is used but it obviously has no
# line break hints.
- #
+ #
# This module provides predefined #pretty_print methods for some of
# the most commonly used built-in classes for convenience.
def pretty_print(q)
@@ -302,7 +302,7 @@ class PP < PrettyPrint
end
# Returns a sorted array of instance variable names.
- #
+ #
# This method should return an array of names of instance variables as symbols or strings as:
# +[:@a, :@b]+.
def pretty_print_instance_variables
@@ -311,7 +311,7 @@ class PP < PrettyPrint
# Is #inspect implementation using #pretty_print.
# If you implement #pretty_print, it can be used as follows.
- #
+ #
# alias inspect pretty_print_inspect
#
# However, doing this requires that every class that #inspect is called on
@@ -629,7 +629,7 @@ if __FILE__ == $0
result = PP.pp(a, '')
assert_equal("#{a.inspect}\n", result)
end
-
+
def test_to_s_without_iv
a = Object.new
def a.to_s() "aaa" end
diff --git a/lib/prettyprint.rb b/lib/prettyprint.rb
index 315c422e9e..48f2ebf1e4 100644
--- a/lib/prettyprint.rb
+++ b/lib/prettyprint.rb
@@ -2,7 +2,7 @@
# This class implements a pretty printing algorithm. It finds line breaks and
# nice indentations for grouped structure.
-#
+#
# By default, the class assumes that primitive elements are strings and each
# byte in the strings have single column in width. But it can be used for
# other situations by giving suitable arguments for some methods:
@@ -18,28 +18,28 @@
# == Bugs
# * Box based formatting?
# * Other (better) model/algorithm?
-#
+#
# == References
# Christian Lindig, Strictly Pretty, March 2000,
# http://www.st.cs.uni-sb.de/~lindig/papers/#pretty
-#
+#
# Philip Wadler, A prettier printer, March 1998,
# http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
-#
+#
# == Author
# Tanaka Akira <akr@m17n.org>
-#
+#
class PrettyPrint
# This is a convenience method which is same as follows:
- #
+ #
# begin
# q = PrettyPrint.new(output, maxwidth, newline, &genspace)
# ...
# q.flush
# output
# end
- #
+ #
def PrettyPrint.format(output='', maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n})
q = PrettyPrint.new(output, maxwidth, newline, &genspace)
yield q
diff --git a/lib/rake.rb b/lib/rake.rb
index 0b21eb7350..caafa5caf0 100755
--- a/lib/rake.rb
+++ b/lib/rake.rb
@@ -29,13 +29,13 @@
# as a library via a require statement, but it can be distributed
# independently as an application.
-RAKEVERSION = '0.8.0'
+RAKEVERSION = '0.8.2'
require 'rbconfig'
-require 'getoptlong'
require 'fileutils'
require 'singleton'
-require 'thread'
+require 'monitor'
+require 'optparse'
require 'ostruct'
######################################################################
@@ -239,6 +239,33 @@ end # class String
##############################################################################
module Rake
+ # Errors -----------------------------------------------------------
+
+ # Error indicating an ill-formed task declaration.
+ class TaskArgumentError < ArgumentError
+ end
+
+ # Error indicating a recursion overflow error in task selection.
+ class RuleRecursionOverflowError < StandardError
+ def initialize(*args)
+ super
+ @targets = []
+ end
+
+ def add_target(target)
+ @targets << target
+ end
+
+ def message
+ super + ": [" + @targets.reverse.join(' => ') + "]"
+ end
+ end
+
+ # Error indicating a problem in locating the home directory on a
+ # Win32 system.
+ class Win32HomeError < RuntimeError
+ end
+
# --------------------------------------------------------------------------
# Rake module singleton methods.
#
@@ -266,16 +293,22 @@ module Rake
module Cloneable
# Clone an object by making a new object and setting all the instance
# variables to the same values.
- def clone
+ def dup
sibling = self.class.new
instance_variables.each do |ivar|
value = self.instance_variable_get(ivar)
new_value = value.clone rescue value
sibling.instance_variable_set(ivar, new_value)
end
+ sibling.taint if tainted?
+ sibling
+ end
+
+ def clone
+ sibling = dup
+ sibling.freeze if frozen?
sibling
end
- alias dup clone
end
####################################################################
@@ -286,12 +319,15 @@ module Rake
attr_reader :names
+ # Create a TaskArgument object with a list of named arguments
+ # (given by :names) and a set of associated values (given by
+ # :values). :parent is the parent argument object.
def initialize(names, values, parent=nil)
@names = names
@parent = parent
@hash = {}
names.each_with_index { |name, i|
- @hash[name.to_sym] = values[i]
+ @hash[name.to_sym] = values[i] unless values[i].nil?
}
end
@@ -307,6 +343,13 @@ module Rake
lookup(index.to_sym)
end
+ # Specify a hash of default values for task arguments. Use the
+ # defaults only if there is no specific value for the given
+ # argument.
+ def with_defaults(defaults)
+ @hash = defaults.merge(@hash)
+ end
+
def each(&block)
@hash.each(&block)
end
@@ -342,6 +385,8 @@ module Rake
end
end
+ EMPTY_TASK_ARGS = TaskArguments.new([], [])
+
####################################################################
# InvocationChain tracks the chain of task invocations to detect
# circular dependencies.
@@ -409,6 +454,9 @@ module Rake
# List of prerequisites for a task.
attr_reader :prerequisites
+ # List of actions attached to a task.
+ attr_reader :actions
+
# Application owning this task.
attr_accessor :application
@@ -446,12 +494,12 @@ module Rake
# +enhance+ to add actions and prerequisites.
def initialize(task_name, app)
@name = task_name.to_s
- @prerequisites = FileList[]
+ @prerequisites = []
@actions = []
@already_invoked = false
@full_comment = nil
@comment = nil
- @lock = Mutex.new
+ @lock = Monitor.new
@application = app
@scope = app.current_scope
@arg_names = nil
@@ -488,6 +536,31 @@ module Rake
@arg_names || []
end
+ # Reenable the task, allowing its tasks to be executed if the task
+ # is invoked again.
+ def reenable
+ @already_invoked = false
+ end
+
+ # Clear the existing prerequisites and actions of a rake task.
+ def clear
+ clear_prerequisites
+ clear_actions
+ self
+ end
+
+ # Clear the existing prerequisites of a rake task.
+ def clear_prerequisites
+ prerequisites.clear
+ self
+ end
+
+ # Clear the existing actions on a rake task.
+ def clear_actions
+ actions.clear
+ self
+ end
+
# Invoke the task if it is needed. Prerequites are invoked first.
def invoke(*args)
task_args = TaskArguments.new(arg_names, args)
@@ -496,7 +569,7 @@ module Rake
# Same as invoke, but explicitly pass a call chain to detect
# circular dependencies.
- def invoke_with_call_chain(task_args, invocation_chain)
+ def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
new_chain = InvocationChain.append(self, invocation_chain)
@lock.synchronize do
if application.options.trace
@@ -511,7 +584,7 @@ module Rake
protected :invoke_with_call_chain
# Invoke all the prerequisites of a task.
- def invoke_prerequisites(task_args, invocation_chain)
+ def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
@prerequisites.each { |n|
prereq = application[n, @scope]
prereq_args = task_args.new_scope(prereq.arg_names)
@@ -529,7 +602,8 @@ module Rake
private :format_trace_flags
# Execute the actions associated with this task.
- def execute(args)
+ def execute(args=nil)
+ args ||= EMPTY_TASK_ARGS
if application.options.dryrun
puts "** Execute (dry run) #{name}"
return
@@ -772,8 +846,8 @@ end
# end
# end
#
-def file(args, &block)
- Rake::FileTask.define_task(args, &block)
+def file(*args, &block)
+ Rake::FileTask.define_task(*args, &block)
end
# Declare a file creation task.
@@ -899,14 +973,38 @@ module FileUtils
ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
}
end
+ if RakeFileUtils.verbose_flag == :default
+ options[:verbose] = false
+ else
+ options[:verbose] ||= RakeFileUtils.verbose_flag
+ end
+ options[:noop] ||= RakeFileUtils.nowrite_flag
rake_check_options options, :noop, :verbose
rake_output_message cmd.join(" ") if options[:verbose]
unless options[:noop]
- res = system(*cmd)
+ res = rake_system(*cmd)
block.call(res, $?)
end
end
+ def rake_system(*cmd)
+ if Rake.application.windows?
+ rake_win32_system(*cmd)
+ else
+ system(*cmd)
+ end
+ end
+ private :rake_system
+
+ def rake_win32_system(*cmd)
+ if cmd.size == 1
+ system("call #{cmd}")
+ else
+ system(*cmd)
+ end
+ end
+ private :rake_win32_system
+
# Run a Ruby interpreter with the given arguments.
#
# Example:
@@ -917,7 +1015,7 @@ module FileUtils
if args.length > 1 then
sh(*([RUBY] + args + [options]), &block)
else
- sh("#{RUBY.sub(/.*\s.*/m, '"\&"')} #{args.first}", options, &block)
+ sh("#{RUBY} #{args.first}", options, &block)
end
end
@@ -961,7 +1059,7 @@ module RakeFileUtils
class << self
attr_accessor :verbose_flag, :nowrite_flag
end
- RakeFileUtils.verbose_flag = true
+ RakeFileUtils.verbose_flag = :default
RakeFileUtils.nowrite_flag = false
$fileutils_verbose = true
@@ -969,10 +1067,10 @@ module RakeFileUtils
FileUtils::OPT_TABLE.each do |name, opts|
default_options = []
- if opts.include?('verbose')
+ if opts.include?(:verbose) || opts.include?("verbose")
default_options << ':verbose => RakeFileUtils.verbose_flag'
end
- if opts.include?('noop')
+ if opts.include?(:noop) || opts.include?("noop")
default_options << ':noop => RakeFileUtils.nowrite_flag'
end
@@ -1029,7 +1127,7 @@ module RakeFileUtils
oldvalue
end
- # Use this function to prevent potentially destructive ruby code from
+ # Use this function to prevent protentially destructive ruby code from
# running when the :nowrite flag is set.
#
# Example:
@@ -1094,21 +1192,6 @@ private(*RakeFileUtils.instance_methods(false))
######################################################################
module Rake
- class RuleRecursionOverflowError < StandardError
- def initialize(*args)
- super
- @targets = []
- end
-
- def add_target(target)
- @targets << target
- end
-
- def message
- super + ": [" + @targets.reverse.join(' => ') + "]"
- end
- end
-
# #########################################################################
# A FileList is essentially an array with a few helper methods defined to
# make file manipulation a bit easier.
@@ -1352,7 +1435,7 @@ module Rake
private :resolve_exclude
# Return a new FileList with the results of running +sub+ against each
- # element of the original list.
+ # element of the oringal list.
#
# Example:
# FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
@@ -1503,7 +1586,7 @@ module Rake
class << self
# Yield each file or directory component.
- def each_dir_parent(dir)
+ def each_dir_parent(dir) # :nodoc:
old_length = nil
while dir != '.' && dir.length != old_length
yield(dir)
@@ -1642,23 +1725,65 @@ module Rake
# Resolve the arguments for a task/rule. Returns a triplet of
# [task_name, arg_name_list, prerequisites].
def resolve_args(args)
+ if args.last.is_a?(Hash)
+ deps = args.pop
+ resolve_args_with_dependencies(args, deps)
+ else
+ resolve_args_without_dependencies(args)
+ end
+ end
+
+ # Resolve task arguments for a task or rule when there are no
+ # dependencies declared.
+ #
+ # The patterns recognized by this argument resolving function are:
+ #
+ # task :t
+ # task :t, [:a]
+ # task :t, :a (deprecated)
+ #
+ def resolve_args_without_dependencies(args)
task_name = args.shift
- arg_names = args #.map { |a| a.to_sym }
- needs = []
- if task_name.is_a?(Hash)
- hash = task_name
- task_name = hash.keys[0]
- needs = hash[task_name]
+ if args.size == 1 && args.first.respond_to?(:to_ary)
+ arg_names = args.first.to_ary
+ else
+ arg_names = args
end
- if arg_names.last.is_a?(Hash)
- hash = arg_names.pop
- needs = hash[:needs]
- fail "Unrecognized keys in task hash: #{hash.keys.inspect}" if hash.size > 1
+ [task_name, arg_names, []]
+ end
+ private :resolve_args_without_dependencies
+
+ # Resolve task arguments for a task or rule when there are
+ # dependencies declared.
+ #
+ # The patterns recognized by this argument resolving function are:
+ #
+ # task :t => [:d]
+ # task :t, [a] => [:d]
+ # task :t, :needs => [:d] (deprecated)
+ # task :t, :a, :needs => [:d] (deprecated)
+ #
+ def resolve_args_with_dependencies(args, hash) # :nodoc:
+ fail "Task Argument Error" if hash.size != 1
+ key, value = hash.map { |k, v| [k,v] }.first
+ if args.empty?
+ task_name = key
+ arg_names = []
+ deps = value
+ elsif key == :needs
+ task_name = args.shift
+ arg_names = args
+ deps = value
+ else
+ task_name = args.shift
+ arg_names = key
+ deps = value
end
- needs = [needs] unless needs.respond_to?(:to_ary)
- [task_name, arg_names, needs]
+ deps = [deps] unless deps.respond_to?(:to_ary)
+ [task_name, arg_names, deps]
end
-
+ private :resolve_args_with_dependencies
+
# If a rule can be found that matches the task name, enhance the
# task with the prerequisites and actions from the rule. Set the
# source attribute of the task appropriately for the rule. Return
@@ -1749,15 +1874,23 @@ module Rake
"_anon_#{@seed}"
end
+ def trace_rule(level, message)
+ puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
+ end
+
# Attempt to create a rule given the list of prerequisites.
def attempt_rule(task_name, extensions, block, level)
sources = make_sources(task_name, extensions)
prereqs = sources.collect { |source|
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
if File.exist?(source) || Rake::Task.task_defined?(source)
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
source
- elsif parent = enhance_with_matching_rule(sources.first, level+1)
+ elsif parent = enhance_with_matching_rule(source, level+1)
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
parent.name
else
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
return nil
end
}
@@ -1814,41 +1947,6 @@ module Rake
DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
- OPTIONS = [ # :nodoc:
- ['--classic-namespace', '-C', GetoptLong::NO_ARGUMENT,
- "Put Task and FileTask in the top level namespace"],
- ['--describe', '-D', GetoptLong::OPTIONAL_ARGUMENT,
- "Describe the tasks (matching optional PATTERN), then exit."],
- ['--rakefile', '-f', GetoptLong::OPTIONAL_ARGUMENT,
- "Use FILE as the rakefile."],
- ['--help', '-h', '-H', GetoptLong::NO_ARGUMENT,
- "Display this help message."],
- ['--libdir', '-I', GetoptLong::REQUIRED_ARGUMENT,
- "Include LIBDIR in the search path for required modules."],
- ['--dry-run', '-n', GetoptLong::NO_ARGUMENT,
- "Do a dry run without executing actions."],
- ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
- "Do not search parent directories for the Rakefile."],
- ['--prereqs', '-P', GetoptLong::NO_ARGUMENT,
- "Display the tasks and dependencies, then exit."],
- ['--quiet', '-q', GetoptLong::NO_ARGUMENT,
- "Do not log messages to standard output."],
- ['--require', '-r', GetoptLong::REQUIRED_ARGUMENT,
- "Require MODULE before executing rakefile."],
- ['--rakelibdir', '-R', GetoptLong::REQUIRED_ARGUMENT,
- "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')"],
- ['--silent', '-s', GetoptLong::NO_ARGUMENT,
- "Like --quiet, but also suppresses the 'in directory' announcement."],
- ['--tasks', '-T', GetoptLong::OPTIONAL_ARGUMENT,
- "Display the tasks (matching optional PATTERN) with descriptions, then exit."],
- ['--trace', '-t', GetoptLong::NO_ARGUMENT,
- "Turn on invoke/execute tracing, enable full backtrace."],
- ['--verbose', '-v', GetoptLong::NO_ARGUMENT,
- "Log message to standard output (default)."],
- ['--version', '-V', GetoptLong::NO_ARGUMENT,
- "Display the program version."],
- ]
-
# Initialize a Rake::Application object.
def initialize
super
@@ -1861,8 +1959,10 @@ module Rake
@default_loader = Rake::DefaultLoader.new
@original_dir = Dir.pwd
@top_level_tasks = []
+ add_loader('rb', DefaultLoader.new)
add_loader('rf', DefaultLoader.new)
add_loader('rake', DefaultLoader.new)
+ @tty_output = STDOUT.tty?
end
# Run the Rake application. The run method performs the following three steps:
@@ -1886,8 +1986,7 @@ module Rake
def init(app_name='rake')
standard_exception_handling do
@name = app_name
- handle_options
- collect_tasks
+ collect_tasks handle_options
end
end
@@ -1942,16 +2041,16 @@ module Rake
[name, args]
end
- # Provide standard exception handling for the given block.
+ # Provide standard execption handling for the given block.
def standard_exception_handling
begin
yield
rescue SystemExit => ex
# Exit silently with current status
- exit(ex.status)
- rescue SystemExit, GetoptLong::InvalidOption => ex
+ raise
+ rescue OptionParser::InvalidOption => ex
# Exit silently
- exit(1)
+ exit(false)
rescue Exception => ex
# Exit with error message
$stderr.puts "rake aborted!"
@@ -1962,7 +2061,7 @@ module Rake
$stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
$stderr.puts "(See full trace by running task with --trace)"
end
- exit(1)
+ exit(false)
end
end
@@ -1971,33 +2070,26 @@ module Rake
def have_rakefile
@rakefiles.each do |fn|
if File.exist?(fn) || fn == ''
- @rakefile = fn
- return true
+ return fn
end
end
- return false
+ return nil
end
- # Display the rake command line help.
- def help
- puts "rake [-f rakefile] {options} targets..."
- puts
- puts "Options are ..."
- puts
- OPTIONS.sort.each do |long, *short, mode, desc|
- case mode
- when GetoptLong::REQUIRED_ARGUMENT
- if desc =~ /\b([A-Z]{2,})\b/
- long = long + "=#{$1}"
- end
- when GetoptLong::OPTIONAL_ARGUMENT
- if desc =~ /\b([A-Z]{2,})\b/
- long = long + "[=#{$1}]"
- end
- end
- printf " %-20s (%s)\n", long, short.join(", ")
- printf " %s\n", desc
- end
+ # True if we are outputting to TTY, false otherwise
+ def tty_output?
+ @tty_output
+ end
+
+ # Override the detected TTY output state (mostly for testing)
+ def tty_output=( tty_output_state )
+ @tty_output = tty_output_state
+ end
+
+ # We will truncate output if we are outputting to a TTY or if we've been
+ # given an explicit column width to honor
+ def truncate_output?
+ tty_output? || ENV['RAKE_COLUMNS']
end
# Display the tasks and dependencies.
@@ -2015,19 +2107,51 @@ module Rake
end
else
width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
- max_column = 80 - name.size - width - 7
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
displayable_tasks.each do |t|
printf "#{name} %-#{width}s # %s\n",
- t.name_with_args, truncate(t.comment, max_column)
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
end
end
end
+ def terminal_width
+ if ENV['RAKE_COLUMNS']
+ result = ENV['RAKE_COLUMNS'].to_i
+ else
+ result = unix? ? dynamic_width : 80
+ end
+ (result < 10) ? 80 : result
+ rescue
+ 80
+ end
+
+ # Calculate the dynamic width of the
+ def dynamic_width
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
+ end
+
+ def dynamic_width_stty
+ %x{stty size 2>/dev/null}.split[1].to_i
+ end
+
+ def dynamic_width_tput
+ %x{tput cols 2>/dev/null}.to_i
+ end
+
+ def unix?
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
+ end
+
+ def windows?
+ Config::CONFIG['host_os'] =~ /mswin/
+ end
+
def truncate(string, width)
if string.length <= width
string
else
- string[0, width-3] + "..."
+ ( string[0, width-3] || "" ) + "..."
end
end
@@ -2039,77 +2163,141 @@ module Rake
end
end
- # Return a list of the command line options supported by the
- # program.
- def command_line_options
- OPTIONS.collect { |lst| lst[0..-2] }
- end
-
- # Do the option defined by +opt+ and +value+.
- def do_option(opt, value)
- case opt
- when '--describe'
- options.show_tasks = true
- options.show_task_pattern = Regexp.new(value || '.')
- options.full_description = true
- when '--dry-run'
- verbose(true)
- nowrite(true)
- options.dryrun = true
- options.trace = true
- when '--help'
- help
- exit
- when '--libdir'
- $:.push(value)
- when '--nosearch'
- options.nosearch = true
- when '--prereqs'
- options.show_prereqs = true
- when '--quiet'
- verbose(false)
- when '--rakefile'
- @rakefiles.clear
- @rakefiles << value
- when '--rakelibdir'
- options.rakelib = value.split(':')
- when '--require'
- begin
- require value
- rescue LoadError => ex
- begin
- rake_require value
- rescue LoadError => ex2
- raise ex
- end
- end
- when '--silent'
- verbose(false)
- options.silent = true
- when '--tasks'
- options.show_tasks = true
- options.show_task_pattern = Regexp.new(value || '.')
- options.full_description = false
- when '--trace'
- options.trace = true
- verbose(true)
- when '--verbose'
- verbose(true)
- when '--version'
- puts "rake, version #{RAKEVERSION}"
- exit
- when '--classic-namespace'
- require 'rake/classic_namespace'
- options.classic_namespace = true
- end
+ # A list of all the standard options used in rake, suitable for
+ # passing to OptionParser.
+ def standard_rake_options
+ [
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
+ lambda { |value|
+ require 'rake/classic_namespace'
+ options.classic_namespace = true
+ }
+ ],
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
+ lambda { |value|
+ options.show_tasks = true
+ options.full_description = true
+ options.show_task_pattern = Regexp.new(value || '')
+ }
+ ],
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
+ lambda { |value|
+ verbose(true)
+ nowrite(true)
+ options.dryrun = true
+ options.trace = true
+ }
+ ],
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
+ lambda { |value|
+ eval(value)
+ exit
+ }
+ ],
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
+ lambda { |value|
+ puts eval(value)
+ exit
+ }
+ ],
+ ['--execute-continue', '-E CODE',
+ "Execute some Ruby code, then continue with normal task processing.",
+ lambda { |value| eval(value) }
+ ],
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
+ lambda { |value| $:.push(value) }
+ ],
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
+ lambda { |value| options.show_prereqs = true }
+ ],
+ ['--quiet', '-q', "Do not log messages to standard output.",
+ lambda { |value| verbose(false) }
+ ],
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
+ lambda { |value|
+ value ||= ''
+ @rakefiles.clear
+ @rakefiles << value
+ }
+ ],
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
+ lambda { |value| options.rakelib = value.split(':') }
+ ],
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
+ lambda { |value|
+ begin
+ require value
+ rescue LoadError => ex
+ begin
+ rake_require value
+ rescue LoadError => ex2
+ raise ex
+ end
+ end
+ }
+ ],
+ ['--rules', "Trace the rules resolution.",
+ lambda { |value| options.trace_rules = true }
+ ],
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
+ lambda { |value| options.nosearch = true }
+ ],
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
+ lambda { |value|
+ verbose(false)
+ options.silent = true
+ }
+ ],
+ ['--system', '-g',
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
+ lambda { |value| options.load_system = true }
+ ],
+ ['--no-system', '--nosystem', '-G',
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
+ lambda { |value| options.ignore_system = true }
+ ],
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
+ lambda { |value|
+ options.show_tasks = true
+ options.show_task_pattern = Regexp.new(value || '')
+ options.full_description = false
+ }
+ ],
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
+ lambda { |value|
+ options.trace = true
+ verbose(true)
+ }
+ ],
+ ['--verbose', '-v', "Log message to standard output (default).",
+ lambda { |value| verbose(true) }
+ ],
+ ['--version', '-V', "Display the program version.",
+ lambda { |value|
+ puts "rake, version #{RAKEVERSION}"
+ exit
+ }
+ ]
+ ]
end
# Read and handle the command line options.
def handle_options
options.rakelib = ['rakelib']
- opts = GetoptLong.new(*command_line_options)
- opts.each { |opt, value| do_option(opt, value) }
+ opts = OptionParser.new
+ opts.banner = "rake [-f rakefile] {options} targets..."
+ opts.separator ""
+ opts.separator "Options are ..."
+
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
+ puts opts
+ exit
+ end
+
+ standard_rake_options.each { |args| opts.on(*args) }
+ parsed_argv = opts.parse(ARGV)
# If class namespaces are requested, set the global options
# according to the values in the options structure.
@@ -2120,12 +2308,11 @@ module Rake
$dryrun = options.dryrun
$silent = options.silent
end
- rescue NoMethodError => ex
- raise GetoptLong::InvalidOption, "While parsing options, error = #{ex.class}:#{ex.message}"
+ parsed_argv
end
# Similar to the regular Ruby +require+ command, but will check
- # for .rake files in addition to .rb files.
+ # for *.rake files in addition to *.rb files.
def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
return false if loaded.include?(file_name)
paths.each do |path|
@@ -2140,34 +2327,95 @@ module Rake
fail LoadError, "Can't find #{file_name}"
end
- def raw_load_rakefile # :nodoc:
+ def find_rakefile_location
here = Dir.pwd
- while ! have_rakefile
+ while ! (fn = have_rakefile)
Dir.chdir("..")
if Dir.pwd == here || options.nosearch
- fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})"
+ return nil
end
here = Dir.pwd
end
- puts "(in #{Dir.pwd})" unless options.silent
- $rakefile = @rakefile
- load File.expand_path(@rakefile) if @rakefile != ''
- options.rakelib.each do |rlib|
- Dir["#{rlib}/*.rake"].each do |name| add_import name end
+ [fn, here]
+ ensure
+ Dir.chdir(Rake.original_dir)
+ end
+
+ def raw_load_rakefile # :nodoc:
+ rakefile, location = find_rakefile_location
+ if (! options.ignore_system) &&
+ (options.load_system || rakefile.nil?) &&
+ directory?(system_dir)
+ puts "(in #{Dir.pwd})" unless options.silent
+ glob("#{system_dir}/*.rake") do |name|
+ add_import name
+ end
+ else
+ fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
+ rakefile.nil?
+ @rakefile = rakefile
+ Dir.chdir(location)
+ puts "(in #{Dir.pwd})" unless options.silent
+ $rakefile = @rakefile if options.classic_namespace
+ load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
+ options.rakelib.each do |rlib|
+ glob("#{rlib}/*.rake") do |name|
+ add_import name
+ end
+ end
end
load_imports
end
+ def glob(path, &block)
+ Dir[path.gsub("\\", '/')].each(&block)
+ end
+ private :glob
+
+ # The directory path containing the system wide rakefiles.
+ def system_dir
+ if ENV['RAKE_SYSTEM']
+ ENV['RAKE_SYSTEM']
+ elsif windows?
+ win32_system_dir
+ else
+ standard_system_dir
+ end
+ end
+
+ # The standard directory containing system wide rake files.
+ def standard_system_dir #:nodoc:
+ File.join(File.expand_path('~'), '.rake')
+ end
+ private :standard_system_dir
+
+ # The standard directory containing system wide rake files on Win
+ # 32 systems.
+ def win32_system_dir #:nodoc:
+ win32home = File.join(ENV['APPDATA'], 'Rake')
+ unless directory?(win32home)
+ raise Win32HomeError, "Unable to determine home path environment variable."
+ else
+ win32home
+ end
+ end
+ private :win32_system_dir
+
+ def directory?(path)
+ File.directory?(path)
+ end
+ private :directory?
+
# Collect the list of tasks on the command line. If no tasks are
# given, return a list containing only the default task.
# Environmental assignments are processed at this time as well.
- def collect_tasks
+ def collect_tasks(argv)
@top_level_tasks = []
- ARGV.each do |arg|
+ argv.each do |arg|
if arg =~ /^(\w+)=(.*)$/
ENV[$1] = $2
else
- @top_level_tasks << arg
+ @top_level_tasks << arg unless arg =~ /^-/
end
end
@top_level_tasks.push("default") if @top_level_tasks.size == 0
diff --git a/lib/rake/loaders/makefile.rb b/lib/rake/loaders/makefile.rb
index f66eb3b35f..9ade098a1b 100644
--- a/lib/rake/loaders/makefile.rb
+++ b/lib/rake/loaders/makefile.rb
@@ -7,31 +7,26 @@ module Rake
# Load the makefile dependencies in +fn+.
def load(fn)
- buffer = ''
open(fn) do |mf|
- mf.each do |line|
- next if line =~ /^\s*#/
- buffer << line
- if buffer =~ /\\$/
- buffer.sub!(/\\\n/, ' ')
- state = :append
- else
- process_line(buffer)
- buffer = ''
- end
+ lines = mf.read
+ lines.gsub!(/#[^\n]*\n/m, "")
+ lines.gsub!(/\\\n/, ' ')
+ lines.split("\n").each do |line|
+ process_line(line)
end
end
- process_line(buffer) if buffer != ''
end
private
# Process one logical line of makefile data.
def process_line(line)
- file_task, args = line.split(':')
+ file_tasks, args = line.split(':')
return if args.nil?
dependents = args.split
- file file_task => dependents
+ file_tasks.strip.split.each do |file_task|
+ file file_task => dependents
+ end
end
end
diff --git a/lib/rake/packagetask.rb b/lib/rake/packagetask.rb
index 71b66a6481..4b0775d09c 100644
--- a/lib/rake/packagetask.rb
+++ b/lib/rake/packagetask.rb
@@ -122,6 +122,7 @@ module Rake
task :package => ["#{package_dir}/#{file}"]
file "#{package_dir}/#{file}" => [package_dir_path] + package_files do
chdir(package_dir) do
+ sh %{env}
sh %{#{@tar_command} #{flag}cvf #{file} #{package_name}}
end
end
diff --git a/lib/rake/rdoctask.rb b/lib/rake/rdoctask.rb
index 54adc6feb5..6cfbda1d6a 100644
--- a/lib/rake/rdoctask.rb
+++ b/lib/rake/rdoctask.rb
@@ -55,7 +55,7 @@ module Rake
# RDoc. (default is none)
attr_accessor :main
- # Name of template to be used by rdoc. (default is 'html')
+ # Name of template to be used by rdoc. (defaults to rdoc's default)
attr_accessor :template
# List of files to be included in the rdoc generation. (default is [])
@@ -74,7 +74,7 @@ module Rake
@rdoc_dir = 'html'
@main = nil
@title = nil
- @template = 'html'
+ @template = nil
@external = false
@options = []
yield self if block_given?
@@ -91,18 +91,18 @@ module Rake
task name
desc "Force a rebuild of the RDOC files"
- task paste("re", name) => [paste("clobber_", name), name]
+ task "re#{name}" => ["clobber_#{name}", name]
desc "Remove rdoc products"
- task paste("clobber_", name) do
+ task "clobber_#{name}" do
rm_r rdoc_dir rescue nil
end
-
- task :clobber => [paste("clobber_", name)]
+
+ task :clobber => ["clobber_#{name}"]
directory @rdoc_dir
task name => [rdoc_target]
- file rdoc_target => @rdoc_files + [$rakefile] do
+ file rdoc_target => @rdoc_files + [Rake.application.rakefile] do
rm_r @rdoc_dir rescue nil
args = option_list + @rdoc_files
if @external
diff --git a/lib/rake/tasklib.rb b/lib/rake/tasklib.rb
index 465a58a0c7..c7fd98133c 100644
--- a/lib/rake/tasklib.rb
+++ b/lib/rake/tasklib.rb
@@ -6,11 +6,16 @@ module Rake
# Base class for Task Libraries.
class TaskLib
-
include Cloneable
- # Make a symbol by pasting two strings together.
- def paste(a,b)
+ # Make a symbol by pasting two strings together.
+ #
+ # NOTE: DEPRECATED! This method is kinda stupid. I don't know why
+ # I didn't just use string interpolation. But now other task
+ # libraries depend on this so I can't remove it without breaking
+ # other people's code. So for now it stays for backwards
+ # compatibility. BUT DON'T USE IT.
+ def paste(a,b) # :nodoc:
(a.to_s + b.to_s).intern
end
end
diff --git a/lib/rake/testtask.rb b/lib/rake/testtask.rb
index f5b77e5957..79154e422b 100644
--- a/lib/rake/testtask.rb
+++ b/lib/rake/testtask.rb
@@ -136,7 +136,12 @@ module Rake
end
def fix # :nodoc:
- ''
+ case RUBY_VERSION
+ when '1.8.2'
+ find_file 'rake/ruby182_test_unit_fix'
+ else
+ nil
+ end || ''
end
def rake_loader # :nodoc:
diff --git a/lib/resolv.rb b/lib/resolv.rb
index d1494b46c9..fc3c78215b 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -13,24 +13,24 @@ end
# interpreter.
#
# See also resolv-replace.rb to replace the libc resolver with # Resolv.
-#
+#
# Resolv can look up various DNS resources using the DNS module directly.
-#
+#
# Examples:
-#
+#
# p Resolv.getaddress "www.ruby-lang.org"
# p Resolv.getname "210.251.121.214"
-#
+#
# Resolv::DNS.open do |dns|
# ress = dns.getresources "www.ruby-lang.org", Resolv::DNS::Resource::IN::A
# p ress.map { |r| r.address }
# ress = dns.getresources "ruby-lang.org", Resolv::DNS::Resource::IN::MX
# p ress.map { |r| [r.exchange.to_s, r.preference] }
# end
-#
-#
+#
+#
# == Bugs
-#
+#
# * NIS is not supported.
# * /etc/nsswitch.conf is not supported.
@@ -38,14 +38,14 @@ class Resolv
##
# Looks up the first IP address for +name+.
-
+
def self.getaddress(name)
DefaultResolver.getaddress(name)
end
##
# Looks up all IP address for +name+.
-
+
def self.getaddresses(name)
DefaultResolver.getaddresses(name)
end
@@ -87,7 +87,7 @@ class Resolv
##
# Looks up the first IP address for +name+.
-
+
def getaddress(name)
each_address(name) {|address| return address}
raise ResolvError.new("no address for #{name}")
@@ -95,7 +95,7 @@ class Resolv
##
# Looks up all IP address for +name+.
-
+
def getaddresses(name)
ret = []
each_address(name) {|address| ret << address}
@@ -309,7 +309,7 @@ class Resolv
# Creates a new DNS resolver.
#
# +config_info+ can be:
- #
+ #
# nil:: Uses /etc/resolv.conf.
# String:: Path to a file using /etc/resolv.conf's format.
# Hash:: Must contain :nameserver, :search and :ndots keys.
@@ -457,7 +457,7 @@ class Resolv
##
# Looks up all +typeclass+ DNS resources for +name+. See #getresource for
# argument details.
-
+
def getresources(name, typeclass)
ret = []
each_resource(name, typeclass) {|resource| ret << resource}
@@ -467,7 +467,7 @@ class Resolv
##
# Iterates over all +typeclass+ DNS resources for +name+. See
# #getresource for argument details.
-
+
def each_resource(name, typeclass, &proc)
lazy_initialize
requester = make_requester
@@ -569,7 +569,7 @@ class Resolv
h = (RequestID[[host, port]] ||= {})
begin
id = rangerand(0x0000..0xffff)
- end while h[id]
+ end while h[id]
h[id] = true
}
id
@@ -1037,7 +1037,7 @@ class Resolv
# A representation of a DNS name.
class Name
-
+
##
# Creates a new DNS name from +arg+. +arg+ can be:
#
@@ -1460,11 +1460,11 @@ class Resolv
class Query
def encode_rdata(msg) # :nodoc:
- raise EncodeError.new("#{self.class} is query.")
+ raise EncodeError.new("#{self.class} is query.")
end
def self.decode_rdata(msg) # :nodoc:
- raise DecodeError.new("#{self.class} is query.")
+ raise DecodeError.new("#{self.class} is query.")
end
end
@@ -1939,7 +1939,7 @@ class Resolv
def initialize(address)
@address = IPv6.create(address)
end
-
+
##
# The Resolv::IPv6 address for this AAAA.
@@ -1956,7 +1956,7 @@ class Resolv
##
# SRV resource record defined in RFC 2782
- #
+ #
# These records identify the hostname and port that a service is
# available at.
diff --git a/lib/time.rb b/lib/time.rb
index 96f3945320..85c715b80b 100644
--- a/lib/time.rb
+++ b/lib/time.rb
@@ -1,37 +1,37 @@
#
# == Introduction
-#
+#
# This library extends the Time class:
# * conversion between date string and time object.
# * date-time defined by RFC 2822
# * HTTP-date defined by RFC 2616
# * dateTime defined by XML Schema Part 2: Datatypes (ISO 8601)
# * various formats handled by Date._parse (string to time only)
-#
+#
# == Design Issues
-#
+#
# === Specialized interface
-#
+#
# This library provides methods dedicated to special purposes:
# * RFC 2822, RFC 2616 and XML Schema.
# * They makes usual life easier.
-#
+#
# === Doesn't depend on strftime
-#
+#
# This library doesn't use +strftime+. Especially #rfc2822 doesn't depend
# on +strftime+ because:
-#
+#
# * %a and %b are locale sensitive
-#
+#
# Since they are locale sensitive, they may be replaced to
# invalid weekday/month name in some locales.
# Since ruby-1.6 doesn't invoke setlocale by default,
# the problem doesn't arise until some external library invokes setlocale.
# Ruby/GTK is the example of such library.
-#
+#
# * %z is not portable
-#
+#
# %z is required to generate zone in date-time of RFC 2822
# but it is not portable.
#
@@ -61,9 +61,9 @@ class Time
'PST' => -8, 'PDT' => -7,
# Following definition of military zones is original one.
# See RFC 1123 and RFC 2822 for the error in RFC 822.
- 'A' => +1, 'B' => +2, 'C' => +3, 'D' => +4, 'E' => +5, 'F' => +6,
+ 'A' => +1, 'B' => +2, 'C' => +3, 'D' => +4, 'E' => +5, 'F' => +6,
'G' => +7, 'H' => +8, 'I' => +9, 'K' => +10, 'L' => +11, 'M' => +12,
- 'N' => -1, 'O' => -2, 'P' => -3, 'Q' => -4, 'R' => -5, 'S' => -6,
+ 'N' => -1, 'O' => -2, 'P' => -3, 'Q' => -4, 'R' => -5, 'S' => -6,
'T' => -7, 'U' => -8, 'V' => -9, 'W' => -10, 'X' => -11, 'Y' => -12,
}
def zone_offset(zone, year=self.now.year)
@@ -436,8 +436,8 @@ class Time
#
# Returns a string which represents the time as rfc1123-date of HTTP-date
- # defined by RFC 2616:
- #
+ # defined by RFC 2616:
+ #
# day-of-week, DD month-name CCYY hh:mm:ss GMT
#
# Note that the result is always UTC (GMT).
@@ -768,21 +768,21 @@ if __FILE__ == $0
def test_rfc2822_leap_second
t = Time.utc(1998,12,31,23,59,59)
assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:59 UTC"))
- assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 -0000"));t.localtime
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:59 +0900"))
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:59 +0100"))
assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 +0000"))
- assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:59 -0100"));t.utc
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:59 -0100"));t.utc
t += 1
assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:60 UTC"))
- assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 -0000"));t.localtime
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:60 +0900"))
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:60 +0100"))
assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 +0000"))
- assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:60 -0100"));t.utc
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:60 -0100"));t.utc
t += 1 if t.sec == 60
assert_equal(t, Time.rfc2822("Thu, 1 Jan 1999 00:00:00 UTC"))
- assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 -0000"));t.localtime
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 09:00:00 +0900"))
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 01:00:00 +0100"))
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 +0000"))
diff --git a/lib/tsort.rb b/lib/tsort.rb
index a014e7f6c2..9fc4feadcd 100644
--- a/lib/tsort.rb
+++ b/lib/tsort.rb
@@ -32,7 +32,7 @@
# array using the user-supplied block.
#
# require 'tsort'
-#
+#
# class Hash
# include TSort
# alias tsort_each_node each_key
@@ -40,10 +40,10 @@
# fetch(node).each(&block)
# end
# end
-#
+#
# {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
# #=> [3, 2, 1, 4]
-#
+#
# {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
# #=> [[4], [2, 3], [1]]
#
@@ -52,19 +52,19 @@
# A very simple `make' like tool can be implemented as follows:
#
# require 'tsort'
-#
+#
# class Make
# def initialize
# @dep = {}
# @dep.default = []
# end
-#
+#
# def rule(outputs, inputs=[], &block)
# triple = [outputs, inputs, block]
# outputs.each {|f| @dep[f] = [triple]}
# @dep[triple] = inputs
# end
-#
+#
# def build(target)
# each_strongly_connected_component_from(target) {|ns|
# if ns.length != 1
@@ -88,18 +88,18 @@
# end
# }
# end
-#
+#
# def tsort_each_child(node, &block)
# @dep[node].each(&block)
# end
# include TSort
# end
-#
+#
# def command(arg)
# print arg, "\n"
# system arg
# end
-#
+#
# m = Make.new
# m.rule(%w[t1]) { command 'date > t1' }
# m.rule(%w[t2]) { command 'date > t2' }
@@ -189,7 +189,7 @@ module TSort
end
#
- # Iterates over strongly connected component in the subgraph reachable from
+ # Iterates over strongly connected component in the subgraph reachable from
# _node_.
#
# Return value is unspecified.
diff --git a/lib/uri/common.rb b/lib/uri/common.rb
index 98dda2a350..9a6a06894a 100644
--- a/lib/uri/common.rb
+++ b/lib/uri/common.rb
@@ -227,7 +227,7 @@ module URI
end
def unescape(str, escaped = @regexp[:ESCAPED])
- str.gsub(escaped) { [$&.hex].pack('U') }
+ str.gsub(escaped) { [$&[1, 2].hex].pack('U') }
end
@@to_s = Kernel.instance_method(:to_s)