Index by title

abbrev

[REMOVED]

This library has been removed from the Sapphire standard library.

Reason for removal

The abbrev library is rarely used and frankly, not very useful.


AOP

Aspect Oriented Programming. You can read about AOP at http://en.wikipedia.org/wiki/Aspect_oriented_programming.

Sapphire and AOP

I plan on supporting a limited form of AOP in Sapphire. I will not, however, adopt some of the more byzantine syntax that languages like Java have. Instead, I will support AOP in two ways. First, by redefining the semantics of super so that it looks at its containing singleton class first, then up the inheritance hierarchy. Second, by providing Object#pre, Object#post and Object#wrap methods as shortcuts.

Redefining super

Currently in Ruby the super method only looks up the inheritance hierarchy. In Sapphire, it will check its containing singleton first. For example:
class String
   def size
      puts "42" # pre
      super
   end
end

"hello".size # Prints "42", then returns the size, 5.

Object#pre, Object#post, Object#wrap

In Sapphire the Object class will define special methods for handling AOP in a more uniform fashion. For example, the following code would be functionally identical to the above example:
class String
   def pre(:size)
      puts "42" 
   end
end

"hello".size # Prints "42", then returns the size, 5
The Object#post method is similar to Object#pre, except that it fires after the method in question. For example:
class String
   def post(:size)
      puts "Howdy" 
   end
end

"hello".size # 5, followed by "howdy" 
The Object#wrap is a combination of Object#pre and Object#post in that it fires both before and after the method. For example:
class String
   def wrap(:size)
      puts "It's a wrap!" 
   end
end

"hello".size # "It's a wrap", followed by 5, followed again by "It's a wrap" 

Global AOP

To apply Object#pre to all methods of a given class, simply don't provide any arguments. For example:
class String
   def pre
      puts "All for one!" 
   end
end

"hello".size          # prints "All for one!", then returns 5
"hello".include?("e") # prints "All for one!", then returns true

And so on. This would probably be unwise in practice.

Array changes

Array#==

Now honors custom to_ary methods for RHS objects.

Array#[]

Now raises an ArgumentError if you specify a negative length for the second form.

Array#collect

Redundant implementation removed. It now uses the mixed-in Enumerable#collect method.

Array#fetch

Removed, useless.

Note that removing this method required an update to optparse.rb.

Array#fill

Array#indexes

Removed, deprecated.

Array#indices

Removed, deprecated.

Array#insert

Providing an index with no other arguments now raises an ArgumentError.

Array#push

Now requires at least one argument or an ArgumentError is raised.

Array#slice

Now a bonafide alias of Array#[] instead of a synonym.

Array#to_s

Uses the same code as Array#inspect. They are now aliases.

Array#transpose

Removed. This is a rarely used method that was added primarily for vectors and matrices. The Matrix and Vector classes implement it independently, so I decided to remove it.

Array#zip

Redundant implementation removed. It now uses the mixed-in Enumerable#zip method.


Asynchronous Methods

Taking a cue from functional languages like Erlang and Fortress, Sapphire will add implement certain list handling methods in an asynchronous fashion.

Use Case

There are instances where a programmer wants to perform some operation on every element, but does not care about the order. Currently Ruby (among other languages) processes the list sequentially. This is fine for small lists, but becomes problematic when dealing with large lists, where performance can start to become a problem.

Map and Select

The two most common use cases for asynchronous methods are Enumerable#map and Enumerable#select. Programmers typically do not care about ordering for these methods, and are thus prime candidates for asynchronization.

Separate Methods

In order to distinguish asynchronous from synchronous methods, the "a_" method name prefix will be used. Thus, Enumerable#a_map and Enumerable#a_select are the asynchronous versions of Enumerable#map and Enumerable#select, respectively.

Who won't benefit?

For lists with relatively small numbers of elements, asynchronous methods will be of little or no benefit. In fact, they may actually show worse performance. Use appropriately.


C99

Advantages of C99

See Coding Standards for Sapphire coding guidelines.

Miscellaneous Notes

The stdint.h for Windows can be found at http://msinttypes.googlecode.com/svn/trunk/stdint.h


