Help convert a Perl user to the Ruby Way.

S

Sebastian Reid

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
 
D

David Vallner

--------------enig38CC852C2FED546CA91A3E9B
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Sebastian said:
Hi all.
=20
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 littl= e
more complicated.
=20
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.
=20
http://pastebin.com/822611
=20
The highlighted line is flagging up the error:
=20
TypeError: no implicit conversion from nil to integer
=20
on line 18 (highlighted).
=20
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.
=20

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

=46rom 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.
=20
I'm willing to accept that this may not be the best way to do thing= s
in Ruby, thus I come to you to ask for a quick rundown on best practice= s
in these situations.
=20
I've played around with default procs and the like in the
initialisation routine to no avail. =20

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


--------------enig38CC852C2FED546CA91A3E9B
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFV1eAy6MhrS8astoRAl2JAJ0R3f8MDV/VCx2/OhT+1Mxag1vAcgCfRfDG
ZpGORMhRTE0zP3StHYb3oNU=
=PG3T
-----END PGP SIGNATURE-----

--------------enig38CC852C2FED546CA91A3E9B--
 
J

James Edward Gray II

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

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
 
D

dblack

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

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

Sebastian Reid

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

Sebastian Reid

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

Tom Pollard

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
 
X

Xavier Noria

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)
HASH(0x1800e8c)
HASH(0x1800e8c)

-- fxn
 
T

Tom Pollard

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)
HASH(0x1800e8c)
HASH(0x1800e8c)

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

Tom
 
P

Phrogz

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.
 

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

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,124
Latest member
JuniorPell
Top