Friday, April 13, 2012

Scoping with Object#tap

Object#tap is great for creating scopes and defining a context. Here are some examples:

Initializers, such as configuring Braintree:

Notice that without the block, the settings variable would become global, which is bad.

In a template:

Assigning local variables inside of a template is a code smell. We can avoid doing so, and create a visual context around the variable with Object#tap. (This was first described by 37signals.)

In tests:

Each tap block scopes the variable and shows a context. This could be split into multiple tests, but this is a nice way to have one test per method.

Friday, December 24, 2010

New Ruby 1.9 hash syntax

Ruby 1.9 supports a new hash syntax. It looks like this:

This only works if the key is a symbol. I don't recommend using it if the hash has both symbols and other types of objects, because you end up mixing the new syntax with the "old".

Textmate Snippet

If you're using Textmate, change your Hash Pair - :key => "value" snippet to the following:
${1:key}: ${2:"${3:value}"}${4:, }

Wednesday, June 2, 2010

Tweaking on Rails 3.0: #4 Object#presence

Object#presence is new to object/blank.rb in ActiveSupport (View source). It simply returns self if self.present? is true, otherwise it returns nil. This works great for blank strings and empty arrays:

Let's give our blog posts a default title if a blank one is provided:

Now let's refactor it with Object#presence:

How cute.

Friday, May 28, 2010

Tweaking on Rails 3.0: #3 class_attribute

We all know that class variables in Ruby don't work well with inheritance. For this reason, ActiveSupport originally added class_inheritable_accessor (view source). The method works by using the Class.inherited callback, and copying attributes down to subclasses. This was basically a hack. It failed to work correctly if the parent's accessor was written to after the inherited classes were defined, and it's wasteful to copy attributes to every inheriting class.

Along comes class_attribute (view source). It works by defining the class accessor methods on the class's singleton class. The technique is described in more detail by a article. The win is that we're getting true inheritance and expected behavior.

How do you improve your code in Rails 3? Simply replace the text class_inheritable_accessor with class_attribute!

Tweaking on Rails 3.0: #2 ActiveSupport::Concern

Unless you've been writing your Ruby code in a cave, deep within uninhabited mountains, you have probably seen and written a Ruby module. And it probably went something like this:

Including this into a User model gives us User.awesome, User.make_everyone_awesome, and User#awesome?.

Before getting into ActiveSupport::Concern, you shouldn't be defining the InstanceMethods module and explicitly including it. Remove it now, based on reasons explained by Yehuda Katz. I only provide it in this example for criticism.

In Rails 3, extend your module with ActiveSupport::Concern. This gives you two things:
  • The including class is automatically extended with the ClassMethods module. No more klass.extend(ClassMethods).
  • You get a new method called "included", which provides a terser way to define the idiomatic "self.include(base)" method.
. This is best described by a refactoring of the above example:

As an aside, the Awesomeness module should definitely go into Rails core, but they keep ignoring my requests.

Thursday, May 27, 2010

Tweaking on Rails 3.0: #1 Callback Blocks

There are plenty of articles about the big changes in Rails 3, so I feel motivated to comment on the small ones.
You might be familiar with using a block with a callback. Let's define an article model for our awesome new blogging software. It defines a default title for a newly created article:

Notice that we must pass the new article to the block. In Rails 3, the block is evaluated within the scope of the new instance, so we can change it to look like this:

Extremely minor, but I find myself using block callbacks more often because of it.

Wednesday, September 2, 2009

Foreigner comes full circle

After actively developing multiple Rails projects over the last three years, I've learned when to not fight the framework, and I've also learned when it's appropriate to roll up your sleeves and write some Ruby code. I've stopped using composite primary keys, I've stopped attempting to namespace models, I always use the default "id" for a primary key, the list goes on.

One thing my mind has not changed on is using foreign keys. I use them, and I've finally completed Foreigner, a foreign key migration library that meets my initial requirements:
  • Do not change existing behavior of the application
  • Perform a no-op when the adapter does not support foreign keys
  • Follow similar API as 'add_index'
  • Dump foreign keys into schema.rb

Do not change existing behavior of the application

I found numerous existing foreign key plugins that attempted to automagically add foreign keys. For example, automatically adding foreign keys based on column names, or automatically adding associations to active record models base on foreign keys in the database. I believe both of these are mistakes. Automatically adding foreign keys changes existing migrations, and always seems to break things. Automatically creating active record associations puts too much guesswork into play. There's a difference between using foreign keys to enforce integrity (good), and using foreign keys to change the behavior of the application (bad).

Lastly, I thought long and hard about whether calls to t.references should automatically add a foreign key. I finally resolved on not doing this, and compromised by adding a new :foreign_key option. For example:
change_table :comments do |t|
t.references :author, :foreign_key => true

Perform a no-op when the adapter does not support foreign keys

While this does not help me, it is required to support adapters such as sqlite3. The AbstractAdapter implements all necessary methods with a no-op. With Foreigner installed, you can develop with sqlite3 and run production with MySql.

Follow similar API as 'add_index'

Adding foreign keys isn't much different than adding indexes, and everyone is familiar with using add_index. You add_foreign_key, remove_foreign_key and can even use change_table.

Dump foreign keys into schema.rb

The final feature I added to Foreigner is dumping foreign keys to schema.rb, so that you can avoid using the SQL structure. Similar to how indexes are added to schema.rb, Foreigner inspects the database and determines the foreign keys.

In conclusion...

There's been a lot of discussion in the past about whether or not you should be using foreign keys. This shows to me is that there is a large group of people who probably want this available out of the box with Rails. The library was designed to not affect those who don't want them; the primary purpose in doing so was to show that everyone can have it their own way. My next step is turning this into a gem.

Get the source: