Forum: Ruby Is there a standard "assert" idiom in Ruby?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Kenneth McDonald (Guest)
on 2007-08-02 05:52
(Received via mailing list)
If you saw an earlier version of this question, apologies...I haven't
seen it, so assume I accidentally deleted it or it got lost or some
such.

In other languages, I'm accustomed to using "assert" as a sort of
enforceable documentation. Is there an equivalent statement or idiom in
Ruby? I find that simply raising exceptions isn't quite right;
exceptions are normally used when one thinks something might be done
about a problem, whereas asserts are used to check that something is the
way it always should be. An 'assert' makes this point in the code. On a
lazier note, an 'assert' also needs less typing :-).

I know I could define an assert myself, but something like that should
be universally, easily understood, not custom.

So, is there such an idiom that people use in place of an 'assert'
statement?


Thanks,
Ken
Gregory B. (Guest)
on 2007-08-02 08:49
(Received via mailing list)
On 8/1/07, Kenneth McDonald <removed_email_address@domain.invalid> wrote:
> If you saw an earlier version of this question, apologies...I haven't
> seen it, so assume I accidentally deleted it or it got lost or some such.
>
> In other languages, I'm accustomed to using "assert" as a sort of
> enforceable documentation. Is there an equivalent statement or idiom in
> Ruby? I find that simply raising exceptions isn't quite right;
> exceptions are normally used when one thinks something might be done
> about a problem, whereas asserts are used to check that something is the
> way it always should be. An 'assert' makes this point in the code. On a
> lazier note, an 'assert' also needs less typing :-).

Because Ruby's objects (and thus, their methods) are always open for
modification, this kind of mechanism is less helpful in Ruby than it
might be in other languages.  There are design by contract frameworks
out there that do what you're looking for, but this often strikes most
seasoned Rubyists as being an imported practice from other languages,
rather than something that's really needed in Ruby.

I don't find exception raising to necessarily indicate that something
might be done about a problem, but it's not a bad thing that they can
easily be caught.  Can you think of a reason why you wouldn't want a
user to possibly handle a failed assertion?

I think that if you describe the kinds of problems you're trying to
solve with assert, folks here will be able to best recommend the
idiomatic approach to handling them.  It's possible that the kind of
work you're doing really needs this kind of behaviour, and then you
would need to either find a suitable third party library or roll your
own code.  However, it's a little more likely that you're seeking a
solution to a problem that doesn't really exist in Ruby.

-greg
Thomas W. (Guest)
on 2007-08-02 08:55
(Received via mailing list)
2007/8/2, Kenneth McDonald <removed_email_address@domain.invalid>:
>
> I know I could define an assert myself, but something like that should
> be universally, easily understood, not custom.
>

I am not sure if you mean Unit Testing or Design by Contract. Ruby has
a Unit Testing Library Test::Unit with many assertions. You can find
it the documentation here
http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/
Kenneth McDonald (Guest)
on 2007-08-02 10:41
(Received via mailing list)
I'm not talking anything quite so complex as design by contract, just
the difference between

if  x != y
    ...raise new exception...
end

vs.

assert x == y

(which, in Python, raises an AssertionError, actually). It's nice to
have a really fast, identifiable way of saying in code, "this (possibly
nonobvious) condition is expected to hold at this point," and is (for
me) as much about documenting twiddly bits of code as it is about error
checking. I was surprised Ruby doesn't offer this, and hoped there'd be
a bit if syntax or semantics I'd missed that took its place. Not a big
deal though.

Thanks,
Ken
Michael F. (Guest)
on 2007-08-02 11:02
(Received via mailing list)
On 8/2/07, Kenneth McDonald <removed_email_address@domain.invalid> wrote:
>
>
> >> about a problem, whereas asserts are used to check that something is the
> >> way it always should be. An 'assert' makes this point in the code. On a
> >> lazier note, an 'assert' also needs less typing :-).

# Though, i have to admit, i never had any need for this kind of
check, here it is:

class AssertionError < StandardError
end

def assert a, b
  raise AssertionError, "#{a.inspect} != #{b.inspect}" unless a == b
end

assert 1, 1
assert 1, 2
Dan Z. (Guest)
on 2007-08-02 12:14
(Received via mailing list)
Michael F. wrote:
>
I like this idea, but I would make it more general (so you could use it
for more than equality tests):

class AssertionError < StandardError
end

def assert(message=nil, &block)
   unless(block.call)
     raise AssertionError, (message || "Assertion failed")
   end
end

Then you can do:
 >> assert {3==3}
=> nil
 >> assert("This test will fail") {false}
