Ruby Weekly News 2nd - 15th May 2005

http://www.rubyweeklynews.org/20050515.html

   Ruby Weekly News 2nd - 15th May 2005

···

------------------------------------

   Ruby Weekly News is (usually) a summary of the week's activity on the
   ruby-talk mailing list / the comp.lang.ruby newsgroup, brought to you by
   Tim Sutherland.

   This week it's a catchup double-dose - the last two weeks rolled into one.

Articles and Announcements
--------------------------

       Ruby Quiz RSS Feed
       ------------------

       James Edward Gray II announced an RSS feed for Ruby Quiz.

       Call for Papers: ACM Symposium on Dynamic Languages
       ---------------------------------------------------

       Curt Hibbs reminded us that the ACM were having a one-day symposium on
       dynamic languages, immediately after RubyConf 2005.

       gabriele renzi observed that "the program committee includes matz :)".

       Ruby Central, Inc. Donation and Pledge Site
       -------------------------------------------

       David A. Black (on behalf of Ruby Central, Inc.) announced the opening
       of the Ruby Central, Inc. Donation and Pledge Site.

       It allows you to make a general donation to Ruby Central, or "sponsor
       or partially sponsor a specific RubyConf 2005 item or activity (meals,
       t-shirts, and Matz's travel expenses)".

       New Presentation on Ruby and RoR available for download
       -------------------------------------------------------

       Curt Hibbs posted a link to a presentation given by Obie Fernandez
       from ThoughtWorks to the Agile Atlanta group. The topic was Ruby on
       Rails.

       http://ruby-co.de/: show the world your love for Ruby code
       ----------------------------------------------------------

       Jan `jast' Krueger announced http://ruby-co.de/, a free URL direction
       service "for the Ruby lovers out there". (For example,
       "http://foo.loves.ruby-co.de/".)

       NegaPosi Compiler
       -----------------
     
       YARV developer SASADA Koichi introduced a new
       language called "NegaPosi". (Part of) a sample program looks like
       "+-+--++--+--+++-+--+-++-+-+-+++-+---+++-+".

       "Yes, it's a only joke software :)"

Ruby User Groups
----------------

       unless baltimore.rb?
       --------------------

       Sam Goldman said "If anyone wants to set up a ruby meet-up in
       Baltimore I can get us a very nice conference room at my university
       and probably free pizza."

       Yahoo group for Austin (and) Texas Ruby users
       ---------------------------------------------

       Hal Fulton announced that the first meeting for the Austin and Texas
       group had been held. "Mexican food was consumed and plans for code
       were laid."

       The group will probably use the name "ARCTAN" - "Austin Ruby Coders'
       Texas Area Network".

       phoenix.rb
       ----------

       James Britt announced the next meeting of the Phoenix Ruby Users Group
       (on the 12th of May).

       "b.square unless b.there?"

       Central Ohio Ruby Users
       -----------------------

       Joe OBrien introduced a new group - the Columbus Ruby Users Group
       (Central Ohio in the U.S.).

       "Please, come one come all."

       Hamburg.rb meeting
       ------------------

       Stephan Kämper said that the Hamburb.rb group were having a meeting on
       May 4th.

       Any Rubyists in Oslo?
       ---------------------

       Chris Pine is moving to Oslo this year and was "wondering if there are
       any fans of Ruby up there?"

       Kent Dahl said that indeed there were, and suggested looking at
       NorwayRUG.

       List Invite - London Ruby Users Group
       -------------------------------------

       Robert McGovern announced a new list for the London Ruby Users Group.

Quote of the Week(s)
--------------------

   John Carter in "What sound does no duck make?",

  "Imagine a flock of ducks in the sky.
  Listen. Now remove the ducks. Listen.
  
  What is the sound of no duck quacking?

  What should nil respond_to?"

   Ryan Leavengood gave the best response, ruby-talk:141586.
   (If you're confused, see the "RCR 303..." thread below.)

Threads
-------

   Interesting threads included:

  another Tk question
  -------------------

   Joe Van Dyk asked, "In Tk, what's the best way to show a large table of
   data that gets constantly updated?"

   Hidetoshi NAGAI said that the TkTable extension can be used for this.
   Ruby/Tk support for it is included with Ruby.

  Object#inside_metaclass?
  ------------------------

   This was another thread considering the issue of what to name the obj in

class FrederickTheFrog
end

fred = FrederickTheFrog.new
obj = class << fred; self; end

   obj is a class that belongs only to fred. You can define methods in it
   that are only for fred - no other instances of FrederickTheFrog will have
   them. (This is the exact reason such a concept exists.)

   The usual syntax for adding such a method is as follows,

fred = FrederickTheFrog.new
class << fred
   def hoppityhop
     puts("Ribburrt")
   end
end

   It's as though when you create a new instance, a new class is created that
   sits between the instance and the original class.

   (For efficiency reasons, the Ruby interpreter doesn't really create the
   class unless you actually need it, but that's just an implementation
   detail. See rb_singleton_class in class.c for details.)

class JustForFred < FrederickTheFrog
end

fred = JustForFred.new

   (For whatever reason, in Ruby methods are defined for classes, not for
   objects. However, since each object has its own personal hidden class, it
   doesn't matter.)

   Matz calls obj a "singleton class", but some people call by names such as
   "meta-class" and "idioclass".

   One argument against the term "singleton class" is that it is also used in
   some other language communities to mean a class which only ever has one
   instance. (For example, in the original book on Design Patterns.)

   Matz: "Interestingly, I started to use the term before the book was
   published in 1995. Sad coincidence."

   He added, "Until we find a better term. I don't think other terms proposed
   such as "exclusive class" are better. I assume Ruby users smart enough to
   deal with them by context until the time."

  Query about the top level object
  --------------------------------

   In a related thread, Gavri Fernandez asked about methods that are defined
   at the "top-level".

   He quoted the first edition of the Pickaxe,

     At the top level, we're executing code in the context of some predefined
     object. When we define methods, we're actually creating (private)
     singleton methods for this object.

   Gavri wasn't sure this was true, he'd read somewhere that they were
   defined as private methods of Object, and his testing appeared to show
   this was the case.

def whoa
   puts "in whoa"
end
Object.new.private_methods.include?('whoa') # true

   This means that every Ruby object then has a private method called whoa.
   An alternative is to define the method in the Kernel module.

module Kernel
   def whoa
     puts "in whoa"
   end
end

Object.new.private_methods.include?('whoa') # false

whoa() # prints "in whoa"

   There is a bit of a confusion, since if you look at self at the top-level,
   you see it is an instance of Object called main.

   (Warnng: Be careful about experimenting with this sort of thing in irb.
   Because of the way irb is implemented, visibility - public, private etc. -
   can be different to usual.)

   Christoph explained that "Defining a (private) method at the "top" scope
   is equivalent to defining a private method at the scope of the class
   Object."

  RCR 303: nil should accept missing methods and return nil
  ---------------------------------------------------------

   John Carter suggested changing nil to never raise NoMethodError. Instead,
   when a method of nil was called which otherwise did not exist, nil would
   be returned.

   "Not only does this simplify ruby programs considerably, it also changes
   certain crash bugs silently into correct programs."

   Matz replied with:

     "I know Objective-C's nil works like that. I once developed an OOP system
     (which was an early version of Ruby) where nil would respond to all
     undefined messages by doing nothing. In production code, it does nothing
     bad, since any production code should not raise an exception. Rather it
     introduces new scheme of error handling.

     But during development, it can hide bugs. It is very difficult to
     distinguish correct error handling by ignoring unknown message, and
     uncontrolled unintentional misbehavior caused by bugs. It's against the
     principle of "early death"."

   Regarding John's assertion that the proposed behaviour wouldn't hide bugs
   in real code, Austin Ziegler said that "I can tell you from experience
   over the last four days that I have fixed no fewer than five *real* bugs
   in PDF::Writer that would have resulted in the incorrect generation of PDF
   output had I done this."

   A number of others also thought the proposal was a bad idea.

   John started a new thread No Thing Here vs Uninitialized and RCR 303
   (under the nickname "Cyent"), which argued that nil is "overloaded too
   heavily" - we should distinguish between `uninitialised' and `nothing'.

   Christoph said this sounded like Javascript (ECMAScript), which has both
   null and undefined.

  AIX Status?
  -----------

   Jaime Fournier asked what the status of Ruby support on AIX was. There had
   recently been discussions of problems with the socket library on AIX.

   KUBO Takehiro said that the CVS version now works on AIX 4.3.3.

  Ruby/DL SendInput
  -----------------

   Peter C. Verhage wanted call SendInput, a win32 function from the
   user32.dll Windows library. (Roughly, he wants to use Ruby to call a
   particular C function.)

   "Unfortunately this method takes some complex arguments; several nested
   structs, including a union and a pointer to an array of a certain struct
   etc. How can I use Ruby/DL to call this method?"

   Takaaki Tateishi gave a link to sendinput.rb from his "DL Cookbook", which
   shows exactly how to do this. It uses DL2, the `next generation' version
   of DL.

   One interesting line in the code was

extern "UINT SendInput(UINT, LPINPUT, int)", :stdcall

   Note the second argument to extern, which indicates the calling convention
   that should be used. This matters on Windows platforms, where different
   functions often have different calling conventions, and is a new feature
   of DL2 over the original DL.

  Amazing Mazes (#31)
  -------------------

   Matt Linnell wrote Ruby Quiz number 31.

   "We've had crosswords, cryptograms, chess puzzles, madlibs ... so why
   don't we try our hand at mazes?"

   There were two parts; writing a program to generate a maze, and writing
   one to solve it.

  Cows and Bulls (#32)
  --------------------

   The following Ruby Quiz was created by Pat Eyler.

   "My kids like to play a variant of "Cows and Bulls" when we're driving.
   One of them (the server, to use a computing term-you'll see why in a
   minute) will think of a word (we usually play with either three or four
   letters), and the other (the client) will try to guess it. With each
   guess, the server will respond with the number of `cows' (letters in the
   word, but not in the right place) and bulls (letters in the correct place
   in the word)."

   Write a client and server to play this game.

  ruby-dev summary 26011-26089
  ----------------------------

   SASADA Koichi posted the latest English summary of the Japanese list
   ruby-dev.

   The CVS version of irb now has improved "save history" support, and mkmf
   supports C++.

  ruby-dev summary 26090-26127
  ----------------------------

   A symlink security problem was found with FileUtils.rm_rf.

  How to interface with an API written in C++?
  --------------------------------------------

   Derek Haskin asked how he could call C++ functions from Ruby.

   Nikolai Weibull posted a couple of links, the first of which was a link to
   the excellent ruby embedded into c++ article by Simon Strandgaard.

   It demonstrates how to use C++ classes from Ruby, and vice versa, both
   with and without SWIG.

   Piers Harding said that you could always use extern "C" around the C++
   code to provide a C interface which Ruby would then use.

   Gennady Bystritksy recently started using SWIG and was very impressed.

   "It transforms your C++ classes into Ruby classes practically seamlessly,
   even giving you opportunity to adjust to naming conventions simply by
   edditing a SWIG interface file. Included typemaps allow you, for example,
   return std::string from your C++ method, aautomatically gets converted and
   returned to the Ruby world as Ruby string, without you doing a stir. Isn't
   it amazing? ;-)"

  Relax NG validator in Ruby?
  ---------------------------

   Miles Keaton asked if there was a Relax NG validator for Ruby. The only
   library he could find was for Java.

   ("Relax NG" is an XML schema language, defining elements such as list and
   define.)

   James Britt said that REXML has some experimental support for this.

  Net::HTTP::Proxy using one or more proxies
  ------------------------------------------

   Botp Peña wondered, "is possible to use multiple proxies in
   Net::HTTP::Proxy?"

   Minero Aoki: "In a word, No. You must use multiple Net::HTTP objects
   explicitly."

  rbtree in the stdlib
  --------------------

   Martin DeMello proposed in RCR-306 that rbtree be included in the Ruby
   standard library, and also that PriorityQueue and other such classes be
   added.

   rbtree is a library providing a Red/Black Tree implementation for Ruby.
   There are many algorithms where a balanced tree structure is useful, for
   example where you need a Set of Arrays. Computing a hash in that case may
   be a relatively slow operation.

  Rubyist formerlly known as Newbie
  ---------------------------------

   Jason Ashbaugh posted a "Thank you" to Chris Pine for his Learn to Program
   tutorial that teaches programming using Ruby.

   "I started learning Ruby about 2 years ago (something came up early on,
   and I had to drop most of my "learning" activities). Chris' introduction
   to programmning was much shorter then :). But About two months ago I
   decided I had time to finally learn to program, and I knew Ruby was still
   the language I wanted to learn, so I google for "Ruby Pine" (I still
   remembered his last name it made such an impression) and I printed out
   your webpages.

   After two months of toting them back and forth to work on a clipboard I
   finally feel like I understand enough programming to start learning :)"

  Redesign 2005, Round Two
  ------------------------

   With a paraffin banjo on his knee, why the lucky stiff presented Round Two
   from the ruby-lang.org website redesign team.

   There were many favourable comments on the design, plus discussion on the
   logo and slogan used. A sexless duck stole the fish's bicycle.

  Rendering text with OpenGL
  --------------------------

   Ilmari Heikkinen wanted to render nice-looking text onto an OpenGL
   texture. "By nice-looking I mean variable width anti-aliased truetype
   fonts with different font sizes, hinting, line heights and all the usual
   font rendering lib niceties."

   George Ogata said that GLUT has simple text-rendering, or you can use
   libfreetype to render to a 2D image buffer. (Both GLUT and libfreetype
   have Ruby bindings.)

   Alternatively, there was the FTGL library. "It has lazy-loaded,
   texture-based fonts for fast rendering, and extruded geometry fonts for
   when you want render text as a solid object from different angles."
   Unfortunately, it doesn't (yet) have Ruby bindings.

   Bill Kelly thought that FTGL sounded great, and plans to write bindings
   for it in the next month or two.

   Ilmari himself went for rcairo, a Ruby interface to the Cairo vector
   graphics library.

  ruby vs. java?
  --------------

   Franz Hartmann, a physics student from Berlin, asked about using Ruby to
   do physical model calculations.

   The appropriateness of Ruby or other languages for this task was debated.
   Michael Ulm suggested GNU Octave, a language designed for numerical
   computations.

   Fortran, Mathematica and others were also mentioned.

   "maybe its the inbreeding but i am confused" was uttered.

  We need a comprehensive test suite
  ----------------------------------

   Daniel Berger exclaimed "Matz's announcement of 1.8.3 beta 1 reminds me of
   something. We need a test suite. A large, comprehensive test suite.
   Written using test-unit. And we need it NOW."

   Eric Hodel said that it should be based on Rubicon, a test suite
   originally written by Dave and Andy of PragmaticProgrammer fame. It needs
   some work to be updated to Ruby 1.8, but has "75% of the work done for you
   already, has nice reports, and has many of the platform/version
   differences spelled out."

   Chad Fowler recalled that Matz had agreed in the past to the idea of
   including Rubicon with Ruby. It just requires someone with enough time and
   motivation to add it to the CVS tree in a clean way.

  String Hashing Algorithms
  -------------------------

   String hashing algorithms were discussed. Phrogz benchmarked some
   algorithms, and Zed A. Shaw gave alternatives to hashing.

  Ruby on Windows CE?
  -------------------

   Volker Voigt asked if Ruby could run under Windows CE.

   nobu replied - "Yes, see wince/README.wince" in the source for
   information.