CHANGES

Core Class Changes

Standard Library Changes

Additions

Refactored

Removals

Internals / Misc


Coding Standards

h2. Coding Standard Examples
// Single line if
if(NIL_P(v_size))
   x = y + z;

// Multi-line if
if(NIL_P(v_size)){
   x = y + z;
   a = b + c;
}

// Single line if and single line else
if(NIL_P(v_size))
   x = y + z;
else
   a = b + c;

// Single line if followed by multi-line else
if(NIL_P(v_size)){
   x = y + z;
}
else{
   x = y / z;
   a = b / c;
}


common.mk

I have added several test targets. Instead of just 'test' or 'test-all', you now have 'test-array', 'test-string', etc.

This makes development and testing much easier because it means I can limit my tests to just the piece I want instead of running the entire test suite.

Make test tasks


dbm

[REMOVED]

The dbm library has been removed from the Sapphire standard library.

Reason for removal

It's a rarely used simple data store library designed originally for Unix. Windows performance is poor and there are better alternatives for personal data stores.

Replacement

KirbyBase


digest-bubblebabble

The digest-bubblebabble library has been replaced with a pure Sapphire version.

Changes

History

I originally ported Benjamin Trott's Digest-BubbleBabble Perl module in August, 2004. It was released as part of the "Shards" project on RubyForge. You can find Ben Trott's module here:

http://search.cpan.org/~btrott/Digest-BubbleBabble-0.01/BubbleBabble.pm

In October 2006 Akinori Musha, apparently unaware that I had a pure Ruby version online, added his own version into the standard library (in one of the point releases) without any announcement.

Motivation

A pure Sapphire library version is easier to maintain. A C extension is overkill for such a simple library.


Dir

Class Methods

Dir.new

Now behaves like Dir.open in that it takes a block and automatically closes the handle.

Dir.open

This is now an alias for Dir.new, which itself adopted the original Dir.open semantics.

Dir.tmpdir

This is now a core method. The tmpdir library has been removed.

This method returns the path found in your TMP, TEMP, USERPROFILE environment variable, in that order. If it cannot be found, it resorts to '/tmp' (Unix) or you Windows directory (Windows), e.g. C:\WINNT\Temp.

