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.
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
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.
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?