New Releases
------------

       Updateable 0.0.3
       ----------------

       John W. Long made the first public release of a small module that
       allows you to update the attributes of an object from a Hash.

       RJournal 0.1.1
       --------------

       Vincent Foley released his first open source project, RJournal, a
       simple LiveJournal client. He plans to add a Fox GUI to it in the
       future.

       HighLine 0.3.0-Now with ANSI colors!, HighLine 0.4.0, HighLine 0.5.0
       --------------------------------------------------------------------

       James Edward Gray II released new versions of HighLine, a library to
       make it easy to write an application that takes input from the
       console.

       0.3 added support for colours in the output, whitespace handlng,
       improved type conversions, and single-character input.

       0.4 added word wrapping and "paged printing" output.

       0.5 brought an "echo setting (for passwords)", confirmation questions
       and case-sensitivity settings.

       Ruby Editor Plugin for jEdit 0.6.1 - method completion release II
       -----------------------------------------------------------------

       Rob released a new version of the Ruby Editor Plugin for jEdit. The UI
       for method completion was improved. An integrated Ruby documentation
       viewer was also added.

       He later asked for feedback on how users feel about the method
       completion.

       There was discussion in the thread announcing 0.6.0.

       Subsequently, "Ruby Editor Plugin for jEdit - release 0.6.5" was
       posted, adding class hierachy information to the documentation viewer.

       An extension to Rational - friendlier with Floats and Strings
       -------------------------------------------------------------

       Dave recalled a recent thread about converting a Float to a Rational,
       for example 1.5 to 3/2.

       He took a look at how Python handled this, and wrote some new methods
       for Ruby in a similar spirit.

       For example, 1.5.to_r returns Rational(3, 2). Other methods added
       include Rational#approximate, to simplify a Rational, to +/- some
       error.

       MuraveyWeb 0.2.1-Ruby on Rails CMS, MuraveyWeb 0.2.2-Emergency Release
       ----------------------------------------------------------------------

       Dmitry V. Sabanin put out a bugfix release for the MuraveyWeb content
       management system. He quickly made an "Emergency Release" of 0.2.2 to
       fix a bug in 0.2.1 that made SimpleFolders unusable.

       Transaction::Simple 1.3.0
       -------------------------

       Austin Ziegler announced the latest release of Transaction::Simple, a
       library allowing you to easily use in-memory transactions. "This is to
       allow "test" changes to be made to an object before making the changes
       permanent."

       This version adds "transaction groups", allowing you to have a single
       transaction for multiple objects.

       John Lam said "Wow wow wow! This is so massively cool."

       Classifier 1.3.0
       ----------------

       Lucas Carlson announced that Classifier is now available as a
       pure-Ruby library, as well as a version that uses the GSL library.

       A new method String#summary was also added. This uses Classifier to
       automatically summarise a block of text by guessing what the most
       important sentences are.

       color-tools 1.1.0
       -----------------

       Austin Ziegler updated his color-tools package. This library is used
       to manipulate RGB and CMYK colours.

       It can now read GIMP colour palette definition files.

       Pages-BibTeX
       ------------

       Tom Counsell announced a script to help users of iWord on MacOS X. It
       fixes bibliographies in the Apple Pages word-processor.

       Pimki 1.7
       ---------

       Assaph Mehr announced Pimki 1.7.092, which he hopes will be the last
       release before Pimki2.

       Bugs were fixed and features added.

       RubyInline 3.3.1 Released
       -------------------------

       Ryan Davis fixed some bugs in RubyInline, a library that makes it easy
       to embed C and C++ code within a Ruby source file.

       traits-0.0.0, traits-0.1.0
       --------------------------

       Ara.T.Howard announced the first version of traits (really, just the
       renaming of attributes.rb). It provides "a better set of attr_*
       methods".

       trails-0.1.0 was also released.

       Packgen 0.1
       -----------

       Ghislain Mary released Packgen, a "simple network packet generator
       handling diffserv markers, useful for testing network bandwidth and
       QoS."

       KirbyBase 2.2
       -------------

       Jamey Cribbs made some major internal changes to KirbyBase, a simple
       pure-Ruby database management system that stores its data in
       plain-text files.

       The design of the code is now much cleaner. Some functional changes
       were also made.

       Rant 0.3.6
       ----------

       Stefan Lang improved Rant, a "flexible build tool written entirely in
       Ruby, similar to Rake".

       Generated files are now automatically cleaned up, and "constraining
       variables" and "directed rules" were added.

       Ruby-GetText-Package-0.9.0
       --------------------------

       Masao Mutoh's Ruby-GetText-Package was updated. It is a Native
       Language Support Library Tool.

       CGI and ERB are now supported.

       Ri18n 0.0.3 Ruby application internationalization library
       ---------------------------------------------------------

       In a related release, dm1 laid out Ri18n-0.0.3, a library whose goals
       are similar to Ruby-GetText-Package - to help internationalisation of
       Ruby programs.

       Ruby/Odeum 0.3.1 Pre-Release
       ----------------------------

       Zed A. Shaw announced a pre-release of Ruby/Odeum, a binding to the
       QDBM Odeum inverted index library. (Useful for implementing a search
       engine.)

       The major new addition is a boolean expression query language,
       allowing you to search for e.g. "Zed & Shaw ! (Frank Blank)".
       Performance has also been improved in some circumstances.

       session-2.4.0
       -------------
       
       Ara.T.Howard added the ability to specify stdin for
       Session::Bash and Session::Sh.

       Session is a set of classes for "driving external progams via pipes."

       sldb-0.0.0, sldb-0.1.0
       ----------------------

       Ara.T.Howard announced "sldb is a multi-thread,
       multi-process, and nfs safe abstraction of sqlite databases." It deals
       with locking and retrying transactions so you don't have to.

       He had earlier introduced sldb-0.0.0.

       webgen 0.3.4
       ------------

       Thomas Leitner improved webgen, a tool to generate web pages from page
       description and template files.

       ERB can now be used, file handling has been enhanced, bugs fixed and
       other features added.

       Webstar Tools 0.5.0 Released!
       -----------------------------

       Zach Dennis posted the latest iteration of his set of command-line
       utilities for the WebSTAR V Internet Server on MacOS X.

       Production Log Analyzer 1.2.0
       -----------------------------

       Eric Hodel released a new version of his Production Log Analyzer, used
       to determine which pages on a dynamic website are the slow ones.

       This version includes a new time summary, plus support for sending
       email with sendmail.

I've got some rails code that is failing in a very strange way. It is
quite repeatable in situ, but I have yet to formulate a simple
explanation of when it fails.

Consider this code:

    case x
       when Array
          print "Array\n"
       else
          print "Bogus\n" if x.is_a? Array
       end

Should it ever print "Bogus\n"? I wouldn't think so, but when x is the
Array returned by main_record.sub_records (where main_record is linked
to sub_records by a "has_many :sub_record") it does.

The class of x is "Array" and (obviously) "x.is_a? Array" but "Array ===
x" returns false. It works for hand made arrays, the Array returned by
"SubRecord.find_all_by_main_record(main_record.id)"--which should be
identical.

I've only found one place in the whole codebase (in activerecord) where
=== is redefined, and then only for active records themselves, not for
the class, and certainly not for Class. In any case, it "redefines" it
as "other.is_a?(self)", which is what I'd expect class to do.

Adding:

class Array
    def self.===(other)
        other.is_a? self
        end
    end

right above the code in question fixes it.

Has anyone else seen this? Did the semantics change when I wasn't
looking? Or is there a bug somewhere? Or am I missing something subtly
obvious?

--MarkusQ

Markus,

when x.has_many :y, the :y property is not an array. It's an object that walks and talks like an array. (Just try using x.y.find sometime--it doesn't work like the Array version, which bit me hard several times in the past.)

