Ruby Weekly News 2nd - 15th May 2005

T

Tim Sutherland

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.
 
M

Markus

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
 
J

Jamis Buck

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. :)

- Jamis
 
M

Markus

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,

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
 
M

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

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
 
J

Jamis Buck

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,


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...

Indeed, truth is stranger than fiction. ;) 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
 
J

Jacob Fugal

=20
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.)
=20
I'd buy that except:
=20
1) x.y.class.name =3D=3D 'Array'
=20
2) x.y.class =3D=3D Array
=20
3) x.y.class.ancestors =3D=3D [].class.ancestors
=20
4) as I mentioned,
=20

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 =3D block
end
def contained_array
@contained_array ||=3D @thunk.call
end
def method_missing( symbol, *args, &block=3Dnil )
contained_array( symbol, *args, &block )
end
end

So then we can have this code:

a =3D ArrayProxy.new { [ :a, :b, :c ] }
a.class # =3D> Array
a[0] # =3D> :a
a.is_a?( Array ) # =3D> 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 =3D=3D=3D a is
false, since a is not, actually an Array.

So how's Array.=3D=3D=3D implemented then? I don't know, but my guess is
that it's inherited from Class#=3D=3D=3D without change, and that Class#=3D=
=3D=3D
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. :)

Jacob Fugal
 
J

Jacob Fugal

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 =3D=3D=3D doesn't get proxied, and I'm not
sure why. Perhaps Ruby does some optimizing under the covers?

It's because the proxy is the argument to, not receiver of, =3D=3D=3D. So
the proxy doesn't intercept =3D=3D=3D. If the implementation of =3D=3D=3D t=
urned
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#=3D=3D=3D is low-level enough that it doesn't need
to call any methods on its argument, and the proxy isn't noticed.

Jacob Fugal
 
M

Markus

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...
 
J

Jamis Buck

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
 
M

Markus

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
 
K

Kent Sibilev

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

Kent.
 
J

Jim Weirich

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...

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.
 
M

Markus

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
 
M

Markus

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top