Help convert a Perl user to the Ruby Way

Hi all.

  I've been working on some Ruby projects using the Rails framework and getting on well, however recently I've started on some projects using Ruby alone and have run into some issues as the task gets a little more complicated.

  One particular script is giving me trouble and I suspect it is due to my Perly ways. Below is a pastebin of the code. As some I'm sure can tell, it is an rbot plugin that right now doesn't do a whole lot.

http://pastebin.com/822611

  The highlighted line is flagging up the error:

TypeError: no implicit conversion from nil to integer

  on line 18 (highlighted).

  Now this is looking to me like a variable instantiation issue which brings us back to Perl. In Perl that line would be perfectly legal (though some would debate its elegance) and hashes and arrays would be created as and when required.

  I'm willing to accept that this may not be the best way to do things in Ruby, thus I come to you to ask for a quick rundown on best practices in these situations.

  I've played around with default procs and the like in the initialisation routine to no avail. There is an implicit dump somewhere which causes problems. What would be the best way to deal with this type of thing?

Many thanks,
Seb

Sebastian Reid wrote:

Hi all.

    I've been working on some Ruby projects using the Rails framework
and getting on well, however recently I've started on some projects
using Ruby alone and have run into some issues as the task gets a little
more complicated.

    One particular script is giving me trouble and I suspect it is due
to my Perly ways. Below is a pastebin of the code. As some I'm sure
can tell, it is an rbot plugin that right now doesn't do a whole lot.

http://pastebin.com/822611

    The highlighted line is flagging up the error:

TypeError: no implicit conversion from nil to integer

    on line 18 (highlighted).

    Now this is looking to me like a variable instantiation issue which
brings us back to Perl. In Perl that line would be perfectly legal
(though some would debate its elegance) and hashes and arrays would be
created as and when required.

In Ruby, only instance variables are autovivified to nil, and that by
itself is a behaviour that gets on my nerves (typo-prone).

From the code it looks @registry should be an object (maybe use a
Struct) instead of a Hash, and you should preinitialise its contents to
empty arrays and whatnot in its initialize method.

    I'm willing to accept that this may not be the best way to do things
in Ruby, thus I come to you to ask for a quick rundown on best practices
in these situations.

    I've played around with default procs and the like in the
initialisation routine to no avail.

Using blocks to initialize hashes / arrays is fine when not nested,
otherwise things get messy. Clean up your data structure initialisation
to be explicit?

David Vallner

Hi --

Hi all.

  I've been working on some Ruby projects using the Rails framework and getting on well, however recently I've started on some projects using Ruby alone and have run into some issues as the task gets a little more complicated.

  One particular script is giving me trouble and I suspect it is due to my Perly ways. Below is a pastebin of the code. As some I'm sure can tell, it is an rbot plugin that right now doesn't do a whole lot.

http://pastebin.com/822611

  The highlighted line is flagging up the error:

TypeError: no implicit conversion from nil to integer

  on line 18 (highlighted).

  Now this is looking to me like a variable instantiation issue which brings us back to Perl. In Perl that line would be perfectly legal (though some would debate its elegance) and hashes and arrays would be created as and when required.

In Ruby that won't work (as you've seen), specifically because is a
method. When you do:

   x["y"]

you're really doing:

   x.("y")

Arrays and hashes have a method, but so can any object; so the
presence of doesn't narrow down the field.

  I'm willing to accept that this may not be the best way to do things in Ruby, thus I come to you to ask for a quick rundown on best practices in these situations.

  I've played around with default procs and the like in the initialisation routine to no avail. There is an implicit dump somewhere which causes problems. What would be the best way to deal with this type of thing?

You'd probably be likely to see @registry initialized in the
initialize method, and then its elements initialized conditionally
along the way:

   (@registry["content"] ||= ).push(m.message)

You can certainly economize on this if you do:

   @registry = Hash.new {|h,k| h[k] = }