If you _need_ it to be an array, you can do x.y.to_a. Otherwise, it's all about duck typing. :slight_smile:

- Jamis

···

On May 23, 2005, at 2:04 PM, Markus wrote:

I've got some rails code that is failing in a very strange way. It is
quite repeatable in situ, but I have yet to formulate a simple
explanation of when it fails.

Consider this code:

    case x
       when Array
          print "Array\n"
       else
          print "Bogus\n" if x.is_a? Array
       end

Should it ever print "Bogus\n"? I wouldn't think so, but when x is the
Array returned by main_record.sub_records (where main_record is linked
to sub_records by a "has_many :sub_record") it does.

The class of x is "Array" and (obviously) "x.is_a? Array" but "Array ===
x" returns false. It works for hand made arrays, the Array returned by
"SubRecord.find_all_by_main_record(main_record.id)"--which should be
identical.

I've only found one place in the whole codebase (in activerecord) where
=== is redefined, and then only for active records themselves, not for
the class, and certainly not for Class. In any case, it "redefines" it
as "other.is_a?(self)", which is what I'd expect class to do.

Adding:

class Array
    def self.===(other)
        other.is_a? self
        end
    end

right above the code in question fixes it.

Has anyone else seen this? Did the semantics change when I wasn't
looking? Or is there a bug somewhere? Or am I missing something subtly
obvious?

