I've just started on my first decent-sized Ruby project and before the number of files spirals too far out of control I wanted to ask for some advice on what the accepted "best practice" for organizing files and code might be. I've tried to download and look at some largish Ruby projects but I don't have a clear picture of what the Right Thing is. Specifically, just say I add some methods to an existing standard class like Array, what would be the best convention for choosing where to store the added code? Here's a concrete example: let's say I want to add a "eol?" method to the StringScanner class? class StringScanner def eol? return true if self.eos? or self.match?(/[\n\r]/) false end end At the moment I have stuck it inside another file (for one of the classes that needs to use this method). But what if I want to keep it in a separate file? What would be the conventional name for such a file (evidently not "strscan.rb" because that would most likely cause confusion with the StringScanner implementation when doing a "require 'strscan'"). Likewise I had a similar doubt when I wanted to added some assertions of my own in addition to those which come with Test::Unit::Assertions. The original assertions are defined in a shared location terminating in "test/unit/assertions.rb"; but how would I name my additions file if I wanted to keep things in a separate file? For the time being I've just stuck the new code at the top of another file, but somehow it just doesn't feel right...
on 2007-01-19 16:29
on 2007-01-19 16:29
On 1/11/07, Greg Hurrell <email@example.com> wrote: > > (evidently not "strscan.rb" because that would most likely cause > doesn't feel right... I don't know the answer, and honestly, this is the only thing about Ruby that makes me nervous. I once heard about a Python project where the same method would have different return types depending on which other methods had been called on the object at earlier points in the program. It was a maintenance nightmare. In practice, I've never actually seen it go wrong, but it seems to me that there are inherent risks when you can modify any part of the system from any point within the system. What if you use two different gems which both modify a fundamental class? In Rails projects, I've put changes to ActiveRecord::Base in the environment config file because I was in a hurry. In the S3 library I mentioned in another thread, I had to change an HTTP library's file-handling. I put it in the S3 library, because that was the functionality which required the change. It's pretty obvious that either one of those changes could have had unpleasant side effects further down the road, although thankfully, neither one has (yet). I think what I'd probably recommend is something like a "lib/modifications/" dir. It really won't make a difference to Ruby where these files live, but if you keep them somewhere like "lib/modifications/new_assertions.rb", that seems pretty tidy.
on 2007-01-19 16:31
On Fri, Jan 12, 2007 at 12:55:09AM +0900, Greg Hurrell wrote: [...] > end In many cases it is not necessary to reopen existing classes at all, and is often preferable not to. For your example, I would guess that you are doing some sort of parsing, maybe something like (pseudo-Ruby, because I don't really know StringScanner): scanner = StringScanner.new(open(filename)) scanner.each do |token| if scanner.eol? #do stuff else #do other stuff end end You need scanner to respond to #eol?, but you don't need to modify StringScanner to do it. Consider using a module: module StringScannerEol def eol? # cleaned up a little self.eos? or self.match?(/[\n\r]/) end end Now we change the first line of the code above to: scanner = StringScanner.new(open(filename)).extend(StringScannerEol) Now scanner will respond to #eol? and all is well. > At the moment I have stuck it inside another file (for one of the > classes that needs to use this method). But what if I want to keep it > in a separate file? What would be the conventional name for such a file > (evidently not "strscan.rb" because that would most likely cause > confusion with the StringScanner implementation when doing a "require > 'strscan'"). The module I gave above can be appropriately included in the file with the code that uses it. > Likewise I had a similar doubt when I wanted to added some assertions > of my own in addition to those which come with Test::Unit::Assertions. [...] Again, a good opportunity for a module. Your test classes can include a module with the additional assertions. --Greg