[REMOVED]
This library has been removed from the Sapphire standard library.
The abbrev library is rarely used and frankly, not very useful.
Aspect Oriented Programming. You can read about AOP at http://en.wikipedia.org/wiki/Aspect_oriented_programming.
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.
class String
def size
puts "42" # pre
super
end
end
"hello".size # Prints "42", then returns the size, 5.
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"
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
Now honors custom to_ary methods for RHS objects.
Now raises an ArgumentError if you specify a negative length for the second form.
Redundant implementation removed. It now uses the mixed-in Enumerable#collect method.
Removed, useless.
Note that removing this method required an update to optparse.rb.
Removed, deprecated.
Removed, deprecated.
Providing an index with no other arguments now raises an ArgumentError.
Now requires at least one argument or an ArgumentError is raised.
Now a bonafide alias of Array#[] instead of a synonym.
Uses the same code as Array#inspect. They are now aliases.
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.
Redundant implementation removed. It now uses the mixed-in Enumerable#zip method.
Taking a cue from functional languages like Erlang and Fortress, Sapphire will add implement certain list handling methods in an asynchronous fashion.
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.
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.
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.
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.
See Coding Standards for Sapphire coding guidelines.
The stdint.h for Windows can be found at http://msinttypes.googlecode.com/svn/trunk/stdint.h
// 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¶
[REMOVED]
The dbm library has been removed from the Sapphire standard library.
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.
The digest-bubblebabble library has been replaced with a pure Sapphire version.
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.
A pure Sapphire library version is easier to maintain. A C extension is overkill for such a simple library.
Now behaves like Dir.open in that it takes a block and automatically closes the handle.
This is now an alias for Dir.new, which itself adopted the original Dir.open semantics.
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.
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.
There is a custom implementation in dir.c, but Ruby isn't using it. Sapphire does.
[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.
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.
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().
The etc library has been removed for several reasons.
Use the sys-admin library instead.
Fixed on MS Windows. It previously did not handle UNC paths properly.
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.
The "~user" notation is now supported on Windows.
Fixed on MS Windows. It previously returned the wrong result if called on a directory.
Returns true or false instead of nil or a size.
On both platforms, all trailing slashes will be stripped before determining whether or not a path is a root path.
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).
require 'file/find'
rule = File::Find.new(
:pattern => "*.rb",
:follow => false,
:path => ['/usr/local/lib', '/opt/local/lib']
)
rule.find{ |f|
puts f
}
Returns the name of the file passed to the constructor.
Now works on Windows.
Now implemented on Windows. Uses File::Stat.size divided by File::Stat.blksize, rounded up.
The file-temp library replaces the tempfile library in MRI. It has several advantages over MRI's tempfile library.
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
[REMOVED]
The Find module has been removed from the Sapphire standard library.
It was little more than a slightly glorified call to Dir.glob and its replacement is far more useful.
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.
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
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
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
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.
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
Florian Groß was a co-author on the new Forwardable module.
[REMOVED]
The ftools library has been removed from the Sapphire standard library.
It was redundant and already considered deprecated in MRI. The fileutils library has the same features, and is better code.
[REMOVED]
The gdbm library has been removed from the Sapphire standard library.
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.
If the RHS argument is a non-hash and it responds to the to_hash method, that will be used for the comparison.
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 = {'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']
Removed. No one used this alias for Hash#[]= in practice.
class Foo
attr_accessor :one, :two
def initialize(one, two)
@one = one
@two = two
end
end
behavior "accessors/automatic" class Foo attr_accessor :one, :two end
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.
[INCOMPLETE]
STDIN no longer blocks threads on MS Windows.
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.
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 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.]
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.
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
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.
This could be used to optimize methods, too, along the same lines as DRuby or Duby.
Look here: http://rubyforge.org/tracker/?group_id=426
283 open bugs. 63 untouched patches. Some of those are several years old.
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.
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 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.
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).
It has become a major hassle to get even the simplest enhancements accepted. So, I've gone my own way.
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.
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.
class Foo
def initialize(name, options)
# ...
end
end
f = Foo.new(:name => "Daniel", :options => {:red => true, :blue => false})
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
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.
[REMOVED]
The nkf library has been removed from the Sapphire standard library.
The nkf library is Japanese specific. Sapphire will not include any language specific libraries as part of its standard library.
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.
This was originally inspired by Rails, but I've wished for it outside of Rails so I've added it.
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
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.
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.
[REMOVED]
The ping library has been removed from the Sapphire standard library.
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.
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 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.
Ruby is a computer programming language written by Yukihiro Matsumoto.
Sapphire is a fork of the Ruby programming language.
Sapphire is still in the development phase, but work is definitely progressing.
I've moved the source over to git.
http://github.com/djberg96/Sapphire
Visit http://sapphire-lang.org/mailman/listinfo/sapphire-core_sapphire-lang.org for subscription options.
[REMOVED]
The sdbm library has been removed from the Sapphire standard library.
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.
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.
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
namespace :rails do s = "hello" s.chars # The chars method Rails has defined. end s.chars # Ruby's base String#chars method
using namespace 'rails' s = "hello" s.chars # The Rails String#chars method
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'
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'
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
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
Loaded suite tc_some_test Started .S. Finished in 0.0 seconds. 3 tests, 3 assertions, 0 failures, 0 errors, 1 skipped
The 'S' in the runner output is a visual indicator that a test was skipped.
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.
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.
class Foo
def old_method
warn DeprecatedWarning, "This method is deprecated. Use 'new_method' instead."
# Continue on
end
end
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
DeprecatedWarning.disableTo 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)
Gregor Schmidt has implemented structured warnings as a library. See http://rug-b.rubyforge.org/structured_warnings/ for more information.
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
[REMOVED]
The syslog library has been removed from the Sapphire standard library.
None.
[REMOVED]
The tempfile library has been removed from the Sapphire standard library.
The tempfile code is overwrought, susceptible to race conditions, and lacks basic umask security.
The Test::Unit library includes the following changes.
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.
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.
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.
Used to check if value is true or false.
Handy shortcut for assert_equal(false, ...)
Handy shortcut for assert_equal(true, ...)
Tests that the specific error message is raised by the provided block.
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.
Removed, deprecated.
Thanks go to Edwin Fine for the original source code for the assert_raise_kind_of method.
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
[REMOVED]
The tmpdir library has been removed from the Sapphire standard library.
The Dir.tmpdir method is now a core Dir method.
Sapphire uses structured warnings. This allows you to enable or disable warnings either globally or within a block. It also makes warnings testable.
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
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
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.
http://www.oreillynet.com/ruby/blog/2008/02/structured_warnings_now.html
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
[REMOVED]
The Win32API library has been removed from the Sapphire standard library.