--MarkusQ

Markus,

when x.has_many :y, the :y property is not an array. It's an object
that walks and talks like an array. (Just try using x.y.find
sometime--it doesn't work like the Array version, which bit me hard
several times in the past.)

I'd buy that except:

1) x.y.class.name == 'Array'

2) x.y.class == Array

3) x.y.class.ancestors == .class.ancestors

4) as I mentioned,

> Adding:
>
> class Array
> def self.===(other)
> other.is_a? self
> end
> end
>
> right above the code in question fixes it.

That's a little strong for duck typing, isn't it? I my book that's
somewhere past duct taping and well into bailing wire territory. I
admit is possible, but it seems unlikely...

--MarkusQ

···

On Mon, 2005-05-23 at 13:21, Jamis Buck wrote:

Ah ha! We're both right! It is a bug, and it does involve some of the
most magicalistic duck typing I've yet seen. Rails (or action*, or...)
changes the Array class! Observe:

Under regular irb:

(markus@lapdog) > irb
irb(main):001:0> .class.ancestors
=> [Array, Enumerable, Object, Kernel]

But under script/console:

(markus@lapdog) > script/console
Loading development environment.
irb(main):001:0> .class.ancestors
=> [Array, Enumerable, Object, Base64::Deprecated, Base64, Kernel]

