

Ruby’s require method gives you more than enough rope to hang yourself. Having now implemented too many projects with too many different approaches to using require, we thought it was worth writing down the conclusions we’ve come to.
We’ve gleaned most of our knowledge from these two articles, here, and here. So a big thank you to their authors.
Do use requires that are relative to ruby’s load path.
For example,
require 'my_project/b/c'
This assumesthat a directory containing the my_project directory is already in Ruby’s load_path (typically your project’s lib folder). It should be — more on this below.
Don’t use requires that are relative to the file.
For example, avoid:
require File.join(File.dirname(__FILE__), "foo", "bar")
Require decides if a file has already been loaded based on the unexpanded path it’s given. Two different relative paths are treated as two different files, so will be loaded twice. Moreover, it’s just yuky. If I want to figure which file a class is defined in, then I shouldn’t have to navigate a tangled web of file references.
Don’t require rubygems in your code.
It’s rude, not everyone wants to use Rubygems. If you want to use it then run
ruby -rubygems my_file.rb
OR use the following environment variable
RUBYOPT="rubygems"
Don’t manipulate ruby’s load path in your code.
Gem will always add your project’s lib directory to ruby’s load path. However, if your running an executable directly from your project (i.e. in your bin directory), or you’re running a set of units tests, then you’ll need to add your lib folder to ruby’s load path. There are a few ways to do this:
Add this line to the top of your executable (contradicts what I just said, but at least in a consistent way).
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
Or run Ruby like this:
Ruby -Ilib my_test_runner
Or set this shell environment variable:
export RUBYOPT=Ilib
Do use underscores as a separator in your project’s name.
No spaces, dashes, or camel case. Most ruby gems follow this convention, so best not to surprise people.
Do create a sub-directory under /lib with the same name as your project. And stick all your ruby files under this.
This is important if you want to package your project up as a Gem at some stage. The contents of every gem’s lib directory installed on your system automatically gets included in ruby’s $LOAD_PATH. Sticking all your code files directly in your lib is asking for same filename collisions. Even if you’re not going to package your project as a gem, it pays to follow this rule for consistency reasons.
Do create a file within your lib directory that is responsible for requiring in your other project files.
This is really an extension of the above. If you’ve nested all your code files in a project directory, then you need a single code file that is responsible for loading them all. For example, your lib fold should look like:
lib/my_project.rb
lib/my_project/
Do make your class namespaces correspond to your directory structure.
Your classes should be within modules that match the directory structure. This way you’re keeping your code namespace consistant with your directory namespace, making it easier to find things. For example, any ruby files in foo/bar should also be in a module Foo::Bar. More importantly, you’re reducing the risk of namespace collision from other libraries.
Do stick your executables in /bin. This is the default location where Gem will install executables from. You can change it, but you shouldn’t unless you have a good reason to.
Don’t use a suffix on your executables (i.e. no .rb). If you’re providing an executable to be run from the shell, then it should work like any other executable (i.e not have a .rb appended at the end).
Do use the following shebang line at the top of your ruby code to make it into an executable.
#!/usr/bin/env ruby
If the above were boiled down to a single principle, it would be this:
Treat require like it’s requiring a namespace, and not a file. This is much the same as how import, and using, work in languages such as >C#, C++, and Java.
Dominic Graefen (@devboy_org) introduces a Flex/Flash extension for Buildr. I recommend Buildr over Maven or other declarative build/dependency systems. Give it a go :)
A lot of my life has been spent on IRC. I got really sick of it. I got tired of the assumptions made by people meeting “anonymous” people for the first time. I include myself here, it seemed inevitable that I’d jump to conclusions without hearing someone out, and in turn made them sick of IRC too.
However, in a recent expedition into making my first clean-coded, well thought out Ruby Gem, I turned to IRC for help.
What I got was much more than I asked for, and it was all awesome-sauce.
We use ActiveRecord::Base.connection.execute occasionally to run really fast read-only queries. Mostly for Dataviz.
I don’t like it. You get a row of string-keyed string-valued hashes back from the database. All that knowledge about the schema and how to handle differently typed columns that ActiveRecord gives us, gone.
Our views and view-helpers were littered with new idioms (bad habbits?) - the “Dealing with direct sql results” idioms.
Not only that, but the SQL queries were in controllers! Ick.
My first cut of this gem is SQLRecord v 0.1.0
It was my first ever DSL in Ruby, and I thought, quite idiomattic.
So I started documenting. It was annoying me that my Yard Index was showing a module ClassMethods rather than showing them as Class Methods of the module that used the include-to-extend idiom.
So I went to #yard at irc.freenode.net channel to ask about indicating that this module’s methods belonged to another. Loren Segal was there and told me there wasn’t any way of munging the docs…
Here’s the beginning of that discussion:
[08:50]
Is there a trick in yard to make Module docs (like ClassMethods) look like those of the parent Module?
[08:51] <@lsegal> you mean like get ClassMethods to be interpreted as a set of class methods? Unfortunately not really
[08:52]yah thats what i meant
[08:53]http://yehudakatz.com/2009/11/12/better-ruby-idioms/ I’m going to re-read this until i get it.
[08:54] <@lsegal> indeed, that’s a good one. I’m not too fond of ClassMethods either
[08:55] <@lsegal> poor docs being one reason why
[08:56] <@lsegal> ClassMethods usually translates to “i have a poorly named mixin”
Yehuda’s post was haunting me but I wasn’t understanding it.
For the next 2 hours, off and on, Loren gave me a code review that changed my world view.
I would say that he improved Ruby by improving one passionate developers view of Ruby.
I now believed that good OO Principles were entirely practicable within this magical wonderland language called Ruby, where all ropes are long and could hang you, your family, and your-pit bull, without breaking a sweat.
Now I had a new perspective, a better Gem and I was pretty sure I was coding what I needed, without over-cooking it.
So one thing that happened as I re-factored, was a mistaken version bump into the 1.0s. Not where I meant to be. I trotted off to #rubygems at irc.freenode.net.
I got an answer about using gem ‘yank’ to remove a gem but then:
<@drbrain> you should not be embarrassed by releasing early and often
Ok then. I broke semantic versioning but I was hitting it hard and “showing my working”. Math education really does have value!
A little later, out of the blue, and not related to my versioning problem, but the code itself, someone said:
doesn’t AR::Base.find_by_sql solve this?
Ok! Here we go, the IRC I know and hate. Someones about to tell me I’m doing it wrong without background information…
Well no not really. Erik Hollensbe, as it turns out, had an abiding interest in what I was doing and made me argue the approach I was using. Moar gravy for my awesome-sauce.
That’s it. Thankyou to the community for being open minded, passionate and brilliant. I’ve had a very good week.
Why is it so hard to find ruby conventions? It’s the biggest aspect of the community, isn’t it?
© 2010 - VisFleet Ltd
No prawns were harmed in
the making of this website
Comments