though this approach might be less slick as you get into deeper
nesting.

You might want to encapsulate the behavior in a class or module, and
even perhaps have a class representing @registry["context"].

David

···

On Mon, 13 Nov 2006, Sebastian Reid wrote:

--
                   David A. Black | dblack@rubypal.com
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

This shouldn't be an issue, if you turn on warnings:

$ ruby warning.rb
warning.rb:4: warning: instance variable @brand_new not initialized
nil
$ cat warning.rb
#!/usr/bin/env ruby -w

# use an unassigned instance variable
p @brand_new

__END__

James Edward Gray II

···

On Nov 12, 2006, at 11:18 AM, David Vallner wrote:

In Ruby, only instance variables are autovivified to nil, and that by
itself is a behaviour that gets on my nerves (typo-prone).

@registry unfortunately is not within my command, though yes I did consider using objects for its contents. I'd rather use simpler (though nested) structures first if I can.

The problem with being explicit when initialising the nested structures is that they're going to be pretty dynamic throughout, not to mention the structure itself being changed as I go along.

That said, how would I go about initialising a hash in a hash value that doesn't exist yet and won't until runtime? Unfortunately this is something I've gotten well used to not needing to know these days.

···

On 12 Nov 2006, at 17:18, David Vallner wrote:

Sebastian Reid wrote:

Hi all.

    I've been working on some Ruby projects using the Rails framework
and getting on well, however recently I've started on some projects
using Ruby alone and have run into some issues as the task gets a little
more complicated.

    One particular script is giving me trouble and I suspect it is due
to my Perly ways. Below is a pastebin of the code. As some I'm sure
can tell, it is an rbot plugin that right now doesn't do a whole lot.

http://pastebin.com/822611

    The highlighted line is flagging up the error:

TypeError: no implicit conversion from nil to integer

    on line 18 (highlighted).

    Now this is looking to me like a variable instantiation issue which
brings us back to Perl. In Perl that line would be perfectly legal
(though some would debate its elegance) and hashes and arrays would be
created as and when required.

In Ruby, only instance variables are autovivified to nil, and that by
itself is a behaviour that gets on my nerves (typo-prone).

From the code it looks @registry should be an object (maybe use a
Struct) instead of a Hash, and you should preinitialise its contents to
empty arrays and whatnot in its initialize method.

    I'm willing to accept that this may not be the best way to do things
in Ruby, thus I come to you to ask for a quick rundown on best practices
in these situations.

    I've played around with default procs and the like in the
initialisation routine to no avail.

Using blocks to initialize hashes / arrays is fine when not nested,
otherwise things get messy. Clean up your data structure initialisation
to be explicit?

David Vallner

Sebastian Reid wrote:
> Hi all.
>
> I've been working on some Ruby projects using the Rails framework
> and getting on well, however recently I've started on some projects
> using Ruby alone and have run into some issues as the task gets a little
> more complicated.
>
> One particular script is giving me trouble and I suspect it is due
> to my Perly ways. Below is a pastebin of the code. As some I'm sure
> can tell, it is an rbot plugin that right now doesn't do a whole lot.
>
> http://pastebin.com/822611
>
> The highlighted line is flagging up the error:
>
> TypeError: no implicit conversion from nil to integer
>
> on line 18 (highlighted).
>
> Now this is looking to me like a variable instantiation issue which
> brings us back to Perl. In Perl that line would be perfectly legal
> (though some would debate its elegance) and hashes and arrays would be
> created as and when required.
>

In Ruby, only instance variables are autovivified to nil, and that by
itself is a behaviour that gets on my nerves (typo-prone).

agreed but see JEGII remark about warnings

From the code it looks @registry should be an object (maybe use a

Struct) instead of a Hash, and you should preinitialise its contents to
empty arrays and whatnot in its initialize method.