So now the question is, how did the inserted Base64 goo break '===' for
Array?

--MarkusQ

···

On Mon, 2005-05-23 at 13:30, Markus wrote:

On Mon, 2005-05-23 at 13:21, Jamis Buck wrote:

> when x.has_many :y, the :y property is not an array. It's an object
> that walks and talks like an array. (Just try using x.y.find
> sometime--it doesn't work like the Array version, which bit me hard
> several times in the past.)

I'd buy that except:

1) x.y.class.name == 'Array'

2) x.y.class == Array

3) x.y.class.ancestors == .class.ancestors

Indeed, truth is stranger than fiction. :wink: Consider this snippet from AR's association_proxy.rb. (Here's where we're getting into some pretty black magic):

    alias_method :proxy_respond_to?, :respond_to?
    instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?|^proxy_respond_to\?|^send)/ }

    def method_missing(symbol, *args, &block)
      load_target
      @target.send(symbol, *args, &block)
    end

    def respond_to?(symbol, include_priv = false)
      proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
    end

It's proxying to an array, and undefining all the relevant methods on the proxy so that things like #class get passed to the proxy, too. Unfortunately, it looks like === doesn't get proxied, and I'm not sure why. Perhaps Ruby does some optimizing under the covers?

At any rate, yah. It can be annoying. But you really are dealing with a proxy, and not an array. At least, not directly.

- Jamis

···

On May 23, 2005, at 2:30 PM, Markus wrote:

On Mon, 2005-05-23 at 13:21, Jamis Buck wrote:

Markus,

when x.has_many :y, the :y property is not an array. It's an object
that walks and talks like an array. (Just try using x.y.find
sometime--it doesn't work like the Array version, which bit me hard
several times in the past.)

I'd buy that except:

1) x.y.class.name == 'Array'

2) x.y.class == Array

3) x.y.class.ancestors == .class.ancestors

4) as I mentioned,

Adding:

class Array
    def self.===(other)
        other.is_a? self
        end
    end

right above the code in question fixes it.

That's a little strong for duck typing, isn't it? I my book that's
somewhere past duct taping and well into bailing wire territory. I
admit is possible, but it seems unlikely...

IANARE (Rails Expert), but my guess it that whatever x.y is is
proxying for a wrapped array. One possilbe reason for this could be
delayed instantiation, etc. In that case we'd have something like:

  class ArrayProxy
    // somehow clean out *all* inherited methods to get a
    // clean proxy (this even cleans out .class, so be careful!
    def initialize( &block )
      @thunk = block
    end
    def contained_array
      @contained_array ||= @thunk.call
    end
    def method_missing( symbol, *args, &block=nil )
      contained_array( symbol, *args, &block )
    end
  end

So then we can have this code:

  a = ArrayProxy.new { [ :a, :b, :c ] }
  a.class # => Array
  a[0] # => :a
  a.is_a?( Array ) # => true

