From: John Carter Date: 2004-10-26T14:22:26+09:00 Subject: Re: Lint for Ruby On Tue, 26 Oct 2004, Matt Mower wrote: > I was wondering whether anyone had done any work on a Lint style tool > for Ruby programs? > > A google for "Ruby Lint" didn't turn up anything obviously useful. Short version of this reply... * "ruby -w" * Unit tests and .respond_to? and kind_of? blur the boundary between static analysis and dynamic testing. Long winded version of this reply... A very interesting question, what static analysis do we need for Ruby? "ruby -w" does a lot for you, don't leave the command line without it. Unit tests really do catch a lot of problems and are so easy to do. I tend to unit test and cross brace with a smattering of asserts in my code at critical places.. For example this gives me similar sort of safety that static typing would have, but with the flexibility of duck typing... def dostuff(duck) raise "The duck '#{duck}' should be able to :quack" unless duck.respond_to? :quack do_deeper_stuff(duck) end def do_deeper_stuff(duck) do_really_deep_stuff(duck) end def do_really_deep_stuff(duck) duck.quack end This example is too trivial to justify it, but in more complex code it can be a good way of forcing such type errors to the surface even when they are not exercised by your unit test. eg. The unit test may not be extensive enough to force control (in a more complex example) all the way down to "do_really_deep_stuff" but the assert catches the type error on _all_ runs. The other trick is Design by Contract style invariants. A lot of these suggestions are dynamic run-time stuff, not lint static analysis, but ruby's introspective abilities like respond_to? and kind_of? and unit tests makes that boundary a bit fuzzy. class MyClass def invariant unless BIG_HAIRY_EXPRESSION_THAT_IS_ALWAYS_TRUE_IF_THIS_OBJECT_IS_SANE pp self raise "Invariant failure" end end def initialize do stuff invariant end def complex_thingy invariant do complex stuff invariant end def hairy_stuff( inputs) raise hell unless ASSERT_STUFF_ABOUT_INPUT do hairy stuff invariant end end The another habit is to use rubyish style that resolves a lot of lint-able problems. C++ uses the "Resource Acquisition Is Initialization" idiom to minimise such things, the Ruby equivalent is using_resource do |resource| do stuff with resource end A lot of lint-able problems like the evil C switch statement has simply been eliminated by Matz's excellent design. The vast majority of the bugs left in my code are either in the "ruby-lint --read-bosses-mind --pendantic mycode.rb" category or in a failure handling case. I'll admit it. My unit tests are fairly good at testing that my code does what it should do, but I'm pretty lousy at imagining and testing ways in which external entities fail. Hence I would say most of my latent bugs are in the area of failure handling. Would lint help there? Maybe, but more creative use of mock objects in my unit tests would help more. I'm looking at Needle as a very promising way of doing that. http://needle.rubyforge.org/ John Carter Phone : (64)(3) 358 6639 Tait Electronics Fax : (64)(3) 359 4632 PO Box 1645 Christchurch Email : john.carter@tait.co.nz New Zealand The universe is absolutely plastered with the dashed lines exactly one space long.