17 Aug 2008, 2:02pm

by Ariejan de Vroom
2 comments

Skinny Controllers and Overweight Models

All Rails developers know the slogan “Skinny Controllers, Fat Models” and I heartily agree with it. Every conference you go to, you hear it. But there’s a problem! My Fat models got overweight!

What happened? By stuffing all applications logic in the Models, they become fat, very fat. Although this is supposed to be a good thing, I don’t like it. My models get so fat that it takes me forever to scroll through it and find the method I’m working on. There must be a better way!

Well, yes there is: create modules! Normally you’d write a module to reuse your code in different places, but there’s no rule that says you may not use a module only once.

So, I package all related code (e.g. Authentication, state management, managing associated objects, etc) into different modules and place them in the /lib directory. Let’s say you have a a bunch of methods to handle keep a counter on your User model

Class User < ActiveRecord::Base
  attr_accessor :counter
 
  def up
    counter += 1
  end
 
  def down
    counter -= 1
  end
 
  def reset
    counter = 0
  end
end

You could create a new file lib/counter.rb and include that module in your User model.

Class User < ActiveRecord::Base
  attr_accessor :counter
  include Counter
end
module Counter
  def up
    counter += 1
  end
 
  def down
    counter -= 1
  end
 
  def reset
    counter = 0
  end
end

As you can see, this keeps your fat User model clean and makes it easier for you to find code that applies to a certain function.

17 Aug 2008, 1:19pm

by Ariejan de Vroom
2 comments

ActiveRecord Read Only Model

ActiveRecord is great in providing CRUD for your data models. In some cases, however, it’s necessary to prevent write access to these models. The data may be provided by an external source and should only be used as a reference in your application, for example.

I’m going to show you how you can easily mark a Model as read only all the time. In this example I have a Item model like this:

class Item < ActiveRecord::Base
end

ActiveRecord::Base provides two methods that may be of interest here:

def readonly!
  @readonly = true
end
 
def readonly?
  defined?(@readonly) && @readonly == true
end

The first method sets the record to read only. This is great, but we don’t want to set the read only property every time we load a model. The second, readonly?, return true if the object is read only or false if it isn’t.

So, if we return true on the readonly? method, our object is marked as read only. Great!

class Item < ActiveRecord::Base
  def readonly?
    true
  end
end

That is all! All Item objects are now marked as read only all the time. If you try to write to the model, you’ll receive an error.

item = Item.find(:first)
item.update_attributes(:name => 'Some item name')
=> ActiveRecord::RecordReadOnly
14 Aug 2008, 12:29pm

by Ariejan de Vroom
2 comments

Useless Ruby Gems for your pleasure

The past few days I’v taken some time to find out how to create a Ruby Gem. This has been on my to-do list for quite a while, but now I’m able to tick it off.

Well, what did I make?

The first Gem can also be used as a Ruby on Rails plugin and is called ActsAsGold. If you’ve ever played World of Warcraft, you’ll know how the money system works. You have Copper. A 100 Copper is worth 1 Silver. And 100 Silver is worth 1 Gold.

The ActsAsGold Gem allows you to extend your ActiveRecord model with this money system. All you need on your model is an attribute that stores a single integer value.

Let me give you a small tour.

class Player < ActiveRecord::Base
  acts_as_gold :column => :money
end
 
# This will be store like @player.money => 2003652
@player.gold => 200
@player.silver => 36
@player.copper => 52
 
# You can also easily earn or spend money
@player.earn(10.gold + 75.silver)
@player.spend(1.gold + 10.silver + 95.copper)

Read more about the Gem, or install the gem right now:

sudo gem install ariejan-acts_as_gold --source http://gems.github.com

The other gem is WarcraftArmory, which is still in early development, so new stuff can and will be added in the future.

WA (WarcraftArmory) allows you to easily retrieve character information from the World of Warcraft Armory. Currently it can retrieve:

  • Name of the character
  • Name of the characters guild
  • Level
  • Race
  • Class

It works for both EU and US warcraft servers.

require 'warcraft_armory'
 
character = WarcraftArmory.find(:eu, 'Aszune', 'Nosius')
 
character.race => "Human"
character.level => 15

Again, simply install the plugin and use it like any other gem or read the README first.

sudo gem install ariejan-warcraft_armory --source http://gems.github.com

Of course, these gems are released under the MIT license. The code is on Gitub (acts_as_gold, warcraft_armory) and patches with fixes and new features are gladly accepted.

Please let me know if you find these gems useful or if you use them in one of your projects.

12 Aug 2008, 11:30pm

by Ariejan de Vroom
4 comments

Ruby on Rails: UUID as your ActiveRecord primary key

Sometimes, using the good old ‘auto increment’ from your database just isn’t good enough. If you really require that all your objects have unique ID, even across systems and different databases there’s only one way go: UUID or Universally Unique IDentifier.

A UUID is generated in such a way that every generated UUID in the world is unique. For example: 12f186e6-687e-11ad-843e-001b632783f1. This string is randomly generated based on several factors that guarantee it’s uniqueness.

Anyway, you want to replace the default integer-based primary keys in your model with a UUID. This is quite easy, but there are some caveats.

First off, you should have a column in your database table that holds the UUID. You may be tempted to just change the column definition for id from integer to string and be done with it. But this won’t work as expected. For your development, and maybe even your production system, this may work fine, but you might be in for some unexpected surprises.

The best example of such a surprise is RSpec. RSpec uses ‘rake db:schema:dump’ to create a sql dump to quickly load the database with. However, the ’schema:dump’ does not look at the id column in your database, but instead adds the default primary key definition from the ActiveRecord adapter.

The solution is to disable the id column and create a primary key column named uuid instead.

create_table :posts, :id => false do |t|
  t.string :uuid, :limit => 36, :primary => true
end

In your Post model you should then set the name of this new primary key column.

class Post < ActiveRecord::Base
  set_primary_key "uuid"
end

The next step is to create the UUID itself. We’ll have to do this the Rails app, because most databases don’t support UUID out of the box.

First install the uuidtools gem

sudo gem install uuidtools

Create a file like lib/uuid_helper.rb and add the following content.

require 'rubygems'
require 'uuidtools'
 
module UUIDHelper
  def before_create()
    self.uuid = UUID.timestamp_create().to_s
  end
end

Then, include this module in all UUID-enabled models, like Post in this example.

class Post < ActiveRecord::Base
  set_primary_key "uuid"
  include UUIDHelper
end

Now, when you save a new Post object, the uuid field is automatically filled with a Universally Unique Identifier. What else could you wish for?