However, a is still an ArrayProxy, not an Array. Case equality, as you
pointed out, is an operator defined on the conditional, not the tested
value. So even though a.is_a?( Array ) by the proxy, Array === a is
false, since a is not, actually an Array.

So how's Array.=== implemented then? I don't know, but my guess is
that it's inherited from Class#=== without change, and that Class#===
is implemented in C and thus ignores the proxy in effect on a.

In short, you need to be *real* careful when using full out proxies
ala method_missing, because once the abstraction begins to leak,
things get real confusing. In my opinion cleaning out *everything*
(including .class, etc.) is not a good idea, but the decision was
obviously intentional. So even if we agree that this have .class get
passed through the proxy isn't desirable, it falls into the class of
"feature", not bug. :slight_smile:

Jacob Fugal

···

On 5/23/05, Markus <markus@reality.com> wrote:

On Mon, 2005-05-23 at 13:21, Jamis Buck wrote:
> Markus,
>
> when x.has_many :y, the :y property is not an array. It's an object
> that walks and talks like an array. (Just try using x.y.find
> sometime--it doesn't work like the Array version, which bit me hard
> several times in the past.)

I'd buy that except:

1) x.y.class.name == 'Array'

2) x.y.class == Array

3) x.y.class.ancestors == .class.ancestors

4) as I mentioned,

> > Adding:
> >
> > class Array
> > def self.===(other)
> > other.is_a? self
> > end
> > end
> >
> > right above the code in question fixes it.

It's because the proxy is the argument to, not receiver of, ===. So
the proxy doesn't intercept ===. If the implementation of === turned
around and called something on the proxy (such as is_a? or class) the
proxy would come into effect and we'd be happy. But it appears the
implementation of Class#=== is low-level enough that it doesn't need
to call any methods on its argument, and the proxy isn't noticed.

Jacob Fugal

···

On 5/23/05, Jamis Buck <jamis@37signals.com> wrote:

It's proxying to an array, and undefining all the relevant methods on
the proxy so that things like #class get passed to the proxy, too.
Unfortunately, it looks like === doesn't get proxied, and I'm not
sure why. Perhaps Ruby does some optimizing under the covers?

So my conclusion:

This is a bug in the code that does the proxying, and either the
following snippet should be added to the proxy class:

class AssociationProxy
     def self.===(other)
        other.is_a? Array
        end
    end

or "class" should be removed from set of the proxied methods.

Otherwise, it breaks the semantics of a number of Ruby idioms for no
good reason--in other words, it's a bug, not a feature.

Any idea who owns this or who I should propagate it to? Jamis?

--MarkusQ

P.S. A little voice in the back of my head is muttering something about
Object#Hash and Object#object_id as well...

···

On Mon, 2005-05-23 at 13:46, Jamis Buck wrote:

>
>>> Adding:
>>>
>>> class Array
>>> def self.===(other)
>>> other.is_a? self
>>> end
>>> end
>>>
>>> right above the code in question fixes it.
>>>

It's proxying to an array, and undefining all the relevant methods on
the proxy so that things like #class get passed to the proxy, too.
Unfortunately, it looks like === doesn't get proxied, and I'm not
sure why. Perhaps Ruby does some optimizing under the covers?

Adding:

class Array
    def self.===(other)
        other.is_a? self
        end
    end

right above the code in question fixes it.

It's proxying to an array, and undefining all the relevant methods on
the proxy so that things like #class get passed to the proxy, too.
Unfortunately, it looks like === doesn't get proxied, and I'm not
sure why. Perhaps Ruby does some optimizing under the covers?

So my conclusion:

This is a bug in the code that does the proxying, and either the
following snippet should be added to the proxy class:

class AssociationProxy
     def self.===(other)
        other.is_a? Array
        end
    end

Except that you'd want to add that to Array, rather than the proxy class, because (as Jacob said), it is used like Array.===(instance) instead of the other way around.

or "class" should be removed from set of the proxied methods.

That's probably a good idea, but I still don't know if it would help. Also as Jacob said, === is implemented in C and probably bypasses the Ruby code, checking the class directly.

Otherwise, it breaks the semantics of a number of Ruby idioms for no
good reason--in other words, it's a bug, not a feature.

Any idea who owns this or who I should propagate it to? Jamis?

You can add a ticket to dev.rubyonrails.com. That way it won't get lost in a shuffle of email.

- Jamis

···

On May 23, 2005, at 4:24 PM, Markus wrote:

On Mon, 2005-05-23 at 13:46, Jamis Buck wrote:

Did that, but in the meantime I found a better solution (and included it
in the ticket):

        Change AssociationProxy to "class AssociationProxy < Array" and
        let the C code do its thing. No muss, no fuss...

--MarkusQ

···

On Mon, 2005-05-23 at 15:34, Jamis Buck wrote:

