From noreply at rubyforge.org Wed Jul 18 21:29:56 2007 From: noreply at rubyforge.org (noreply at rubyforge.org) Date: Wed, 18 Jul 2007 21:29:56 -0400 (EDT) Subject: [Facets] [ facets-Patches-12397 ] FileUtils.which/whereis: Add .cmd to other extensions (.bat, .exe) Message-ID: <20070719012956.87AE05240F0A@rubyforge.org> Patches item #12397, was opened at 2007-07-18 18:29 You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3171&aid=12397&group_id=804 Category: None Group: None Status: Open Resolution: None Priority: 3 Submitted By: Tyler Rick (tylerrick) Assigned to: Nobody (None) Summary: FileUtils.which/whereis: Add .cmd to other extensions (.bat, .exe) Initial Comment: In both of these files: /usr/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/fileutils/which.rb /usr/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/fileutils/whereis.rb I believe that .cmd should be added as a valid win32 executable extension, like so: if defined?(Win32Exts) Win32Exts ||= %w{.exe .com .bat .cmd} else Win32Exts = %w{.exe .com .bat .cmd} end I only discovered this .cmd extension today, but apparently that's how RubyGems works on windows: it creates a .cmd wrapper for all executables specified in a gems gemspec when you install the gem. Examples: >dir c:\ruby\bin\rails.* Volume in drive C has no label. Volume Serial Number is 4C33-932F Directory of c:\ruby\bin 2007-07-17 11:12 352 rails 2007-07-17 11:12 30 rails.cmd 2 File(s) 382 bytes 0 Dir(s) 1,827,725,312 bytes free rails.cmd is what gets executed when you type rails from the command line. (And then rails.cmd immediately calls 'ruby rails %*') But FileUtils.which returns nil!: >irb irb> require 'facets/core/fileutils/whereis' => true irb> require 'facets/core/fileutils/which' => true irb> FileUtils.which('rails') => nil irb> FileUtils.whereis('rails') => nil Another example: > irb irb> require 'facets/core/fileutils/which' irb> require 'facets/core/fileutils/whereis' irb> ENV['PATH'] => "C:\ruby\lib\ruby\gems\1.8\gems\subwrap-0.3.3\bin;c:\ruby\bin;C:\Program Files\Subversion\bin" # This should return # "C:\ruby\lib\ruby\gems\1.8\gems\subwrap-0.3.3\bin\svn.cmd" # since that directory is listed first in ENV['PATH'], but instead # it only lists the svn.exe file irb> FileUtils.which('svn') => "C:\Program Files\Subversion\bin\svn.exe" # Currently, the only way to find a .cmd file is to explicitly include .cmd in the string you pass to FileUtils.which irb> FileUtils.which('svn.cmd') => "C:\ruby\lib\ruby\gems\1.8\gems\subwrap-0.3.3\bin\svn.cmd" Thanks, Tyler :-) ---------------------------------------------------------------------- You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3171&aid=12397&group_id=804 From noreply at rubyforge.org Wed Jul 25 01:26:00 2007 From: noreply at rubyforge.org (noreply at rubyforge.org) Date: Wed, 25 Jul 2007 01:26:00 -0400 (EDT) Subject: [Facets] [ facets-Feature Requests-12559 ] Improved snapshot.rb Message-ID: <20070725052600.75B425240F02@rubyforge.org> Feature Requests item #12559, was opened at 2007-07-24 22:25 You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3172&aid=12559&group_id=804 Category: None Group: Next Release (example) Status: Open Priority: 3 Submitted By: Nasir Khan (rnakhan) Assigned to: Nobody (None) Summary: Improved snapshot.rb Initial Comment: Consider adding optional fields argument to both take_snapshot() and restore_snapshot() to selectively take snapshot of selected fields as against all fields which is the default. Patch provided below over 1.8.54 : methods changes take_snapshot and restore_snapshot. # = snapshot.rb # # == Copyright (c) 2004 Michael Neumann # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. # # == Author(s) # # * Michael Neumann # # == Developer Notes # # TODO Perhaps extend to offer multiple depths. # # TODO Should key consitancy be enforced? Currently # Struct's will have symobl keys while other classes # will have string keys in the form of "@name". # # TODO Add other core classes. # # TODO Convert to Kernel#to_data ? # Author:: Michael Neumann # Copyright:: Copyright (c) 2004 Michael Neumann # License:: Ruby License # = Snapshot # # A lightweight single-depth object state capture. # The #take_snapshot method reads the object's state, # which is generally it's collection of instance variables, # and returns them in a hash. The state can be restored # with #apply_snapshot. # # == Usage # # Customer = Struct.new("Customer", :name, :address, :zip) # joe = Customer.new( "Joe Pitare", "1115 Lila Ln.", 47634 ) # # # simple transactions # joe_snap = joe.take_snapshot # begin # do_something_with( joe ) # rescue # joe.apply_snapshot( joe_snap ) # end # # joe_snap[:name] => "Joe Pitare" # joe_snap[:address] => "1115 Lila Ln." # joe_snap[:zip] => 47634 # # == Details # # Class Snapshot simply represents a collection of objects from # which snapshots were taken via their methods #take_snapshot. # It provides methods to add an object to a snapshot # (Snapshot#add) as well as to restore all objects # of the snapshot to their state stored in the snapshot (method # Snapshot#restore). # # In Wee, this class is used to backtracking the state of # components (or decorations/presenters). Components that want # an undo-facility to be implemented (triggered for example by # a browsers back-button), have to overwrite the # Wee::Component#backtrack_state method. class Snapshot def initialize @objects = Hash.new end def add(object) oid = object.object_id @objects[oid] = [object, object.take_snapshot] unless @objects.include?(oid) end def restore @objects.each_value { |object, value| object.restore_snapshot(value) } end end # Simplist form of a snapshot. # #module SnapshotMixin # def take_snapshot() dup end # def restore_snapshot(snap) replace(snap) end #end # Implements a value holder. In Wee this is useful for # backtracking the reference assigned to an instance variable # (not the object itself!). An example where this is used is the # @__decoration attribute of class Wee::Component. class Snapshot::ValueHolder attr_accessor :value def initialize(value=nil) @value = value end def take_snapshot @value end def restore_snapshot(value) @value = value end end #-- # Extend some base classes of Ruby (Object, Array, String, Hash, # Struct) for the two methods #take_snapshot and # #restore_snapshot, required by Snapshot. #++ class Object def take_snapshot(fields=nil) snap = Hash.new fields = instance_variables unless fields fields.each do |iv| snap[iv] = instance_variable_get(iv) end snap end def restore_snapshot(snap, fields=nil) fields = instance_variables unless fields fields.each do |iv| instance_variable_set(iv, snap[iv]) end end end class Array def take_snapshot() dup end def restore_snapshot(snap) replace(snap) end end class String def take_snapshot() dup end def restore_snapshot(snap) replace(snap) end end class Hash def take_snapshot() dup end def restore_snapshot(snap) replace(snap) end end class Struct def take_snapshot snap = Hash.new each_pair {|k,v| snap[k] = v} snap end def restore_snapshot(snap) snap.each_pair {|k,v| send(k.to_s + "=", v)} end end # _____ _ # |_ _|__ ___| |_ # | |/ _ \/ __| __| # | | __/\__ \ |_ # |_|\___||___/\__| # =begin testing require 'test/unit' class TC_Snapshot < Test::Unit::TestCase def setup customer = Struct.new("Customer", :name, :address, :zip) joe = customer.new( "Joe Pitare", "1115 Lila Ln.", 47634 ) @joe_snap = joe.take_snapshot end def test_storage assert_equal( "Joe Pitare", @joe_snap[:name] ) assert_equal( "1115 Lila Ln.", @joe_snap[:address] ) assert_equal( 47634, @joe_snap[:zip] ) end end =end ---------------------------------------------------------------------- You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3172&aid=12559&group_id=804 From noreply at rubyforge.org Thu Jul 26 05:22:40 2007 From: noreply at rubyforge.org (noreply at rubyforge.org) Date: Thu, 26 Jul 2007 05:22:40 -0400 (EDT) Subject: [Facets] [ facets-Bugs-10372 ] String#index_all breaks when passed a String for a search term Message-ID: <20070726092241.3A15B5240A26@rubyforge.org> Bugs item #10372, was opened at 2007-04-25 18:34 You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3169&aid=10372&group_id=804 Category: None Group: None >Status: Closed >Resolution: Accepted Priority: 3 Submitted By: Tyler Rick (tylerrick) Assigned to: Nobody (None) Summary: String#index_all breaks when passed a String for a search term Initial Comment: The existing test shows that it works for Regexp as input: def test_index_all_with_regexp assert_equal( [0,4,8], "a123a567a9".index_all(/a/) ) end but doesn't take into account the other 2 classes of search terms that a user might want to pass in (at least if you're trying to offer behavior analogous to that of String#index, http://corelib.rubyonrails.org/classes/String.html#M001505, which I hope you are)... I've added a new test case for strings: def test_index_all_with_string assert_equal( [0,4,8], "a123a567a9".index_all('a') ) end ... which fails unless the implementation is changed. NoMethodError: undefined method `length' for nil:NilClass Changing this line: i += (reuse ? 1 : $~.length ) to this seems to have fixed the problem: i += (reuse ? $~.length : 1 ) I really don't understand the reuse flag (could you add some comments explaining how it works and (to the docs) why I'd ever want to use that flag (performance boost??), but it looked like you just had the order mixed up on that one line :) Would suggest a test such as this be added that actually exercises that behavior: def test_index_all_with_regexp_reuse assert_equal( [0,4,8], "a123a567a9".index_all(/a/, true) ) end I tried to see what was going on by adding this line: puts "Incrementing by #{ (reuse ? $~.length : 1 ) }" but it printed "1" for all of 3 tests, indicating that the reuse flag didn't make one iota of difference... Question: Should it only use $~ if s is a Regexp? (Should it *automatically* use it if $~ is a Regexp?) $~ appears to not even be set if s is a string; only if it's a Regexp, so we probably shouldn't let people shoot their foot off by calling index_all('a', true)... Haven't even looked at the Fixnum===s case. class String # Like index but returns an array of all index locations. # The reuse flag allows the trailing portion of a match to be # reused for subsquent matches. # # "abcabcabc".index_all('a') #=> [0,3,6] # def index_all(s, reuse=false) ia = []; i = 0 while (i = self.index(s,i)) ia << i i += (reuse ? $~.length : 1 ) end ia end end By the way, thanks for this method. I was just about to create my own String#nth_index(s, 2) method, but then saw this and decided I could just use index_all(s)[2] instead... ---------------------------------------------------------------------- Comment By: Trans Onoma (transami) Date: 2007-07-26 04:20 Message: Thanks for the bug report. I added s = Regexp.new(Regexp.escape(s)) unless Regexp===s To take into account the string case. As for the reuse flag. Turns out the real problem wasn't the order but '$~.size'. The fix is: i += (reuse ? 1 : $~[0].size) The reuse flag increments the search offset by one, without it the search index is incremented by the size of the last match. The difference is: "bbb".index_all('bb') => [0] "bbb".index_all('bb',true) => [0,1] Should be all good now for next release (Facets 2.0). ---------------------------------------------------------------------- You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3169&aid=10372&group_id=804 From noreply at rubyforge.org Thu Jul 26 05:41:50 2007 From: noreply at rubyforge.org (noreply at rubyforge.org) Date: Thu, 26 Jul 2007 05:41:50 -0400 (EDT) Subject: [Facets] [ facets-Bugs-9466 ] String margin error Message-ID: <20070726094150.53C415240A3D@rubyforge.org> Bugs item #9466, was opened at 2007-03-22 07:50 You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3169&aid=9466&group_id=804 Category: None Group: None >Status: Closed >Resolution: Accepted Priority: 3 Submitted By: Trans Onoma (transami) Assigned to: Nobody (None) Summary: String margin error Initial Comment: I have been getting errors from Facets when I use the margin facility: brains hgs 52 %> ./class_generator.rb /usr/local/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/string/margin.rb:16:in `margin': undefined method `[]' for nil:NilClass (NoMethodError) from ./class_generator.rb:353:in `process_arg' from ./class_generator.rb:309:in `each_pair' from ./class_generator.rb:309:in `process_arg' from ./class_generator.rb:191:in `initialize' from ./class_generator.rb:132:in `open' from ./class_generator.rb:132:in `initialize' from ./class_generator.rb:2079:in `new' from ./class_generator.rb:2079 brains hgs 53 %> cat -n /usr/local/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/string/margin.rb | body 10 20 10 # }.margin 11 # 12 #-- 13 # This may still need a bit of tweaking. 14 #++ 15 def margin(n=0) 16 d = /\A.*\n\s*(.)/.match( self )[1] 17 d = /\A\s*(.)/.match( self)[1] unless d 18 return '' unless d 19 if n == 0 20 gsub(/\n\s*\Z/,'').gsub(/^\s*[#{d}]/, '') brains hgs 54 %> It seems to me that match is only ever used if it will fail (it is a test). Line 17 expects possible failure of line 16 ("unless d"). When match fails, it returns nil. ----------------------------------------------------------- Regexp#match rxp.match(str) => matchdata or nil ------------------------------------------------------------------------ Returns a +MatchData+ object describing the match, or +nil+ if there was no match. This is equivalent to retrieving the value of the special variable +$~+ following a normal match. /(.)(.)(.)/.match("abc")[2] #=> "b" Nil does not have the [] method, so arguably the example in the docs is buggy as well. I suggest the following change in the patch below. However, I have a problem. Running setup will attempt a full install. Running setup.rb config setup.rb setup setup.rb test tells me that it has run 0 tests, with 0 failures and 0 errors. How do I get tests to run to be sure that this change doesn't break things before I install? --Hugh --- facets-1.8.54/lib/facets/core/string/margin.rb.orig 2007-02-20 15:39:38.000000000 +0000 +++ facets-1.8.54/lib/facets/core/string/margin.rb 2007-03-22 11:37:53.927850000 +0000 @@ -13,8 +13,8 @@ # This may still need a bit of tweaking. #++ def margin(n=0) - d = /\A.*\n\s*(.)/.match( self )[1] - d = /\A\s*(.)/.match( self)[1] unless d + d = ((/\A.*\n\s*(.)/.match( self )) || + (/\A\s*(.)/.match( self)))[1] return '' unless d if n == 0 gsub(/\n\s*\Z/,'').gsub(/^\s*[#{d}]/, '') ---------------------------------------------------------------------- >Comment By: Trans Onoma (transami) Date: 2007-07-26 04:41 Message: Thanks for the report. I made your changes and tested. It looks good. It would be good to have a test case for what was creating this error, though. Do you have a simple example? As for testing. The basic way it to remark the test block, putting a # in front of =being and =end like so: #=begin test ... #=end Then just run the script. It has no other dependencies so no worries about the load_path. I actually use a tool that does this automatically for me. 'gem install 'exacto' and then: $ exrb margin.rb Also, I have a script that extracts all embedded tests and adds them to test/. (I haven't released that yet though.) I should have shipped those tests with the package. I'll fix that for the next release. ---------------------------------------------------------------------- You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3169&aid=9466&group_id=804 From noreply at rubyforge.org Thu Jul 26 09:21:00 2007 From: noreply at rubyforge.org (noreply at rubyforge.org) Date: Thu, 26 Jul 2007 09:21:00 -0400 (EDT) Subject: [Facets] [ facets-Bugs-9988 ] Singular function fails many tests Message-ID: <20070726132100.DB3DC5240BAF@rubyforge.org> Bugs item #9988, was opened at 2007-04-09 08:19 You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3169&aid=9988&group_id=804 Category: None Group: None Status: Open >Resolution: Postponed Priority: 3 Submitted By: Nobody (None) Assigned to: Nobody (None) Summary: Singular function fails many tests Initial Comment: I ran some additional tests on the Singular function and it failed 10 out of the following 68 tests: tests["Americans"] = "American" tests["analyses"] = "analysis" tests["bays"] = "bay" tests["bureaus"] = "bureau" tests["businesses"] = "business" tests["cacti"] = "cactus" tests["cactuses"] = "cactus" tests["calves"] = "calf" tests["carpets"] = "carpet" tests["chiefs"] = "chief" tests["choruses"] = "chorus" tests["churches"] = "church" tests["companies"] = "company" tests["courses"] = "course" tests["cows"] = "cow" tests["crashes"] = "crash" tests["criteria"] = "criterion" tests["discos"] = "disco" tests["doors"] = "door" tests["factories"] = "factory" tests["farms"] = "farm" tests["farmers"] = "farmer" tests["faxes"] = "fax" tests["firemen"] = "fireman" tests["fish"] = "fish" tests["flowers"] = "flower" tests["forks"] = "fork" tests["foxes"] = "fox" tests["friends"] = "friend" tests["garages"] = "garage" tests["gardens"] = "garden" tests["geese"] = "goose" tests["grown-ups"] = "grown-up" tests["heroes"] = "hero" tests["highways"] = "highway" tests["horses"] = "horse" tests["hovercraft"] = "hovercraft" tests["indexes"] = "index" tests["indices"] = "index" tests["kisses"] = "kiss" tests["lives"] = "life" tests["lights"] = "light" tests["loaves"] = "loaf" tests["memos"] = "memo" tests["mountains"] = "mountain" tests["mice"] = "mouse" tests["ovens"] = "oven" tests["parties"] = "party" tests["pens"] = "pen" tests["pennies"] = "penny" tests["potatoes"] = "potato" tests["prizes"] = "prize" tests["proofs"] = "proof" tests["scarves"] = "scarf" tests["series"] = "series" tests["staples"] = "staple" tests["statuses"] = "status" tests["stores"] = "store" tests["Swiss"] = "Swiss" tests["tables"] = "table" tests["take-offs"] = "take-off" tests["teachers"] = "teacher" tests["theses"] = "thesis" tests["thieves"] = "thief" tests["tomatoes"] = "tomato" tests["torpedoes"] = "torpedo" tests["videos"] = "video" tests["watches"] = "watch" ---------------------------------------------------------------------- >Comment By: Trans Onoma (transami) Date: 2007-07-26 08:21 Message: Nice work! Thanks, for catching these. We'll work on this soon. But BIG NEWS here too. inflect.rb is one of libraries being spun-off from Facets. It will now be found in the English project. (http://english.rubyforge.org). The intent is that we can better develop a solid, strong english grammer and general "writ-processing" library by having it separate. I'll keep the community posted when this get's it's first release. If anyone is interested in becoming a main developer on that project, please let me know. --trans ---------------------------------------------------------------------- You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3169&aid=9988&group_id=804 From noreply at rubyforge.org Thu Jul 26 09:37:02 2007 From: noreply at rubyforge.org (noreply at rubyforge.org) Date: Thu, 26 Jul 2007 09:37:02 -0400 (EDT) Subject: [Facets] [ facets-Bugs-11544 ] FileUtils.compress('zip', 'filepath', 'filepath.zip') got error when filepath.zip doedn't exist Message-ID: <20070726133702.E95735240BF2@rubyforge.org> Bugs item #11544, was opened at 2007-06-13 04:37 You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3169&aid=11544&group_id=804 Category: None Group: None Status: Open Resolution: None Priority: 3 Submitted By: Bin Dong (dongbin) Assigned to: Nobody (None) Summary: FileUtils.compress('zip', 'filepath', 'filepath.zip') got error when filepath.zip doedn't exist Initial Comment: Found the reason: in the compress.rb cmd = "zip -cf #{to_file} #{folder}" should be replace with cmd = "zip #{to_file} #{folder}" The "-c" option of zip is different with tar. ---------------------------------------------------------------------- >Comment By: Trans Onoma (transami) Date: 2007-07-26 08:37 Message: Thanks. compress.rb is still a work in progress. For Facets 2.0 I renamed it ZipUtils. (I know it's not _all_ about Zip, but hey tissue paper isn't all Kleenex either ;) I'm actually not sure what that line should be. I'm thinking: cmd = "zip -ru #{to_file} #{folder}" recursive with updates. Look right? On the TODO list is to use Ruby's own zlib to create the Zip files (should be possible). Anyone want to take a crack at it? ---------------------------------------------------------------------- You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3169&aid=11544&group_id=804 From noreply at rubyforge.org Mon Jul 30 11:02:14 2007 From: noreply at rubyforge.org (noreply at rubyforge.org) Date: Mon, 30 Jul 2007 11:02:14 -0400 (EDT) Subject: [Facets] [ facets-Feature Requests-12691 ] Enumerable.dupes - return items that occur >1 Message-ID: <20070730150216.5CA145240AA4@rubyforge.org> Feature Requests item #12691, was opened at 2007-07-30 11:02 You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3172&aid=12691&group_id=804 Category: None Group: None Status: Open Priority: 3 Submitted By: Dan Bernier (danbernier) Assigned to: Nobody (None) Summary: Enumerable.dupes - return items that occur >1 Initial Comment: It'd be great if an Enumerable could return an Array of its duplicates. I've come across this need several times, and think it'd be a great addition to facets. There are three pretty good implementations at: http://snippets.dzone.com/posts/show/4148 http://snippets.dzone.com/posts/show/3838 Usage example (borrowed from the links above): arr = %w{foo bar baz bar baz qux foo zub} puts arr.dupes.inspect # => ["baz", "foo", "bar"] arr = [1,3,5,5,6,7,9,10,14,18,22,22,4,4,4,3,6] puts arr.dupes #=> [5, 22, 6, 3, 4] ---------------------------------------------------------------------- You can respond by visiting: http://rubyforge.org/tracker/?func=detail&atid=3172&aid=12691&group_id=804 From transfire at gmail.com Mon Jul 30 14:24:34 2007 From: transfire at gmail.com (TRANS) Date: Mon, 30 Jul 2007 11:24:34 -0700 Subject: [Facets] [ facets-Feature Requests-12691 ] Enumerable.dupes - return items that occur >1 In-Reply-To: <20070730150216.5CA145240AA4@rubyforge.org> References: <20070730150216.5CA145240AA4@rubyforge.org> Message-ID: <4b6f054f0707301124o6bcd95f7t2f569b159509dc6b@mail.gmail.com> On 7/30/07, noreply at rubyforge.org wrote: > Initial Comment: > It'd be great if an Enumerable could return an Array of its duplicates. I've come across this need several times, and think it'd be a great addition to facets. > > There are three pretty good implementations at: > http://snippets.dzone.com/posts/show/4148 > http://snippets.dzone.com/posts/show/3838 > > Usage example (borrowed from the links above): > arr = %w{foo bar baz bar baz qux foo zub} > puts arr.dupes.inspect # => ["baz", "foo", "bar"] > > arr = [1,3,5,5,6,7,9,10,14,18,22,22,4,4,4,3,6] > puts arr.dupes #=> [5, 22, 6, 3, 4] Hmm... I suppose I could add Enumerable#duplicates. There's already #nonuniq, but maybe that name isn't obvious. Also there if #collisions which does nearly same thing, but is less efficient b/c it can also take a block. T. From transfire at gmail.com Tue Jul 31 16:39:58 2007 From: transfire at gmail.com (Trans) Date: Tue, 31 Jul 2007 20:39:58 -0000 Subject: [Facets] Facets SVN Message-ID: <1185914398.541104.99240@r34g2000hsd.googlegroups.com> Hi-- I just imported Facets into the Rubyforge subversion repository. You should be able to get an anonymous checkout of the latest with: svn checkout svn://rubyforge.org/var/svn/facets/trunk facets/ Facets 2.0 is coming along nicely --still some things I'd like to get cleaned up, but a release (beta?) is Real Soon Now(tm). Btw, if you are interested in becoming a honest to goodness ruby spit- shining Facets developer, let me know. T.