AssertionError: This test will fail
         from (irb):24:in `assert'
         from (irb):29
 >> assert { 3>=1 }
=> nil

And AssertionError will cause an exit (which is desirable behavior),
unless it is caught.

Dan
Thomas W. (Guest)
on 2007-08-02 12:34
(Received via mailing list)
2007/8/2, Kenneth McDonald <removed_email_address@domain.invalid>:
>
I am quite sure you're talking about Unit Testing. The PickAxe book
has a good and concise chapter(chapter 12) about it.

<code>
roman.rb
class Roman
  MAX_ROMAN = 4999

  def initialize(value)
    if value <= 0 or value > MAX_ROMAN then
      fail "Roman values must be > 0 and <= #{MAX_ROMAN}"
    end
    @value = value
  end

  FACTORS = [["m", 1000], ["cm", 900], ["d", 500],
                    ["cd", 400], ["c", 100], ["xc", 90],
                    ["l", 50], ["xl", 40], ["c", 10],
                    ["ix", 9], ["v", 5], ["iv", 4],
                    ["i", 1]]

  def to_s
    value = @value
    roman = ""
    for code, factor in FACTORS
      count, value = value.divmod(factor)
      roman << code unless count.zero?
    end
    roman
  end
end

test_roman.rb
require 'roman'
require 'test/unit'

class TestRoman < Test::Unit::TestCase
  def test_simple
    assert_equal("i", Roman.new(1).to_s)
    #will throw a failure
    #assert_equal("xi", Roman.new(9).to_s)
    assert_equal("ix", Roman.new(9).to_s)
    assert_raise(RuntimeError) { Roman.new(5000) }
  end
end
</code>

http://www.ruby-doc.org/stdlib/libdoc/test/unit/rd...
lists all available assert_* methods.
Tom L. (Guest)
on 2007-08-02 13:10
> Then you can do:
>  >> assert {3==3}
> => nil
>  >> assert("This test will fail") {false}
> AssertionError: This test will fail
>          from (irb):24:in `assert'
>          from (irb):29
>  >> assert { 3>=1 }
> => nil

If implemented this way, assert comes with a potential speed penalty.
This is why such an assert feature usually implies a command line option
to ignore assertions in production mode (eg in Eiffel). In ruby this can
in theory be done by using the method_added hook or similar. The code
could then look like this

    pre {|a, b| a > 0 && b > 0}
    post {|r| r > 0}
    def foo(a, b)
        a + b
    end

When a flag isn't set, pre & post could simply be ignored. There are
some libraries around that try to achieve this but IMHO they all have
some shortcoming. (Please correct me if I'm wrong.)

I think that a general way to define such wrappers had to be part of
ruby core language in order to get this right.

If ruby were lisp, you could define your own def_by_contract. But AFAIK
blocks cannot take optional arguments, which makes this currently
impossible (or not so useful).

Regards,
Thomas.
John C. (Guest)
on 2007-08-03 04:19
(Received via mailing list)
On Thu, 2 Aug 2007, Kenneth McDonald wrote:

> If you saw an earlier version of this question, apologies...I haven't seen
> it, so assume I accidentally deleted it or it got lost or some such.
>
> In other languages, I'm accustomed to using "assert" as a sort of enforceable
> documentation. Is there an equivalent statement or idiom in Ruby? I find that
> simply raising exceptions isn't quite right; exceptions are normally used
> when one thinks something might be done about a problem, whereas asserts are
> used to check that something is the way it always should be.

I been around this particular block a few times and have come up with
the simplest...

  def foo( a)
    raise "ASSERTION FAILURE! a '#{a}' not nice!" unless
      a.nice?

  end

Note a couple of other things...

I have thought about creating an
   class AssertionFailure < Exception
   end

But I'm not sure I care to. By an assertion failing I'm saying
"Something untoward/unclean has happened. I know not what, I have just
spotted the symptom. Give me some info, give me a backtrace, and I'll
sort it out later. The program has gone barking mad, take it out back
and shoot it"

Variants on the above, I may change the ASSERTION FAILURE to
PRECONDITION is that's what I mean...

  def foo( a)
    raise "PRECONDITION FAILURE! a '#{a}' not nice!" unless
      a.nice?

  end


Or I may let ruby do it's thing so instead of

  def foo( a)
    raise "PRECONDITION FAILURE! a '#{a}' is nil!" if a.nil?
    a.doStuff()
  end

I prefer...

  def foo( a)
    a.doStuff()
  end

And let Ruby throw the appropriate exception (which gives me a back
trace any way.)

Note I _never_ do something like...

   x = ARGV[0].to_f
   raise "ASSERTION FAILURE: x '#{x}' is negative" if x < 0

   puts x ** 0.5

An assertion failure _always_ means I have may a mistake, _never_ the
user.

In fact some of my scripts have at the end...

rescue Exception => details
   puts "This is John's Fault. Call John now and tell him to fix it.:
   puts details.to_s
   ...



Speedwise I profile, and if one of my asserts show up in a hot
spot... A # or two later it is gone from the execution path, but is
still there as documentation.

Another approach is to try lift the assert up the call tree. Usually
you escape some loop.

def foo(a)
   @n.times {|s| bah(s,a)
end

def bah(s,a)
   raise "ASSERT FAILURE!" unless a.nice?

   # do things with s and nice a
end

becomes...

def foo(a)
   raise "ASSERT FAILURE!" unless a.nice?
   @n.times {|s| bah(s,a)
end

def bah(s,a)
#  raise "ASSERT FAILURE!" unless a.nice?

   # do things with s and nice a
end

John C.                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : 
removed_email_address@domain.invalid
New Zealand
This topic is locked and can not be replied to.