You can add a ticket to dev.rubyonrails.com. That way it won't get
lost in a shuffle of email.

The target of AssociationProxy is not always an instance of Array. For
example, for BelongsToAssociation it's a ActiveRecord::Base instance.

Kent.

···

On 5/23/05, Markus <markus@reality.com> wrote:

On Mon, 2005-05-23 at 15:34, Jamis Buck wrote:
> You can add a ticket to dev.rubyonrails.com. That way it won't get
> lost in a shuffle of email.

Did that, but in the meantime I found a better solution (and included it
in the ticket):

        Change AssociationProxy to "class AssociationProxy < Array" and
        let the C code do its thing. No muss, no fuss...

--MarkusQ

FileList in Rake used to inherit from Array to get array-like behavior. I
recently changed it to delegate to an internally held array. I found that
the delegation allowed a FileList to be used in more places where an Array
was expected ... mainly because some methods check for the to_ary method to
see if an object should be treated like an array.

Given the following code:

[FileList['a', 'b'], FileList['c', 'd']].flatten

Rake 0.5.0 which inherits from Array will return:
Rake 0.5.3 which delegates to Array will return: ['a', 'b', 'c', 'd']

That's because flatten accesses the array elements directly. Since FileList
is lazy loaded, bypassing the methods means that the elements are never
loaded. By switching to a delegation method, flatten checks for :to_ary,
which can be used to trigger loading and make everyone happy.

Short version: Inheriting from Array is not always the most compatible.

Another moral to the story: If you want to check for something that is array
like (but doesn't have to be an array), check to see if it responds to
to_ary.

···

On Monday 23 May 2005 11:55 pm, Markus wrote:

On Mon, 2005-05-23 at 15:34, Jamis Buck wrote:
> You can add a ticket to dev.rubyonrails.com. That way it won't get
> lost in a shuffle of email.

Did that, but in the meantime I found a better solution (and included it
in the ticket):

        Change AssociationProxy to "class AssociationProxy < Array" and
        let the C code do its thing. No muss, no fuss...

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

The target of AssociationProxy is not always an instance of Array. For
example, for BelongsToAssociation it's a ActiveRecord::Base instance.

Kent.

Good point. Descending it from Array shouldn't hurt for the other
cases, but it won't fix the problem for them either. I suppose we could
add === for all classes that might be proxied, by that seems needlessly
inefficient (it would pretty much bypass the C version of Class#===).

Is it always either an instance of Array or of ActiveRecord? If so,
basing AssociationProxy off Array and adding === to ActiveRecord should
fix it for both cases.

==MarkusQ

···

On Mon, 2005-05-23 at 21:08, Kent Sibilev wrote:

On 5/23/05, Markus <markus@reality.com> wrote:
> On Mon, 2005-05-23 at 15:34, Jamis Buck wrote:
> > You can add a ticket to dev.rubyonrails.com. That way it won't get
> > lost in a shuffle of email.
>
> Did that, but in the meantime I found a better solution (and included it
> in the ticket):
>
> Change AssociationProxy to "class AssociationProxy < Array" and
> let the C code do its thing. No muss, no fuss...
>
> --MarkusQ
>
>
>

FileList in Rake used to inherit from Array to get array-like behavior. I
recently changed it to delegate to an internally held array. I found that
the delegation allowed a FileList to be used in more places where an Array
was expected ... mainly because some methods check for the to_ary method to
see if an object should be treated like an array.

This is a much more thorough wrapping (see earlier on this thread). It
fools everything (x.class, x.is_a?, etc.) but does not fool Class#===,
which produces incorrect behaviour.

If you are going to produce something that walks like a duck, by all
means use a delegater; if you want something to _be_ a duck, you need to
make sure that Duck#=== accepts it as one.

Given the following code:

[FileList['a', 'b'], FileList['c', 'd']].flatten

Rake 0.5.0 which inherits from Array will return:
Rake 0.5.3 which delegates to Array will return: ['a', 'b', 'c', 'd']

That's because flatten accesses the array elements directly. Since FileList
is lazy loaded, bypassing the methods means that the elements are never
loaded. By switching to a delegation method, flatten checks for :to_ary,
which can be used to trigger loading and make everyone happy.

In this case. _all_ the methods are delegated, so it should work
correctly.

Short version: Inheriting from Array is not always the most compatible.

Another moral to the story: If you want to check for something that is array
like (but doesn't have to be an array), check to see if it responds to
to_ary.

Agreed. Here though, you could do all the checking you wanted and still
get caught out. If the _only_ way to distinguish a surrogate from it's
intended class is a bug in the impersonation, it is a bug. This is not
to say that they might have gotten away with something Array like, just
that if they are going to return true for x.is_a?(Array), and return
Array for x.class, then Array === x should return true as well.

--MarkusQ

···

On Mon, 2005-05-23 at 21:47, Jim Weirich wrote: