ruby programming best practice

S

Shannon Fang

As a dynamic language, Ruby is much more flexible and easier than other
language I use, for example, Delphi. However, there is a "intrinsic" problem
in ruby programming -- a typical error I get again and again:

private method `split' called for nil:NilClass

The following statement:

array.pop.split(',')

will generate this error if array is empty. Of course I can use rescue
Exception statement, as a matter of fact, I do use that. Even after 4-5
years, some of my program still generate these kind of error occasionally.

Real-world situation is just too complicated. How do we write robust AND
elegant code, and let our thinking smooth rather than considering exceptions
all the time?

Experts, please tell us, your best practice, facing this "intrinsic" problem
of dynamic languages...

Thanks,
Shannon

_________________________________________________________________
Be the first to hear what's new at MSN - sign up to our free newsletters!
http://www.msn.co.uk/newsletters
 
F

Farrel Lifson

As a dynamic language, Ruby is much more flexible and easier than other
language I use, for example, Delphi. However, there is a "intrinsic" problem
in ruby programming -- a typical error I get again and again:

private method `split' called for nil:NilClass

The following statement:

array.pop.split(',')

will generate this error if array is empty. Of course I can use rescue
Exception statement, as a matter of fact, I do use that. Even after 4-5
years, some of my program still generate these kind of error occasionally.

Real-world situation is just too complicated. How do we write robust AND
elegant code, and let our thinking smooth rather than considering exceptions
all the time?

Experts, please tell us, your best practice, facing this "intrinsic" problem
of dynamic languages...

Thanks,
Shannon

_________________________________________________________________
Be the first to hear what's new at MSN - sign up to our free newsletters!
http://www.msn.co.uk/newsletters

There's a number of ways to handle this. If the array is possibly
empty you could check beforehand and return a default result
array.empty? ? ['','',''] : array.pop.split(',')
If the code that handles the split result can handle empty arrays then
you could just
array.pop.to_s.split(',')
or
String(array.pop).split(',')

Farrel
 
M

matt neuburg

Shannon Fang said:
As a dynamic language, Ruby is much more flexible and easier than other
language I use, for example, Delphi. However, there is a "intrinsic" problem
in ruby programming -- a typical error I get again and again:

