Ruby comes “out of the box” with a large and useful library of modules and classes. This chapter contains a sampling of the more useful of these.
Interestingly, and unlike some of the code in later chapters, all of these libraries are written in Ruby. You'll find the source in the lib
subdirectory of the standard Ruby distribution.
require "complex"
v1 = Complex(2,3) → Complex(2, 3)
v2 = 2.im → Complex(0, 2)
v1 + v2 → Complex(2, 5)
v1 * v2 → Complex(-6, 4)
v2**2 → Complex(-4, 0)
Math.sin(v1) → Complex(9.154499147, -4.16890696)
v1 < v2 → false
v2**2 == -4 → true
Complex::I |
0 +1i |
---|
In addition to the Complex.new
constructor, the Complex
library defines the method Numeric.im
, such that aNumeric.im
returns 0 + aNumerici. Complex numbers are also constructed using the global method Complex
, which takes one or two arguments. The value it returns depends on the type of its arguments:
a | b | Result |
---|---|---|
Number | Number | a + bi |
Complex | 0 | a |
Complex | Complex | Complex( a.real - b.image, a.image + b.real ) |
Number | Complex | Complex( a - b.image, b.real ) |
ref | + |
aNumeric → aComplex | Addition |
ref | - |
aNumeric → aComplex | Subtraction |
ref | * |
aNumeric → aComplex | Multiplication |
ref | / |
aNumeric → aComplex | Division |
ref | % |
aNumeric → aComplex | Remainder |
ref | ** |
aNumeric → aComplex | Exponentiation (real and complex power) |
<=>
other.abs.true
or false
true
if its real and imaginary parts match ref. If anObject is a simple number, returns true
if ref.real
equals anObject and ref.image
is zero. Otherwise, attempts to coerce anObject to a complex number and compares the result.Complex(real.to_f, image.to_f)
.Complex(real.to_i, image.to_i)
.Complex(real.to_r, image.to_r)
, converting both parts of the complex to a rational number.In addition, the Math
functions sqrt
, exp
, cos
, sin
, tan
, log
, log10
, and atan2
are extended to support a Complex
argument.
require 'date'
d = Date.new(2000, 3, 31) → #<Date: 2451635,2299161>
[d.year, d.yday, d.wday] → [2000, 91, 5]
[d.month, d.mday] → [3, 31]
[d.cwyear, d.cweek, d.cwday] → [2000, 13, 5]
[d.jd, d.mjd] → [2451635, 51634.5]
(d << 1).to_s → "2000-02-29"
d.succ.to_s → "2000-04-01"
(d + 100).to_s → "2000-07-09"
d.leap? → true
Date.new(2000, 3, -10).to_s → "2000-03-22"
d1 = Date.neww(2000, 13, 7) → #<Date: 2451637,2299161>
d1.to_s → "2000-04-02"
[d1.cwday, d1.wday] → [7, 0]
The date
library implements class Date
, which provides a comprehensive set of facilities for storing, manipulating, and converting dates. To document its options, we need to take a brief historical detour to establish some vocabulary.
Internally a date is stored as a Julian day number, the number of days since midday, January 1st, 4713 BCE. (In the code, you may find references to the year -4712. As astronomical dates include a year zero, 4713 BCE is the same year as -4712.) The rules for converting a Julian day number to a calendar date are complicated because the Romans estimated the length of a year incorrectly. In the Julian calendar (often called Old Style, or O.S.), every year divisible by 4 is a leap year. The Date
class has options to convert dates using this as an assumption.
By the sixteenth century, the inaccuracies in this measurement had become apparent. An edict from Pope Gregory XIII in 1582 created the New Style (N.S.) or Gregorian calendar, where years divisible by 100 were no longer leap years unless they were also divisible by 400. This system was adopted by most Catholic countries immediately, but religious differences held up a wider adoption. England (and several other countries) switched in 1752, with some countries following later. The Date
class allows you to determine whether to implement the cutover in 1582 (the Date::ITALY
option), 1752 (Date::ENGLAND
), or another date of your choosing.
The Date
class also provides conversions to Modified Julian Day (MJD) numbers. MJD values count from midnight, November 17, 1858. Because these values count from midnight, not midday, there is a half-day added to the conversion factor.
The descriptions that follow use the abbreviations listed in Table 24.1.
Class Date
exports the constant arrays Date::MONTHNAMES
and Date::DAYNAMES
, which can be indexed by mon and wday values to return the corresponding English names.
The Date
class also provides low-level date-conversion methods:
civil_to_jd
commercial_to_jd
ordinal_to_jd
mjd_to_jd
jd_to_mjd
jd_to_civil
jd_to_commercial
jd_to_ordinal
These methods perform limited error checking of their parameters, and are not documented here. The somewhat confusingly named exist..?
routines perform conversions from different formats into a Julian day number with error checking. These routines also automatically normalize their parameters.
Field | Meaning |
---|---|
cwday | An ISO 8601 calendar weekday. 1 is Monday, 7 is Sunday. |
cweek | An ISO 8601 calendar week. Week 1 is the week containing the first Thursday (or equivalently the week that contains January 4th). |
cwyear | An ISO 8601 calendar-week-based year. May be different from year, as it rolls forward only on a Monday. |
jd | The Julian day number—the number of days since January 1st, 4713 BCE. |
mday | The day of the month (1..31). |
mjd | A modified Julian day number. |
mon | The month of the year (1..12). |
sg | The start of the Gregorian correction: Date::ITALY (the default) for 1582, Date::ENGLAND for 1752, or JULIAN , meaning no correction. You may also provide an arbitrary Julian day number for this parameter, in which case the correction will start from this date. |
wday | The day of the week (0 is Sunday). |
week | The week number into a year (1..53). |
yday | The day into the year (1..366). |
year | A year (1966, 2001, and the like). |
Comparable
<
, <=
, ==
, >=
, >
, between?
Date::ITALY
) → jdnil
on error.Date::ITALY
) → jdnil
if the parameters are invalid.Date::ITALY
) → jdtrue
or false
true
if year is divisible by 4, otherwise returns true
if year is divisible by 400.true
or false
true
if year is divisible by 4.true
or false
Date.gregorian_leap?
.Date::ITALY
) → aNewDateDate
for the given year, mon, and mday. If mon is negative, it counts back from the end of the year. If mday is negative, it counts back from the end of the month.Date::ITALY
) → aNewDateDate
corresponding to the given Julian day number.Date::ITALY
) → aNewDateDate
for the given year and yday. If yday is negative, it counts back from the end of the year.Date::ITALY
) → aNewDateDate.new
.Date::ITALY
) → aNewDateDate
for the given cyear, cweek, and cwday. If cweek is negative, it counts back from the end of the year. If cwday is negative, it counts back from the end of the week.Date::ITALY
) → aNewDateDate
for today.Date
anInteger days from ref.-
anInteger → aNewDate-
anOtherDate → anIntegerDate
anInteger days before ref. The second form returns the number of days between ref and anOtherDate.Date
formed by subtracting anInteger months to ref, adjusting the mday value back to the last day of the month if it otherwise exceeds it.Numeric
, in which case it is treated as a Julian day number, or a Date
. Returns -1, 0, +1 if ref is less than, equal to, or greater than anOther. See module Comparable
.true
or false
Numeric
, in which case it is treated as a Julian day number, or a Date
. Returns true
if the Julian day number of anOther is the same as ref.Date
formed by adding anInteger months to ref, adjusting the mday value back to the last day of the month if it otherwise exceeds it..newsg(Date::ENGLAND)
..newsg(Date::GREGORIAN)
..newsg(Date::ITALY)
..newsg(Date::JULIAN)
.true
or false
true
if ref falls within a leap year.Date::ITALY
) → aNewDateDate
.true
or false
true
if ref falls in the period of New Style dates.true
or false
true
if ref falls in the period of Old Style dates.self
as “year-mon-mday.”require "English"
$OUTPUT_FIELD_SEPARATOR = ' -- '
"waterbuffalo" =~ /buff/
print $LOADED_FEATURES, $POSTMATCH, $PID, "\n"
print $", $', $$, "\n"
produces:
English.rb -- alo -- 32130 --
English.rb -- alo -- 32130 --
Include the English library file in a Ruby script, and you can reference the global variables such as $_
using less cryptic names, listed in the following table.
$* |
$ARGV | $" |
$LOADED_FEATURES |
$? |
$CHILD_STATUS | $& |
$MATCH |
$< |
$DEFAULT_INPUT | $. |
$NR |
$> |
$DEFAULT_OUTPUT | $, |
$OFS |
$! |
$ERROR_INFO | $\ |
$ORS |
$@ |
$ERROR_POSITION | $\ |
$OUTPUT_RECORD_SEPARATOR |
$; |
$FIELD_SEPARATOR | $, |
$OUTPUT_FIELD_SEPARATOR |
$; |
$FS | $$ |
$PID |
$= |
$IGNORECASE | $' |
$POSTMATCH |
$. |
$INPUT_LINE_NUMBER | $` |
$PREMATCH |
$/ |
$INPUT_RECORD_SEPARATOR | $$ |
$PROCESS_ID |
$~ |
$LAST_MATCH_INFO | $0 |
$PROGRAM_NAME |
$+ |
$LAST_PAREN_MATCH | $/ |
$RS |
$_ |
$LAST_READ_LINE |
require "find"
Find.find("/etc/passwd", "/var/spool/lp1", ".") do |f|
Find.prune if f == "."
puts f
end
produces:
/etc/passwd
/var/spool/lp1
/var/spool/lp1/status
/var/spool/lp1/lock
/var/spool/lp1/.seq
The Find
module supports the top-down traversal of a set of file paths.
Find.find
.require 'ftools'
File.copy 'testfile', 'testfile1' → true
File.compare 'testfile', 'testfile1' → true
The FTools
library adds several methods to the built-in File
class. These methods are particularly useful to programs that move and copy files, such as installers.
false
) → true
or false
File.compare
.false
) → true
or false
true
only if the contents of files name1 and name2 are identical.false
) → true
or false
File.syscopy
, but logs the attempt to $stderr if verbose is not false
.false
) → true
or false
File.copy
.nil
, verbose=false
)File.syscopy
, unless toName already exists and has the same content as fromName. Sets the mode of the resulting file to aMode unless aMode is nil
.true
. Creates any missing parent directories as required.File.makedirs
.false
) → true
or false
false
.false
) → true
or false
File.move
.File.safe_unlink
(the name refers to the Unix rm -f
command).nil
true
. The method attempts to make all files writable before unlinking them, so no errors will occur deleting read-only files. Returns the number of files deleted, or nil
on error.true
or false
true
on success.# Call using "ruby example.rb --size 10k -v -q a.txt b.doc"
require 'getoptlong'
# specify the options we accept and initialize
# the option parser
opts = GetoptLong.new(
[ "--size", "-s", GetoptLong::REQUIRED_ARGUMENT ],
[ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
[ "--query", "-q", GetoptLong::NO_ARGUMENT ],
[ "--check", "--valid", "-c", GetoptLong::NO_ARGUMENT ]
)
# process the parsed options
opts.each do |opt, arg|
puts "Option: #{opt}, arg #{arg.inspect}"
end
puts "Remaining args: #{ARGV.join(', ')}"
produces:
Option: --size, arg "10k"
Option: --verbose, arg ""
Option: --query, arg ""
Remaining args: a.txt, b.doc
Class GetoptLong
supports GNU-style command-line option parsing. Options may be a minus sign (`-') followed by a single character, or two minus signs (`--') followed by a name (a long option). Long options may be abbreviated to their shortest unambiguous lengths.
A single internal option may have multiple external representations. For example, the option to control verbose output could be any of -v
, --verbose
, or --details
. Some options may also take an associated value.
Each internal option is passed to GetoptLong
as an array, containing strings representing the option's external forms and a flag. The flag (NO_ARGUMENT
, REQUIRED_ARGUMENT
, or OPTIONAL_ARGUMENT
) specifies how GetoptLong
is to associate an argument with the option.
If the environment variable POSIXLY_CORRECT
is set, all options must precede nonoptions on the command line. Otherwise, the default behavior of GetoptLong
is to reorganize the command line to put the options at the front. This behavior may be changed by setting GetoptLong#ordering=
to one of the constants PERMUTE
, REQUIRE_ORDER
, or RETURN_IN_ORDER
. POSIXLY_CORRECT
may not be overridden.
Per-option constants | ||
---|---|---|
NO_ARGUMENT |
Flags an option that takes no argument. | |
OPTIONAL_ARGUMENT |
A nonoption following this option will be used as this option's argument. | |
REQUIRED_ARGUMENT |
This option must be followed by an argument. |
Overall constants | ||
---|---|---|
PERMUTE |
Options and their arguments will be shuffled to the front of the command line. | |
REQUIRE_ORDER |
Options and their arguments must appear at the start of the command line. The first nonoption terminates option processing. | |
RETURN_IN_ORDER |
Return options in the order in which they occur on the command line. |
set_options
.GetoptLong#get
, passing the returned option and argument to the associated block. The loop ends when get
returns nil
for anOption.Exception
object documenting any error that has occurred, or nil
if there has not been an error.Returns the next option, along with any associated argument. If there is no argument, nil
is returned for anArgument. If there are no remaining unprocessed options, or if there is an error in option processing and quiet
has been set, nil
is returned for anOption. Otherwise, if there is an error, a message is written to $stderr and an exception (a subclass of StandardError
) is raised.
The option string returned is the first option that was given in the corresponding array passed to set_options
.
GetoptLong#get
.PERMUTE
, REQUIRE_ORDER
, or RETURN_IN_ORDER
. Quietly ignored if the environment variable POSIXLY_CORRECT
is set. Ordering may not be changed once option processing has been started.true
or false
quiet
attribute.true
or false
quiet
attribute. If false
, any errors encountered are reported to $stderr.true
or false
GetoptLong#quiet
.NO_ARGUMENT
, OPTIONAL_ARGUMENT
, or REQUIRED_ARGUMENT
. See the sample code on at the start of the documentation for this class.GetoptLong#each
or on its own. For example, calling the following program using “ruby example.rb --size 10k -v -term -q a.txt b.doc
” will leave the -q
and filenames in ARGV.
require 'getoptlong'
opts = GetoptLong.new(
[ "--size", "-s", GetoptLong::REQUIRED_ARGUMENT ],
[ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
[ "--term", "-t", GetoptLong::NO_ARGUMENT ],
[ "--query", "-q", GetoptLong::NO_ARGUMENT ],
[ "--check", "--valid", "-c", GetoptLong::NO_ARGUMENT ]
)
opts.each do |opt, arg|
puts "Option: #{opt}, arg #{arg.inspect}"
opts.terminate if (opt == '--term')
end
puts "Remaining args: #{ARGV.join(', ')}"
produces:
Option: --size, arg "10k"
Option: --verbose, arg ""
Option: --term, arg ""
Remaining args: -q, a.txt, b.doc
true
or false
true
if option processing has been terminated.The mkmf
library is used by Ruby extension modules to help create Makefiles
. When writing an extension, you create a program named “extconf.rb
”, which may be as simple as:
require 'mkmf'
create_makefile("Test")
When run, this script will produce a Makefile
suited to the target platform. mkmf
contains several methods you can use to find libraries and include files and to set compiler flags.
For more information on creating extension modules, see Chapter 17, “Extending Ruby.”
PLATFORM |
varies | A constant string that describes the platform on which Ruby is running, such as “mswin32” or “i686-linux.” |
$CFLAGS |
Global variable for compiler flags. | |
$LDFLAGS |
Global variable for linker flags. |
Makefile
for an extension named target. If this method is not called, no Makefile
is created.Looks for directory configuration options for name given as arguments to this program or to the original build of Ruby. These arguments may be one of:
--with-name-dir=directory |
--with-name-include=directory |
--with-name-lib=directory |
The given directories will be added to the appropriate search paths (include or link) in the Makefile
.
true
or false
have_library
, but will also search in the given directory paths.true
or false
Makefile
and returns true
.true
or false
Makefile
and returns true
.true
or false
dir_config
, adds the library to the link command in the Makefile
and returns true
.The ParseDate
module defines a single method, ParseDate.parsedate
, which converts a date and/or time string into its constituents. It uses heuristics that handle a wide variety of date and time formats, including a subset of ISO 8601, Unix ctime
, and most common written variants. The following table shows some examples.
String | Guess? | yy | mm | dd | hh | min | sec | zone | wd |
---|---|---|---|---|---|---|---|---|---|
1999-09-05 23:55:21+0900 | F | 1999 | 9 | 5 | 23 | 55 | 21 | +0900 | -- |
1983-12-25 | F | 1983 | 12 | 25 | -- | -- | -- | -- | -- |
1965-11-10 T13:45 | F | 1965 | 11 | 10 | 13 | 45 | -- | -- | -- |
10/9/75 1:30pm | F | 75 | 10 | 9 | 13 | 30 | -- | -- | -- |
10/9/75 1:30pm | T | 1975 | 10 | 9 | 13 | 30 | -- | -- | -- |
Mon Feb 28 17:15:49 CST 2000 | F | 2000 | 2 | 28 | 17 | 15 | 49 | CST | 1 |
Tue, 02-Mar-99 11:20:32 GMT | F | 99 | 3 | 2 | 11 | 20 | 32 | GMT | 2 |
Tue, 02-Mar-99 11:20:32 GMT | T | 1999 | 3 | 2 | 11 | 20 | 32 | GMT | 2 |
12-January-1990, 04:00 WET | F | 1990 | 1 | 12 | 4 | 0 | -- | WET | -- |
4/3/99 | F | 99 | 4 | 3 | -- | -- | -- | -- | -- |
4/3/99 | T | 1999 | 4 | 3 | -- | -- | -- | -- | -- |
10th February, 1976 | F | 1976 | 2 | 10 | -- | -- | -- | -- | -- |
March 1st, 84 | T | 1984 | 3 | 1 | -- | -- | -- | -- | -- |
Friday | F | -- | -- | -- | -- | -- | -- | -- | 5 |
false
) → [ year, mon, mday, hour, min, sec, zone, wday ]Fixnum
objects containing the various components. nil
is returned for fields that cannot be parsed from aString. If the result contains a year that is less than 100 and guessYear is true, parsedate
will return a year value equal to year plus 2000 if year is less than 69, year plus 1900 otherwise.The profile
library prints to $stderr a summary of the number of calls to, and the time spent in, each method in a Ruby program. The output is sorted by the total time spent in each method. Profiling can be enabled from the command line using the -r
profile
option, or from within a source program by requiring the profile
module.
require 'profile'
def ackerman(m, n)
if m == 0 then n+1
elsif n == 0 and m > 0 then ackerman(m-1, 1)
else ackerman(m-1, ackerman(m, n-1))
end
end
ackerman(3,3)
produces:
time seconds seconds calls ms/call ms/call name
74.17 2.47 2.47 2432 1.02 41.95 Object#ackerman
17.42 3.05 0.58 3676 0.16 0.16 Fixnum#==
5.71 3.24 0.19 2431 0.08 0.08 Fixnum#-
2.70 3.33 0.09 1188 0.08 0.08 Fixnum#+
0.00 3.33 0.00 1 0.00 0.00 Module#method_added
0.00 3.33 0.00 57 0.00 0.00 Fixnum#>
0.00 3.33 0.00 1 0.00 3330.00 #toplevel
The PStore
class provides transactional, file-based persistent storage of Ruby objects. The following example stores two hierarchies in a PStore. The first, identified by the key “names
”, is an array of Strings. The second, identified by “tree
”, is a simple binary tree.
require "pstore"
class T
def initialize(val, left=nil, right=nil)
@val, @left, @right = val, left, right
end
def to_a
[ @val, @left.to_a, @right.to_a ]
end
end
store = PStore.new("/tmp/store")
store.transaction do
store['names'] = [ 'Douglas', 'Barenberg', 'Meyer' ]
store['tree'] = T.new('top',
T.new('A', T.new('B')),
T.new('C', T.new('D', nil, T.new('E'))))
end
# now read it back in
store.transaction do
puts "Roots: #{store.roots.join(', ')}"
puts store['names'].join(', ')
puts store['tree'].to_a.inspect
end
produces:
Roots: names, tree
Douglas, Barenberg, Meyer
["top", ["A", ["B", [], []], []], ["C", ["D", [], ["E", [], []]], []]]
Each PStore
can store several object hierarchies. Each hierarchy has a root, identified by a key (often a string). At the start of a PStore
transaction, these hierarchies are read from a disk file and made available to the Ruby program. At the end of the transaction, the hierarchies are written back to the file. Any changes made to objects in these hierarchies are therefore saved on disk, to be read at the start of the next transaction that uses that file.
In normal use, a PStore
object is created and then is used one or more times to control a transaction. Within the body of the transaction, any object hierarchies that had previously been saved are made available, and any changes to object hierarchies, and any new hierarchies, are written back to the file at the end.
PStore
object associated with the given
file. If the file exists, its contents must have been previously
written by PStore
.true
or false
true
if anObject is the key of a root in
this store.PStore#abort
, or if it raises an exception, no data is saved back to the associated file. Otherwise, if it invokes PStore#commit
, or if it terminates normally, the object hierarchies are written back to the file. The value returned is the value returned by the block.require "tempfile"
tf = Tempfile.new("afile")
tf.path → "/tmp/afile32146.0"
tf.puts("Cosi Fan Tutte") → nil
tf.close → nil
tf.open → #<File:0x40196fc8>
tf.gets → "Cosi Fan Tutte\n"
tf.close(true) → #<File:0x40196fc8>
Class Tempfile
creates managed temporary files. Although they behave the same as any other IO
objects, temporary files are automatically deleted when the Ruby program terminates. Once a Tempfile
object has been created, the underlying file may be opened and closed a number of times in succession.
Tempfile
does not directly inherit from IO
. Instead, it delegates calls to a File
object. From the programmer's perspective, apart from the unusual new
, open,
and close
semantics, a Tempfile
object behaves as if it were an IO
object.
Constructs a temporary file in the given directory. The file name is built by concatenating basename, the current process id and (as an extension) a unique sequence number. If the tmpdir parameter is not supplied, it defaults to the value of one of the environment variables TMPDIR
, TMP
, or TEMP
, or to the directory /tmp
.
The file is then opened using mode “w+”, which allows reading and writing and deletes any existing content (see Table 22.5).
Tempfile.new
.false
)true
, deletes the underlying real file. If final is false
, ref may be subsequently reopened. In all cases, the underlying file is deleted when the program terminates.require 'thread'
sema4 = Mutex.new
a = Thread.new {
sema4.synchronize {
# access shared resource
}
}
b = Thread.new {
sema4.synchronize {
# access shared resource
}
}
Mutex
implements a simple semaphore that can be used to coordinate access to shared data from multiple concurrent threads.
true
or false
true
if this lock is currently held by some thread.Mutex#lock
), runs the block, and releases the lock when the block completes.true
or false
true
if the lock was granted.nil
nil
if ref wasn't locked.require 'thread'
mutex = Mutex.new
resource = ConditionVariable.new
a = Thread.new {
mutex.synchronize {
# Thread 'a' now needs the resource
resource.wait(mutex)
# 'a' can now have the resource
}
}
b = Thread.new {
mutex.synchronize {
# Thread 'b' has finished using the resource
resource.signal
}
}
ConditionVariable
objects augment class Mutex
. Using condition variables, it is possible to suspend while in the middle of a critical section until a resource becomes available (see the discussion “Condition Variables”).
require "timeout"
for snooze in 1..2
puts "About to sleep for #{snooze}"
timeout(1.5) do
sleep(snooze)
end
puts "That was refreshing"
end
produces:
About to sleep for 1
That was refreshing
About to sleep for 2
/tc/usr/lib/ruby/1.6/timeout.rb:37: execution expired (TimeoutError)
from prog.rb:5:in `timeout'
from prog.rb:5
from prog.rb:3:in `each'
from prog.rb:3
The timeout
method takes a single parameter, representing a timeout period in seconds, and a block. The block is executed, and a timer is run concurrently. If the block terminates before the timeout, timeout
returns true
. Otherwise, a TimeoutError
exception is raised.
require "weakref"
ref = "fol de rol"
puts "Initial object is #{ref}"
ref = WeakRef.new(ref)
puts "Weak reference is #{ref}"
ObjectSpace.garbage_collect
puts "But then it is #{ref}"
produces:
Initial object is fol de rol
Weak reference is fol de rol
prog.rb:8: Illegal Reference - probably recycled (WeakRef::RefError)
In Ruby, objects are not eligible for garbage collection if there are still references to them. Normally, this is a Good Thing—it would be disconcerting to have an object simply evaporate while you were using it. However, sometimes you may need more flexibility. For example, you might want to implement an in-memory cache of commonly used file contents. As you read more files, the cache grows. At some point, you may run low on memory. The garbage collector will be invoked, but the objects in the cache are all referenced by the cache data structures, and so will not be deleted.
A weak reference behaves exactly as any normal object reference with one important exception—the referenced object may be garbage collected, even while references to it exist. In the cache example, if the cached files were accessed using weak references, once memory runs low they will be garbage collected, freeing memory for the rest of the application.
Weak references introduce a slight complexity. As the object referenced can be deleted by garbage collection at any time, code that accesses these objects must take care to ensure that the references are valid. Two techniques can be used. First, the code can reference the objects normally. Any attempt to reference an object that has been garbage collected will raise a WeakRef::RefError
exception.
An alternative approach is to use the WeakRef#weakref_alive?
method to check that a reference is valid before using it. Garbage collection must be disabled during the test and subsequent reference to the object. In a single-threaded program, you could use something like:
ref = WeakRef.new(someObject)
#
# .. some time later
#
gcWasDisabled = GC.disable
if ref.weakref_alive?
# do stuff with 'ref'
end
GC.enable unless gcWasDisabled
true
or false
false
if the object referenced by ref has been garbage collected.Extracted from the book "Programming Ruby - The Pragmatic Programmer's Guide"
Copyright © 2001 by Addison Wesley Longman, Inc. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/).
Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder.
Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.