501/2 > ruby -e 'puts Array.new[nil]'
-e:1:in `': no implicit conversion from nil to integer (TypeError)
        from -e:1
Given that I rather think that
(a) @registry["memory"] is referencing an Array object
and
(b) @registry["context"][1] evaluates to nil

That was the question OP, or was it not?
Now if you really want to shoot yourself into your leg here is the gun:

class GunArray < Array
       def *args, &block
            super(*args,&block) rescue nil end
       end
end

you could also monkeypatch Array but I am too lazy to look if its
method_alias or alias_method :wink:

> I'm willing to accept that this may not be the best way to do things
> in Ruby, thus I come to you to ask for a quick rundown on best practices
> in these situations.
>
> I've played around with default procs and the like in the
> initialisation routine to no avail.

Using blocks to initialize hashes / arrays is fine when not nested,
otherwise things get messy. Clean up your data structure initialisation
to be explicit?

That is definitely my advice too, but I am less strict :wink:

David Vallner

Cheers

Robert

···

On 11/12/06, David Vallner <david@vallner.net> wrote:

--
The reasonable man adapts himself to the world; the unreasonable one
persists in trying to adapt the world to himself. Therefore all progress
depends on the unreasonable man.

- George Bernard Shaw

Hi --

Hi all.

  I've been working on some Ruby projects using the Rails framework and getting on well, however recently I've started on some projects using Ruby alone and have run into some issues as the task gets a little more complicated.

  One particular script is giving me trouble and I suspect it is due to my Perly ways. Below is a pastebin of the code. As some I'm sure can tell, it is an rbot plugin that right now doesn't do a whole lot.

http://pastebin.com/822611

  The highlighted line is flagging up the error:

TypeError: no implicit conversion from nil to integer

  on line 18 (highlighted).

  Now this is looking to me like a variable instantiation issue which brings us back to Perl. In Perl that line would be perfectly legal (though some would debate its elegance) and hashes and arrays would be created as and when required.

In Ruby that won't work (as you've seen), specifically because is a
method. When you do:

  x["y"]

you're really doing:

  x.("y")

Arrays and hashes have a method, but so can any object; so the
presence of doesn't narrow down the field.

  I'm willing to accept that this may not be the best way to do things in Ruby, thus I come to you to ask for a quick rundown on best practices in these situations.

  I've played around with default procs and the like in the initialisation routine to no avail. There is an implicit dump somewhere which causes problems. What would be the best way to deal with this type of thing?

You'd probably be likely to see @registry initialized in the
initialize method, and then its elements initialized conditionally
along the way:

  (@registry["content"] ||= ).push(m.message)

You can certainly economize on this if you do:

  @registry = Hash.new {|h,k| h[k] = }

though this approach might be less slick as you get into deeper
nesting.

You might want to encapsulate the behavior in a class or module, and
even perhaps have a class representing @registry["context"].

David
  
  I think we actually tried @registry = Hash.new {|h,k| h[k] = } which was where the problem with the dump came in.

  Desperately trying not to turn this into a set of classes since it should be fairly simple otherwise, but I can see it drifting that way.

···

On 12 Nov 2006, at 17:37, dblack@wobblini.net wrote:

On Mon, 13 Nov 2006, Sebastian Reid wrote:

Sebastian Reid wrote:

@registry unfortunately is not within my command, though yes I did
consider using objects for its contents. I'd rather use simpler
(though nested) structures first if I can.

The problem with being explicit when initialising the nested
structures is that they're going to be pretty dynamic throughout, not
to mention the structure itself being changed as I go along.

That said, how would I go about initialising a hash in a hash value
that doesn't exist yet and won't until runtime?

Let's say we have an instance variable named "hash" that we are not sure has
been initialized at runtime, and we want to create a new hash where there
is none, but only then. Add this line:

hash ||= {}

Means "if 'hash' is a hash, do nothing, otherwise create a hash".

···

--
Paul Lutus
http://www.arachnoid.com

Strictly speaking, doesn't it mean "if 'hash' is nil (or false), assign to a newly created empty hash"? You're not testing whether it's actually a hash already. Also, maybe it's worth reminding the OP that an empty hash is not treated as 'false' in a logical expression in Ruby, as it would be in Perl. So, if you were to repeatedly evaluate

     hash ||= {}

you'd only create a new Hash the first time. In Perl, on the other hand, repeatedly evaluating the similar expression

     $hashref ||= {}

would create and assign a new hash repeatedly, until some hash element was assigned.

Tom

···

On Nov 12, 2006, at 2:40 PM, Paul Lutus wrote:

Let's say we have an instance variable named "hash" that we are not sure has
been initialized at runtime, and we want to create a new hash where there
is none, but only then. Add this line:

hash ||= {}

Means "if 'hash' is a hash, do nothing, otherwise create a hash".

Paul Lutus wrote:
[snip]

hash ||= {}

Means "if 'hash' is a hash, do nothing, otherwise create a hash".

To be precise, that is not what that means. It means:
  hash = hash || {}

which can be interpreted as:
  "If the 'hash' variable is nil or false, set it to a new Hash;
   otherwise, leave it alone (assigning it to itself)."

In practice it is used to mean what you say (if this variable isn't
already what I want it to be, set it to that thing), so your comment
largely stands as correct. I just don't want anyone coming to Ruby to
read that and think it's some special syntax that involves the object
on the right hand side in the decision of whether or not the assignment
should occur.

Well, in that case there's only one assignment indeed, because a hashref is true in Perl as well:

   $ perl -wle 'do {$h ||= {}; print $h} for 1..3'
   HASH(0x1800e8c)

-- fxn

···

On Nov 12, 2006, at 8:58 PM, Tom Pollard wrote:

On Nov 12, 2006, at 2:40 PM, Paul Lutus wrote:

Let's say we have an instance variable named "hash" that we are not sure has
been initialized at runtime, and we want to create a new hash where there
is none, but only then. Add this line:

hash ||= {}

Means "if 'hash' is a hash, do nothing, otherwise create a hash".

Strictly speaking, doesn't it mean "if 'hash' is nil (or false), assign to a newly created empty hash"? You're not testing whether it's actually a hash already. Also, maybe it's worth reminding the OP that an empty hash is not treated as 'false' in a logical expression in Ruby, as it would be in Perl. So, if you were to repeatedly evaluate

    hash ||= {}

you'd only create a new Hash the first time. In Perl, on the other hand, repeatedly evaluating the similar expression

    $hashref ||= {}

would create and assign a new hash repeatedly, until some hash element was assigned.

Tom Pollard wrote:

Add this line:

hash ||= {}

Means "if 'hash' is a hash, do nothing, otherwise create a hash".

Strictly speaking, doesn't it mean "if 'hash' is nil (or false),
assign to a newly created empty hash"?

Yes, and a good clarification. I was addressing the intent of the line, not
its true basis.

···

--
Paul Lutus
http://www.arachnoid.com

Good point. I confused the ref with the hash. I'll shut up again, now...

Tom

···

On Nov 12, 2006, at 3:22 PM, Xavier Noria wrote:

On Nov 12, 2006, at 8:58 PM, Tom Pollard wrote:

Strictly speaking, doesn't it mean "if 'hash' is nil (or false), assign to a newly created empty hash"? You're not testing whether it's actually a hash already. Also, maybe it's worth reminding the OP that an empty hash is not treated as 'false' in a logical expression in Ruby, as it would be in Perl. So, if you were to repeatedly evaluate

    hash ||= {}

you'd only create a new Hash the first time. In Perl, on the other hand, repeatedly evaluating the similar expression

    $hashref ||= {}

would create and assign a new hash repeatedly, until some hash element was assigned.

Well, in that case there's only one assignment indeed, because a hashref is true in Perl as well:

  $ perl -wle 'do {$h ||= {}; print $h} for 1..3'
  HASH(0x1800e8c)