private method `split' called for nil:NilClass

The following statement:

array.pop.split(',')

will generate this error if array is empty.

If my goal is that I just want skip the step if it can't be done, I like
this kind of thing:

array.pop.split(',') if array.last

If array itself could be nil then I have to add:

puts array.pop.split(',') if array and array.last

m.
 
S

Shannon Fang

My question is: is there a way to find out these spots in my program, like a
compiler do in Delphi/C.

e.g.:

def test(arr)
arr.each do |a|
...
end
end

in the above code, I will naturally assume arr is an array. Only after the
program is in use (may be even a year later!), I suddenly got a report of
error caused by "arr is nil".

In delphi, for example, such error is detected while compile, e.g. (a might
not have been initialized). I know Ruby works differently, but is there a
way (or a project) exists, like a normal compiler, or something like FxCop
for the C# language?

Thanks,
Shannon

From: (e-mail address removed) (matt neuburg)
Reply-To: (e-mail address removed)
To: (e-mail address removed) (ruby-talk ML)
Subject: Re: ruby programming best practice
Date: Thu, 2 Nov 2006 02:25:22 +0900



If my goal is that I just want skip the step if it can't be done, I like
this kind of thing:

array.pop.split(',') if array.last

If array itself could be nil then I have to add:

puts array.pop.split(',') if array and array.last

m.
--
matt neuburg, phd = (e-mail address removed), http://www.tidbits.com/matt/
Tiger - http://www.takecontrolbooks.com/tiger-customizing.html
AppleScript - http://www.amazon.com/gp/product/0596102119
Read TidBITS! It's free and smart. http://www.tidbits.com

_________________________________________________________________
Download the new Windows Live Toolbar, including Desktop search!
http://toolbar.live.com/?mkt=en-gb
 
R

Richard Conroy

Experts, please tell us, your best practice, facing this "intrinsic" problem
of dynamic languages...

Use Unit Tests.

With automated unit testing, the benefits of static typing, bounds checking,
null checking etc. start to disappear, as you typically write tests for these
scenarios anyway.

So dynamic languages with unit testing can do away with these features
and get better productivity bonuses, and more flexible coding styles
for no cost.

It does mean that you are committed to using unit tests though for any
significant work. In reality *all* significant projects really need
them, but you will miss them earlier in a dynamic language.
 
D

David Vallner

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

Paul said:
Also, Ruby isn't that different from compiled languages. As to the latt= er, a
null pointer will in many cases go unnoticed until it creates a runtime=
error when the code the pointer belongs to is executed, as you say, per= haps
years later.
=20

Unless you use one with compile-time null-safety checking. Defensive
compile-time DbC does loads for making your program's behaviour predictab=
le.

(Disclaimer: I am not an Eiffel weenie. Weird syntax, TOO_MANY_ALLCAPS,
etc.)

David Vallner


--------------enig2FC01D06C1E17DE46D4550A3
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)

iD8DBQFFSmSSy6MhrS8astoRAu37AJ4grfQIougH6vfLZ+bamigHJwwAsQCeJzD4
kzV5i/+WEz0a8wO5Jaqe9VE=
=gMyS
-----END PGP SIGNATURE-----

--------------enig2FC01D06C1E17DE46D4550A3--
 
D

David Vallner

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

Richard said:
=20
Use Unit Tests.
=20
With automated unit testing, the benefits of static typing, bounds
checking,
null checking etc. start to disappear, as you typically write tests for=
these
scenarios anyway.
=20

<rant> <!-- Hide the women and children. -->

The benefits disappear as you reimplement them in test cases anyway?
Insert comment about wheel reinvention here.

Also, compiler checks fail fast. That's a Good Thing (tm). Bounds checks
also fail faster than a NoMethodError elsewhere on the stack. Unit test
checks that noone thought of coding yet don't fail fast - having to
cover your whole code with checks for let's say public object contracts
isn't too gratifying work, and I wouldn't be surprised if a lot of
people "didn't get around to it". And believe it or not, knowing I
-have- these checks in a language compiler / runtime does mean I
wouldn't even dream of unit-testing for them. DRY.

The point is, to achieve robustness, amongst other things, trivial
errors must cause a failure, this must manifest itself as close to the
origin of the error as possible, and be detected immediately. Whether
using design-by-contract checks, IDE templates of common defensive
programming checks on method arguments / return values, or exhaustive
testing, special cases have to be detected and handled specially.
So dynamic languages with unit testing can do away with these features
and get better productivity bonuses, and more flexible coding styles
for no cost.
=20

Except you end up with a larger mass of unit test code. Which, make no
mistake, is code, can be buggy, and needs to be developed and
maintained, and if you want to avoid redundancy, also well-designed.

(Pop Quiz: how often do you test on array index bounds being valid? How
reusable is the test code? A Test::Unit extension library of reusable
pre / postcondition at method parameter and object state scope around
method calls would be interesting, if we're swapping compile-time
contract checks for unit-test-time ones.)

So, risking to reinforce my troll position, I'll postulate that the
productivity increase from dynamic typing is a delusion. From what I
have seen, a decently designed type hierarchy, and some degree of type
inference would do away with 90% of the trivial anecdotes demonstrating
time savings. In Ruby, I see more productivity gainers in
metaprogramming facilities, and the powerful object model - these aren't
at all exclusive with type / contract checking. As of yet I remain
unconvinced that extensive use duck typing and eval'ing public methods
into objects is more good than harm in the long term. I blame years of
doing code maintenance that taught me that most programmers (myself
included) think they're much smarter than they are and are way too
impressionable by "clever ideas" (=3D horrid hacks) to think of edge case=
s.

</rant>

(To wit: my last work project wrapped ORM in domain-specific facades,
complete with some rudimentary transaction management. Quick, easy to
mechanically churn out new facade code, no dependency on a declarative
TX mgmt. solution, read very nicely and was easy to use. Fast forward to
several junior developers, including me, being put to work on a new
feature, mostly guessing around from existing code. I was the first to
ask for some non-trivial code that was working with the model to be
reviewed by a senior, and got blinked at for doing what was supposed to
be an atomic change in multiple facade calls, i.e. multiple
transactions. Of course by that time the others had commited several
show-stopping bugs related to optimistic locking that created
unmodifiable zombie records in the database that required a manual edit
of the testing server's database to bring things back on track. And
thrashed the development DB of everyone else working on the module.
However, had the optimistic locking checks not been there, what was
essentially a potential invalid database change would have passed
silently and concurrent users would send the data integrity down the
proverbial creek with no paddle once out of testing because the
transaction-wrapping facades would hide the problem.)

David Vallner


--------------enigBF22C3B8BCB5AC4D486EA72B
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)

iD8DBQFFSnDKy6MhrS8astoRAkYmAJ0YOTz/3nbAKi8Gk2fX3+VxxnilcgCfdIrw
uL3S32OQLY+EXkUVS9xCGFw=
=Ll4Z
-----END PGP SIGNATURE-----

--------------enigBF22C3B8BCB5AC4D486EA72B--
 
J

James Edward Gray II

<rant> <!-- Hide the women and children. -->

The benefits disappear as you reimplement them in test cases anyway?
Insert comment about wheel reinvention here.

I quit reading right here.

It's *not* reimplementation!

You need to be testing your software anyway, even if you are using
the perfect language that protects you from all your errors. Since
you're doing that anyway, this is just a free side effect for your
good habits.

James Edward Gray II
 
D

David Vallner

--------------enigA9AAE955C5D04EE22DA9D078
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
<rant> <!-- Hide the women and children. -->

The benefits disappear as you reimplement them in test cases anyway?
Insert comment about wheel reinvention here.
=20
I quit reading right here.
=20
It's *not* reimplementation!
=20
You need to be testing your software anyway, even if you are using the
perfect language that protects you from all your errors. Since you're
doing that anyway, this is just a free side effect for your good habits= =2E
=20[/QUOTE]

That's what the original quote states though. "You typically write tests
for these scenarios anyway.", amongst those scenarios being some that
can be checked using static analysis in a suitably more predictable
language. If you're using a dynamic language, and covering checks for
those scenarios in unit tests, then you're reimplementing what a
contract-verifying compiler / runtime would catch in them, quite simply
because without such a compiler / runtime you don't have much choice if
you want your code to be robust along those axes. In and of themselves,
unit tests are (supposed to be) a complement to (substitute for the
absence of) language-provided unit contract checks - since they're
arbitrarily user-defined, they are to be used to check dynamic aspects
of those contracts that statical analysis (if there is any) can't.
Generally the unit testing methodology gets more awkward when you need
to test complex emergent behaviour that comes from the cooperation of
these units, and equating them to "testing your software" is just
misleading.

If you gain enough coverage for dynamic unit contracts, I'll admit it's
probably safe to assume that you have the more trivial checks that
statical analysis can do covered - in that case you can do away with
anything that would be reimplementing a compiler's checks. However, such
coverage takes nontrivial effort to create and maintain, and usually at
least I want to be able to reason on a code unit's correctness at least
to some extent while expending no / minimal effort on test code for the
"D'oh" type of error. For example, I'd very much like if I could tag a
variable to disallow assigning a nil into it and raise an exception when
that happens -immediately-, instead of getting a NME someplace else in
the code. I'd be hard to convince removing that check into a unit test
would be more concise than having language support for it.

David Vallner


--------------enigA9AAE955C5D04EE22DA9D078
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)

iD8DBQFFSrbiy6MhrS8astoRAmWBAJ4qOlBVHTs48ghvT/aNAL7n79kdZgCaA/j6
UQeo+B8nHWJwxCrYAh5Vr34=
=3BdU
-----END PGP SIGNATURE-----

--------------enigA9AAE955C5D04EE22DA9D078--
 
M

M. Edward (Ed) Borasky

David Vallner wrote:

[snip]

By the way, testing can only show the *presence* of defects, not their
absence.

:)
 
P

Phlip

M. Edward (Ed) Borasky said:
By the way, testing can only show the *presence* of defects, not their
absence.

:)

Is there _anyone_ here who didn't know that already??
 
M

M. Edward (Ed) Borasky

Phlip said:
Is there _anyone_ here who didn't know that already??

I don't really know. Invariably, someone will post a question about
tools, or the language, or strong typing, or compile-time checking, or
processes, or practices, or just about anything, and the answer will
come back, "testing is good". I know testing is good. But sometimes I
wonder if we aren't expecting too much from testing.
 
P

Phlip

M. Edward (Ed) Borasky said:
I don't really know. Invariably, someone will post a question about
tools, or the language, or strong typing, or compile-time checking, or
processes, or practices, or just about anything, and the answer will
come back, "testing is good". I know testing is good. But sometimes I
wonder if we aren't expecting too much from testing.

At this point in the discussion, those who advocate relentless testing often
point out they never use the Ruby debugger.

Maybe I should; I might learn the control flow thru some of those wicked
continuances in all these Ruby libraries. I certainly debug a lot, with
trace statements.

But advanced debugging certainly isn't a best practice. It can prove the
absense of bugs much worse than testing can! ;-)
 
R

Rafael Vieira

David said:
That's what the original quote states though. "You typically write tests
for these scenarios anyway.", amongst those scenarios being some that
can be checked using static analysis in a suitably more predictable
language. If you're using a dynamic language, and covering checks for
those scenarios in unit tests, then you're reimplementing what a
contract-verifying compiler / runtime would catch in them, quite simply
because without such a compiler / runtime you don't have much choice if
you want your code to be robust along those axes. In and of themselves,
unit tests are (supposed to be) a complement to (substitute for the
absence of) language-provided unit contract checks - since they're
arbitrarily user-defined, they are to be used to check dynamic aspects
of those contracts that statical analysis (if there is any) can't.
Generally the unit testing methodology gets more awkward when you need
to test complex emergent behaviour that comes from the cooperation of
these units, and equating them to "testing your software" is just
misleading.

If you gain enough coverage for dynamic unit contracts, I'll admit it's
probably safe to assume that you have the more trivial checks that
statical analysis can do covered - in that case you can do away with
anything that would be reimplementing a compiler's checks. However, such
coverage takes nontrivial effort to create and maintain, and usually at
least I want to be able to reason on a code unit's correctness at least
to some extent while expending no / minimal effort on test code for the
"D'oh" type of error. For example, I'd very much like if I could tag a
variable to disallow assigning a nil into it and raise an exception when
that happens -immediately-, instead of getting a NME someplace else in
the code. I'd be hard to convince removing that check into a unit test
would be more concise than having language support for it.

David Vallner
Hi David,

Im looking for Ruby Programmer to work in Bratislava/SK.
Please let me know if you are interested or if you know someone.

Thanks and sorry for any inconvenience.

Rafael Vieira - IM: (e-mail address removed)
 

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,770
Messages
2,569,584
Members
45,077
Latest member
SangMoor21

Latest Threads

Top