[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

ruby programming best practice

Xiangrong Fang

11/1/2006 11:37:00 AM

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


17 Answers

Farrel Lifson

11/1/2006 12:39:00 PM

0

On 01/11/06, Shannon Fang <xrfang@hotmail.com> wrote:
> 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/n...

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

matt

11/1/2006 5:24:00 PM

0

Shannon Fang <xrfang@hotmail.com> wrote:

> 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.
--
matt neuburg, phd = matt@tidbits.com, http://www.tidbits...
Tiger - http://www.takecontrolbooks.com/tiger-custom...
AppleScript - http://www.amazon.com/gp/product/...
Read TidBITS! It's free and smart. http://www.t...

Erik Veenstra

11/1/2006 11:04:00 PM

0

> How do we write robust AND elegant code, and let our thinking
> smooth rather than considering exceptions all the time?
>
> array.pop.split(',')

(array.pop || '').split(',')

Just an example...

gegroet,
Erik V. - http://www.erikve...


Xiangrong Fang

11/2/2006 1:36:00 AM

0

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: matt@tidbits.com (matt neuburg)
>Reply-To: ruby-talk@ruby-lang.org
>To: ruby-talk@ruby-lang.org (ruby-talk ML)
>Subject: Re: ruby programming best practice
>Date: Thu, 2 Nov 2006 02:25:22 +0900
>
>Shannon Fang <xrfang@hotmail.com> wrote:
>
> > 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.
>--
>matt neuburg, phd = matt@tidbits.com, http://www.tidbits...
>Tiger - http://www.takecontrolbooks.com/tiger-custom...
>AppleScript - http://www.amazon.com/gp/product/...
>Read TidBITS! It's free and smart. http://www.t...
>

_________________________________________________________________
Download the new Windows Live Toolbar, including Desktop search!
http://toolbar.live.com/...


Paul Lutus

11/2/2006 2:20:00 AM

0

Shannon Fang wrote:

/ ...

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

Basically, Ruby, unlike compiled languages, in many cases won't detect an
undefined entity until it encounters it at runtime. So you have several
options. You can test your program before releasing it (seems the
professional thing to do), or you can write a validator that checks for
orphan variable names. Such a validator would be fairly easy to write --
tear the source apart, filter out all the Ruby reserved words, and list
those words that appear only once -- some will be defined but unused, some
will be used but not defined.

This won't help you with scoping issues, of course, unless you traverse the
program's scopes. That begins to be more complex.

The first approach (exhaustive testing) is preferred. I would never release
a program that had not had every line executed in typical conditions.

Also, Ruby isn't that different from compiled languages. As to the latter, 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, perhaps
years later.

--
Paul Lutus
http://www.ara...

Richard Conroy

11/2/2006 11:25:00 AM

0

On 11/1/06, Shannon Fang <xrfang@hotmail.com> wrote:
> 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.

David Vallner

11/2/2006 9:35:00 PM

0

Paul Lutus wrote:
> Also, Ruby isn't that different from compiled languages. As to the latter, 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, perhaps
> years later.
>

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

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

David Vallner

David Vallner

11/2/2006 10:28:00 PM

0

Richard Conroy wrote:
> On 11/1/06, Shannon Fang <xrfang@hotmail.com> wrote:
>> 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.
>

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

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" (= horrid hacks) to think of edge cases.

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

James Gray

11/2/2006 11:18:00 PM

0

On Nov 2, 2006, at 4:27 PM, David Vallner wrote:

> Richard Conroy wrote:
>> On 11/1/06, Shannon Fang <xrfang@hotmail.com> wrote:
>>> 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.
>>
>
> <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


David Vallner

11/3/2006 3:27:00 AM

0

James Edward Gray II wrote:
> On Nov 2, 2006, at 4:27 PM, David Vallner wrote:
>
>> Richard Conroy wrote:
>>> On 11/1/06, Shannon Fang <xrfang@hotmail.com> wrote:
>>>> 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.
>>>
>>
>> <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.
>

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