Unlike Ruby, it does not check 'TMPDIR' (no one sets this), does not alter its behavior based on $SAFE (I'm going to scrap $SAFE), does not default to the current directory (bad idea), and does not verify whether or not the directory exists, that it really is a directory, or if the directory is writable. That's your job.

Instance Methods

Dir#closed?

This is a new method that simply tells you if the directory stream is closed or not. Analogous to IO#closed? and handy for testing to boot.

Dir#inspect

There is a custom implementation in dir.c, but Ruby isn't using it. Sapphire does.


Enumerable

Enumerable#map

[TODO]

The method signature is now Enumerable#map(method, *args). If present, the method will be applied to each argument in the list. If args are present, they will be passed as arguments to method.

Enumerable#zip

Each argument uses to_ary instead of to_a for duck typing purposes. This allowed me to remove a redundant implementation for the Array class.


error.c

rb_sys_fail()

In MRI, if errno isn't set when rb_sys_fail() is called within an extension it will segfault. Since error handling is not typically handled via errno on MS Windows but instead through GetLastError(), this means MRI Ruby will segfault from time to time when rb_sys_fail() is called instead of providing a useful or correct error message.

In Sapphire rb_sys_fail() resorts to GetLastError() and creates a message using rb_w32_strerror().


Etc

The etc library has been removed for several reasons.

Replacement

Use the sys-admin library instead.


File

File.basename [TODO]

Fixed on MS Windows. It previously did not handle UNC paths properly.

File.dirname [TODO]

Fixed on MS Windows. It previously did not handle UNC paths properly.

MRI also contains inconsistencies with regards to path normalization. It strips extra leading slashes, but leaves others.

For example:
File.dirname("////foo//bar/baz") # => /foo//bar (MRI)

Sapphire will normalize all paths.

File.expand_path [TODO]

The "~user" notation is now supported on Windows.

File.file?

Fixed on MS Windows. It previously returned the wrong result if called on a directory.

File.size? [TODO]

Returns true or false instead of nil or a size.

File.split [TODO]

The semantics for splitting a root path were never clearly defined, and didn't work at all on MS Windows. It is now defined as follows: Keep in mind that on MS Windows the following are considered root paths:

On both platforms, all trailing slashes will be stripped before determining whether or not a path is a root path.


file-find

The file-find library replaces the old find module in MRI because it was little more than a glorified Dir.glob call. The file-find library is based on the typical find command found on Unix systems (though it works on MS Windows as well).

Synopsis

require 'file/find'

rule = File::Find.new(
   :pattern => "*.rb",
   :follow  => false,
   :path    => ['/usr/local/lib', '/opt/local/lib']
)

rule.find{ |f|
   puts f
}

File-Stat

Additional Methods

File-Stat#name

Returns the name of the file passed to the constructor.

Refactored Methods

File-Stat#blksize

Now works on Windows.

File-Stat#blocks

Now implemented on Windows. Uses File::Stat.size divided by File::Stat.blksize, rounded up.


file-temp

The file-temp library replaces the tempfile library in MRI. It has several advantages over MRI's tempfile library.

Synopsis

require 'file/temp'

# Automatically delete the file when finished
fh = FileTemp.new
fh.puts "hello" 
fh.close

# Allow the temp file to live on in your FileTemp::TMPDIR directory.
fh = FileTemp.new(false)
fh.puts "world" 
fh.close

# Specify a custom naming template
FileTemp.new(false, 'my_template_XXXXXX') do |fh|
   fh.puts "test" 
end

find

[REMOVED]

The Find module has been removed from the Sapphire standard library.

Reason for removal

It was little more than a slightly glorified call to Dir.glob and its replacement is far more useful.

Replacement

file-find


Fine Grained Mixins

Sapphire's fine grained mixins solve the multi-mixin problem without resorting to the static composition of Traits. In short, it allows you to selectively include, exclude or alias methods at the moment of inclusion.

Altering include

Whereas Ruby's include method includes all methods from a given module into a class, Sapphire alters the include method to accept a list of methods to selectively include. The following example illustrates a class that mixes in the method_a method from ModA, and the method_b method from ModB:
module ModA
   def method_a; end
   def method_b; end
end

module ModB
   def method_a; end
   def method_b; end
end

# Mixin 'method_a' from ModA, and 'method_b' from ModB
class ClassA
   include ModA, :method_a
   include ModB, :method_b
end

Explicit exclusion

If a module has a large number of methods, and a programmer wants all but one or two them, the above notation quickly becomes cumbersome. Therefore, Sapphire accepts an exclude parameter, which accepts an array of method names to explicitly exclude from the mixed in module. The rest of the module's methods are included. The following example excludes method_a and method_e from being mixed into ClassA, accepting the rest of the methods:
module ModA
   def method_a; end
   def method_b; end
   def method_c; end
   def method_d; end
   def method_e; end
end

# Mixin all methods except 'method_a' and 'method_e'
class ClassA
   include ModA, :exclude => [:method_a, :method_e]
end

On the fly aliasing

In some cases a programmer may want the functionality of a particular method from a given module, but at the same time needs to avoid a name collision. Sapphire's include method accepts the alias keyword parameter that allows you to alias a method name on the fly, including the method by the alias rather than its original name. The following example mixes in method_a from ModA into ClassA, but with the name my_method_a:

module ModA
   def method_a; end
   def method_b; end
end

# Mixin 'method_a' from ModA, but as 'my_method' instead.
class ClassA
   include ModA, :alias => [:method_a, :my_method]
end

a = ClassA.new
a.my_method # Legal

Rules


forwardable

Notes

The forwardable library included in Sapphire is a heavily refactored version of the one that ships with MRI.

The SingleForwardable module has been removed entirely since it was rarely used and not very useful. Instead a single, unified interface has been provided. This is in addition to some code cleanup (making it warning free) and documentation improvements.

Synopsis

class Foo
   extend Forwardable
   delegate :length => :@str
   delegate [:first, :last] => :@arr

   def initialize
      @arr = ["foo","bar","baz"]
      @str = "hello" 
   end
end

f = Foo.new
puts f.length # 5, length of @str
puts f.first  # "foo", first element of @arr
puts f.last   # "baz", last element of @arr

Acknowledgements

Florian Groß was a co-author on the new Forwardable module.


ftools

[REMOVED]

The ftools library has been removed from the Sapphire standard library.

Reason for removal

It was redundant and already considered deprecated in MRI. The fileutils library has the same features, and is better code.


gdbm

[REMOVED]

The gdbm library has been removed from the Sapphire standard library.

Reason for removal

It's a rarely used simple data store library designed originally for Unix. Windows performance is poor and there are better alternatives for personal data stores.

Replacement

KirbyBase


Hash

Instance Methods

Hash#==

If the RHS argument is a non-hash and it responds to the to_hash method, that will be used for the comparison.

Hash#[]

Redefined to allow you to select multiple keys simultaneously (i.e. hash slicing), returning an array of values.
hash = {'a' => 1, 'b' =>2, 'c' => 3}
hash['a', 'b'] => [1, 2]

Note that there is one subtle difference between Hash#[] and Hash#values_at. The former only returns a single value if a single key is provided, while the latter returns an array regardless of the number of keys provided.

hash.values_at('a') => [1]
hash['a'] => 1

Hash#[]=

Redefined to handle slice assignment.
hash = {'a' => 1, 'b' =>2, 'c' => 3}

# One to one
hash['a', 'b'] = 'hello', 'world'
hash['a'] => 'hello'
hash['b'] => 'world'

# One to many
hash['a', 'b] = 7
hash['a', 'b'] => [7, nil]

# Many to one
hash['a'] = 'hello', 'world'
hash['a'] => ['hello', 'world']

Hash#store

Removed. No one used this alias for Hash#[]= in practice.


Implicit Getters and Setters [MAYBE]

This has always felt a little clumsy to me in Ruby:
class Foo
   attr_accessor :one, :two
   def initialize(one, two)
      @one = one
      @two = two
   end
end

The above in Sapphire will be accomplished through a Behavior:
behavior "accessors/automatic" 

class Foo
   attr_accessor :one, :two
end

Improved Unicode Support

Ah, yes. The holy grail. Unicode Support. That thing that every programmer wants but no one can define without degenerating into politically correct rants, charges of racism, arguments about file encodings, and debates centered around your favorite locale.

What do I mean when I say Unicode Support for Sapphire? First, alterations to the String class, both in terms of the API and its underlying implementation. Second, regular expressions.

Strings

[INCOMPLETE]


Kernel

Kernel#select

STDIN no longer blocks threads on MS Windows.


memoize

Memoization is a fairly standard and easy technique to speed up some methods. It can be quite useful for eliminating bottlenecks. The memoize library has therefore been included in the Sapphire standard library.

Synopsis

require 'memoize'
include Memoize

# Inefficient fibonacci method
def fib(n)
   return n if n < 2
   fib(n-1) + fib(n-2)
end

fib(100) # Slow

memoize(:fib)
fib(100) # Fast

# Or store the cache to a file for later use
memoize(:fib, "fib.cache")
fib(100) # Fast

Method Annotations

Method annotations in Sapphire are an amalgam of ideas taken from languages like Fortress and Python. They allow programmers to add metadata to a method definition without affecting its behavior.

[WARNING: The following code may affect behavior, too. I haven't decided.]

Type Annotations

The following method definition appears to be a form of static typing but is, in fact, a method annotation:

def some_method(String x, Integer y)
   # ...
end

# Or, another possibility along the lines of Scala.

def some_method(x: String, y: Integer)
  # ...
end

How is this useful? First, it provides additional information about the method that can be later retrieved by the (hypothetical) Method#signature method.

m = method(:some_method)
m.signature # [String, Integer]

Second, it provides a way for documentation systems such as RDoc to document the expected type of any given argument.

Return Type Annotations

An expected return type may also be added to the method definition:

def some_method(x, y) => Fixnum
   ...
end

This information could be retrieved by the (hypothetical) Method#return_type method. It could also be used by documentation systems like RDoc to document the expected return type.

m = method(:some_method)
m.return_type # Fixnum

Putting it all together

Here's an example that includes type annotations, default values and a return type.

def some_method(String x = 'hello', Integer y = 7) => Fixnum
  # ...
end

# A different variant

def some_method(x: String => 'hello', y: Integer => 7) => Fixnum
  # ...
end

This still looks fairly clean IMHO.

Other Possible Benefits

This could be used to optimize methods, too, along the same lines as DRuby or Duby.


MOTIVATION

Ruby is badly managed

Look here: http://rubyforge.org/tracker/?group_id=426

283 open bugs. 63 untouched patches. Some of those are several years old.

The Ruby standard library needs an overhaul

Too many libraries are dependent on third party libraries , many are deprecated, many do not work on Windows, a few are Japanese-specific, several are generally useless, and some just plain suck.

There is very little refactoring in the core Ruby language or standard library.

I'm not talking about bug fixes. I'm talking about reworking the overall design of any given class or library. Or even just general cleanup efforts. It happens rarely.

Ruby is poorly tested

Ruby has about 1500 core tests. That's it. The rest are from the standard library, and their quantity and quality varies wildly depending on the library.

MS Windows is a second class citizen

Ruby has clearly focused on Unix (specifically, Linux) over Windows because that's what Matz develops on. Some of the core kernel methods don't work properly on Windows, most notably Kernel#select. Other issues range from the use of deprecated functions (sometimes in an effort to support 95/98/ME) to bugs (File.size doesn't work on files larger than 2gb) to generally cruddy implementations (File.basename).

I'm tired of arguing with the core list members!

It has become a major hassle to get even the simplest enhancements accepted. So, I've gone my own way.

I want to add features the core developers aren't interested in

The (tentative) list includes method annotations, type inferencing, keyword arguments (not quite the same as 2.0), selector namespaces, structured warnings, something to replace $SAFE levels (probably some form of sandboxing), AOP support and fine grained mixins.


Named Parameters

Sapphire will support named parameters like so:

class Foo
  def initialize(name, age)
    # ...
  end
end

f = Foo.new(:age => 38, :name => "Daniel")

In short, the argument names a programmer uses in the method definition automatically become keywords. Those keywords can then be used instead of positional parameters. Note that positional parameters would still be valid also:

f = Foo.new("Daniel", 38)

Or, a combination of both positional and keyword arguments:

f = Foo.new(:age => 38, "Daniel")

However, mixing and matching named and positional arguments should generally be frowned on, since it becomes confusing as to what the positions are in methods with larger signatures.

Explicit hashes

The notation that Sapphire uses requires that explicit hashes be used to disambiguate between keyword arguments and literal hashes:
class Foo
  def initialize(name, options)
    # ...
  end
end

f = Foo.new(:name => "Daniel", :options => {:red => true, :blue => false})

Alternative Syntax

I'd also consider something along the lines of Scala's argument syntax: http://www.scala-lang.org/node/2075

The types could be used for some sort of inferencing engine, or simply be annotative.

class Foo
  # Standard parameters
  def method_a(name, age)
    # ...
  end

  # Parameters with defaults
  def method_b(name: "John", age: 30)
    # ...
  end

  # Parameters with types
  def method_c(name: String, age: Fixnum)
    # ...
  end

  # Parameters with types and default arguments
  def method_d(name: String => "John", age: Fixnum => 30)
    # ...
  end

net-ping

The net-ping library replaces the old ping library that shipped with MRI. It was added because it supports more protocol types, has improved error and warning handling, and a better interface in general.

The following protocols are supported:

An External ping class may also be added, but is dependent on making the open3 library cross-platform.


nkf

[REMOVED]

The nkf library has been removed from the Sapphire standard library.

Reason for removal

The nkf library is Japanese specific. Sapphire will not include any language specific libraries as part of its standard library.


Object

Object#blank?

By default all objects are blank if they are nil. Individual subclasses define their own blank? methods.

This primarily affects String, Array and Hash objects. All three return true if the object is either nil or empty.

Inspiration

This was originally inspired by Rails, but I've wished for it outside of Rails so I've added it.

Object#in?

Returns a boolean value indicating whether or not object is part of an enumerable object of some sort. The argument to Object#in? must respond to the include? method.

a = ['alpha', 1, 2, 3]

2.in?(a)     # => true
'foo'.in?(a) # => false
3.in?(4)     # => ArgumentError

Object#to_bool

Returns a boolean indicating whether or not the object is nil or false. To be true, an object must not be nil and not be false.

Inspiration

An idiom I see in practice that I don't like is the double negation syntax that some programmers use in order to determine if the object is false or nil:

!!var       # => ick
var.to_bool # => more readable

C extension writers will recognize that this is just an RTEST() call.


ping

[REMOVED]

The ping library has been removed from the Sapphire standard library.

Reason for removal

The MRI ping library only supports the TCP protocol, doesn't allow configuration in terms of handling Errno::ECONNREFUSED, and provides no inspection for warnings.

Replacement

net-ping


Process changes

Process.ppid

Now works properly on MS Windows.

Yes, Windows processes have a parent process, even if the relationship isn't the same as it is on Unix


Rake

Rake has become the defacto build and installation tool for the Ruby community and the author is a great guy. It has become a very useful library that absolutely warrants inclusion in the standard library.

http://rake.rubyforge.org


Ruby

Ruby is a computer programming language written by Yukihiro Matsumoto.

http://www.ruby-lang.org


Sapphire

Sapphire is a fork of the Ruby programming language.

CHANGES
MOTIVATION

Overview

Planned Core Features (Tentative)

Supported Platforms

Status

Sapphire is still in the development phase, but work is definitely progressing.

Git

I've moved the source over to git.

http://github.com/djberg96/Sapphire

Mailing Lists

Visit http://sapphire-lang.org/mailman/listinfo/sapphire-core_sapphire-lang.org for subscription options.


sdbm

[REMOVED]

The sdbm library has been removed from the Sapphire standard library.

Reason for removal

It's a rarely used simple data store library designed originally for Unix. Windows performance is poor and there are better alternatives for personal data stores.

Replacement

KirbyBase


Selector Namespace

Description

Selector namespaces are a way to provide a distinction between identical method names in the same object. As modules can be used to provide a namespace for classes, selector namespaces provide a namespace for individual methods within that class.

Use Cases

Selector namespaces would have two main use cases. First, subclasses can use it to override an inherited method. The overridden method in question would make sense in its own context. However a programmer may still wish to access its parent's method of the same name, either to avoid confusion for the end user of the class in question, or to provide a way for end users to access either method in its appropriate context, or both.

Consider the following example with our theoretical notation:
class UString < String
   namespace :unicode

   # chars instead of bytes
   def size
      wstrlen(self)
   end
end

string = UString.new("Ελλάσ") # "Hellas (Greece)" 

p string.size          # 10
p string::unicode.size # 5

The second use case is as an alternative, safe way to monkeypatch a base Sapphire class instead of subclassing. For example, there was recently a conflict between Ruby and Rails with regards to the String#chars method. The Rails core team defined a String#chars method, only to have it defined in later versions of Ruby, but the implementations were incompatible.

Here's how that case would be handled with selector namespaces:
class String
   namespace :rails

   # array of chars
   def chars
      self.split('//')
   end
end

s = "hello" 
s.chars        # Ruby's base String#chars method
s::rails.chars # The Rails String#chars method

However, the above notation would quickly become impractical if we had to explicitly define the namespace for every call of every object that used our own custom method. Instead, a block form would be used, where everything within that block uses that particular namespace. Consider the following example:
namespace :rails do
   s = "hello" 
   s.chars # The chars method Rails has defined.
end

s.chars # Ruby's base String#chars method

Global Namespaces

Another idea would be to allow programmers to define a global namespace, ala C++, at the top of their script. All method calls made after that declaration would default to that namespace. For example:
using namespace 'rails'
s = "hello" 
s.chars # The Rails String#chars method

Modules

A namespace could be defined in a module and mixed in the same way a class defines a namespace. For example:
module MyModule
   namespace :mymodule

   def some_method
      puts "hello" 
   end
end

class MyClass
   include MyModule

   # Our own definition of some_method
   def some_method
      puts "world" 
   end
end

m = MyClass.new
m.some_method           # 'world'
m::mymodule.some_method # 'hello'

Conflicting Namespaces

If two modules were mixed in with the same namespace, the last module included wins, though a NamespaceWarning would be issued. For example:
module One
   namespace :number
   def hello
      puts "hello" 
   end
end

module Two
   namespace :number
   def hello
      puts "hi" 
   end
end

class MyClass
   include One
   include Two # raises a NamespaceWarning
end

m = MyClass.new
m::number.hello # 'hi'

Alternate Notation

I am leaning towards using the double colon notation strictly for namespaces. This would cause some breakage with Ruby code, but I find that the double colon notation is uncommon in the wild, and the duplicate notation has always annoyed me a little. By using re-using the double colon I not only disambiguate the two different kinds of notation, I can still keep the final notation Sapphire-ish.

However, I have not ruled out other notations. Another possibility is to use a single ":" to identify a namespace. This has the advantage of being a little shorter, and being familiar to Rake users. Two possibilities for this notation are:
s:unicode.size # Object:Namespace.method
unicode:s.size # Namespace:Object.method

The second is more Rake-ish.

skip example

require 'test/unit'
require 'rbconfig'
include Config

# tc_some_test.rb
class TC_SomeTest < Test::Unit::TestCase
   def setup
      @obj = MyUnixClass.new
   end

   # Skip this test on MS Windows
   def test_something
      skip('MS Windows'){ CONFIG['host_os'] =~ /mswin/i }
      assert_equal(5, @obj.test)
   end

   def test_something_else
      assert_equal(true, @obj.foo?)
      assert_equal(true, @obj.bar?)
   end

   def test_yet_something_else
      assert_equal(7, @obj.whatever)
   end

   def teardown
      @obj = nil
   end
end

If this test case is run on MS Windows the output will look something like this:
Loaded suite tc_some_test
Started
.S.
Finished in 0.0 seconds.

3 tests, 3 assertions, 0 failures, 0 errors, 1 skipped

There were three tests, but one test was skipped. The three assertions that ran were from the test_something_else and test_yet_something_else methods.

The 'S' in the runner output is a visual indicator that a test was skipped.


String Changes

String#blank?

This is a new method that returns true if the string is empty, i.e. is an empty string or contains nothing but whitespace.

Inspired by Rails, but I think it's a useful method for general programming needs.


Structured Warnings

Visit http://www.oreillynet.com/ruby/blog/2008/02/structured_warnings_now.html for a summary of what's to follow.

Ruby currently doesn't not support structured warnings, i.e. it uses the 'warn' method, and that emits a message to STDERR. It's behavior is governed by -W levels ranging from 0 to 2.

Problems with Ruby's warn system

Warnings in Sapphire

Sapphire will implement a structured warning system. In other words, there will be a hierarchy of warning classes that all descend from a base Warning class. Examples include TypeWarning, UninitializeWarning, UnusedBlockWarning, etc. A specific warning can then raised:
class Foo
  def old_method
    warn DeprecatedWarning, "This method is deprecated. Use 'new_method' instead." 
    # Continue on
  end
end

Testing Warnings

The ability to explicitly raise specific types of warnings then makes them testable. For example:
require 'test/unit'

class TC_Foo_Tests < Test::Unit::TestCase
  def setup
    @foo = Foo.new
  end

  # Assume we've added an assert_warn method to Test::Unit
  def test_old_method
    assert_warn(DeprecatedMethodWarning){ @foo.old_method }
  end
end

Disabling Warnings

Warnings can be disabled globally or temporarily within a block. For example, to globally disable deprecation warnings you could do this:
DeprecatedWarning.disable
To disable deprecation warnings temporarily, use a block. For example:
# No warnings here.
DeprecatedMethodWarning.disable{
  [1,2,3,4,5].indexes(1,3) # Array#indexes is a deprecated method
}

# But here I would get a warning since it's outside the block:
[1,2,3,4,5].indexes(1,3)

Other Semantics

Third Party Implementations

Gregor Schmidt has implemented structured warnings as a library. See http://rug-b.rubyforge.org/structured_warnings/ for more information.


Sys::Admin

The sys-admin library is a cross platform replacement for Ruby's Etc module. It has several advantages over Etc.

The sys-admin library can be found at http://www.rubyforge.org/projects/sysutils


syslog

[REMOVED]

The syslog library has been removed from the Sapphire standard library.

Reasons for Removal

Replacement

None.


tempfile

[REMOVED]

The tempfile library has been removed from the Sapphire standard library.

Reason for removal

The tempfile code is overwrought, susceptible to race conditions, and lacks basic umask security.

Replacement

file-temp


test-unit

The Test::Unit library includes the following changes.

Additional Instance Methods

Test::Unit::TestCase#startup

This hook is called only once, prior to any tests being run for the given test suite. Compare versus the setup method, which is called once per test.

Handy for one time operations, like socket connections.

Test::Unit::TestCase#shutdown

This hook is called only once, after all tests have been run for the given test suite. Compare versus the teardown method, which is called once per test.

Generally used in conjunction with the startup method.

Test::Unit::Assertions#assert_raise_kind_of(parent_error_class)

For those cases where you know a test should raise an error, but you can't predict exactly what kind of error it will raise. Primarily useful for dealing with system specific errors in a more general way, where different platforms may raise different errors, but you cannot know in advance exactly which error will be raised.

Test::Unit::Assertions#assert_boolean(value)

Used to check if value is true or false.

Test::Unit::Assertions#assert_false(condition)

Handy shortcut for assert_equal(false, ...)

Test::Unit::Assertions#assert_true(condition)

Handy shortcut for assert_equal(true, ...)

Test::Unit::Assertions#assert_raise_message(message){ code }

Tests that the specific error message is raised by the provided block.

Test::Unit::Assertions#skip(message){ condition }

Allows you to skip a given test based on condition, which is then tallied as a skipped test by the runner. The message is optional.

Typically used for platform specific tests.

skip example

Removed Instance Methods

Test::Unit::Assertions#assert_raises

Removed, deprecated.

Acknowledgements

Thanks go to Edwin Fine for the original source code for the assert_raise_kind_of method.


Time

Time#before? and Time#after?

No one can seem to remember how Time#> or Time#< work. So, I've added before? and after? aliases.

halloween = Time.mktime(2010, 10, 31)
christmas = Time.mktime(2010, 12, 25)

halloween.before?(christmas) # => true
christmas.after?(halloween)  # => true

tmpdir

[REMOVED]

The tmpdir library has been removed from the Sapphire standard library.

Reason for removal

The Dir.tmpdir method is now a core Dir method.


Warning

Sapphire uses structured warnings. This allows you to enable or disable warnings either globally or within a block. It also makes warnings testable.

Synopsis

You "raise" a warning the same way you raise an exception. The difference is that warnings don't break control flow, and aren't rescuable. They are informational only.

class Foo
  def old_method
    warn DeprecatedWarning, "this method is old" 
    # some old code...
  end
end

A warn call without a specific class raises a StandardWarning

class Foo
  def old_method
    warn "this method is old" # No warning class defaults to StandardWarning
  end
end

Disabling Warnings

A Warning class can be disabled locally or globally. If a block is provided, the warning is disabled only within the scope of that block. Otherwise, it is disabled at the point of the call.

# Disable globally
DeprecatedWarning.disable

# Disable within code block
DeprecatedWarning.disable do
  # Ignore deprecation warnings within this block
end

Advantages

1. Better control. No more "on" vs "off".
2. Better information. You can get backtrace information.
3. Testable. Both that a warning is raised, and the warning message.

Article

http://www.oreillynet.com/ruby/blog/2008/02/structured_warnings_now.html


win32-api

The win32-api library replaces the old Win32API library. Advantages include:

You can find a current version of win32-api at http://www.rubyforge.org/projects/win32utils


Win32API

[REMOVED]

The Win32API library has been removed from the Sapphire standard library.

Reasons for removal

Replacement

win32-api


win32.c