Archive for April, 2008
I ran into this problem while trying to MonkeyPatch some class methods into existing Rails classes.
Say you have the following Mooable module (as a rails plugin) that you want to dynamically inject into ActiveRecord::Base (or any ruby class for that matter).
[sourcecode language='ruby']
# app/models/my_model.rb
class MyModel < ActiveRecord::Base
...
end
# plugins/mooable/lib/mooable.rb
module Mooable
def moo
return "Moo!"
end
end
# plugins/mooable/init.irb
ActiveRecord::Base.send :include, Mooable
# test
MyModel.new.moo # => "Moo!"
[/sourcecode]
But what if you wanted #moo to be a class method on ActiveRecord::Base? I was tempted to do the following:
[sourcecode language='ruby']
#plugins/mooable/lib/mooable.rb
module Mooable
def self.moo
return "Moo!"
end
end
# test
MyModel.moo # => NoMethodError: undefined method `moo' for MyModel:Class
[/sourcecode]
As you can see, simply defining the module method as a class method is a no-go (I'm sure this is a major nuby mistake, but it's always worth a try!)
The best way I have found so far to monkeypatch new class methods into rails classes with a plugin is to use the Object#extend method inside of the module's self.included hook. The new class methods are defined within a sub-module Mooable::ClassMethods for easy inclusion.
[sourcecode language='ruby']
# plugins/mooable/lib/mooable.rb
module Mooable
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def moo
return "Moo Static/Class Method"
end
end
def moo
return "Moo Instance Method!"
end
end
# test
MyModel.moo # => "Moo Static/Class Method"
MyModel.new.moo # => "Moo Instance Method!"
[/sourcecode]
This works like a charm.
1 Comment »
April 12, 2008 | opinion
I had a shared blog post from kottke.org in my google reader today. It's an Ernest Hemingway quote on the author's use of symbolism:
"No good book has ever been written that has in it symbols arrived at beforehand and stuck in," says Hemingway. "That kind of symbol sticks out like raisins in raisin bread. Raisin bread is all right, but plain bread is better." He opens two bottles of beer and continues: "I tried to make a real old man, a real boy, a real sea and a real fish and real sharks. But if I made them good and true enough they would mean many things. The hardest thing is to make something really true and sometimes truer than true."
I often get a lot of flack for thinking and talking about design patterns. I think the main reason is because most people who learn about them, get excited and start wildly trying to apply them everywhere possible-- which subverts the underlying principle of design patterns.
One of the most valuable lessons I've learned about design patterns is to find them within the context of the problem and not to let the problem be defined by the pattern. Don't apply your favorite pattern to a problem just because you know or like it. This is what Hemingway is saying; applying a design pattern for design pattern's sake "sticks out like raisins in raisin bread."
No Comments »
April 10, 2008 | code, rails
Having problems with Has and Belongs To Many(habtm) relationships loading your models with the wrong Ids? I was, the story follows.
Admittedly, HABTM is a relationship in Rails that I use rarely. This little snag caught me off-guard last week, and the usual internet searches didn't yield particularly fruitful results.
Say you have a simple HABTM relationship:
[sourcecode lang='ruby']
#Predator.rb
class Predator < ActiveRecord::Base
has_and_belongs_to_many :prey
end
#Prey.rb
class Predator < ActiveRecord::Base
has_and_belongs_to_many :predators
end
[/sourcecode]
So you've added the relationship, now you go to add your join table as a new migration:
[sourcecode lang='ruby']
class CreatePreyPredators < ActiveRecord::Migration
def self.up
create_table :prey_predators do |t|
t.integer :prey_id
t.integer :predator_id
end
end
def self.down
drop_table :prey_predators
end
end
[/sourcecode]
Now, say you've added a few relationships in your database:
[sourcecode lang='sql']
select * from prey_predators;
+---+--------------+--------------+
| id | prey_id | predator_id |
+------------------+--------------+
| 1 | 218872420 | 368007386 |
+------------------+--------------+
1 row in set (0.00 sec)
[/sourcecode]
BUT, when you look at your shiny new relationship in the console, you see this:
[sourcecode lang='html']
>> Prey.find(:first).predators
#
[/sourcecode]
You can see that IRB is reporting the predator's id as 1 and not 368007386. What you're seeing isn't the primary key of the predator record, but the primary key of the relationship record in the join table where predator was found.
It would appear that Rails gives any column named 'id' precedence when determining the id of the record-- ignoring the foreign kesy in the join table. I tried specifying the :foreign_key and :associated_foreign_key on the has_and_belongs_to_many relationship, but that only appears to affect the query that selects the records, not the model's "reconstitution" from the database.
Unless you want this behavior, just add :id => false to your migration and you're woes will be gone:
[sourcecode lang='ruby']
class CreatePreyPredators < ActiveRecord::Migration
def self.up
create_table :prey_predators, :id => false do |t|
t.integer :prey_id
t.integer :predator_id
end
end
def self.down
drop_table :prey_predators
end
end
[/sourcecode]
No Comments »
I'm going to start hosting my feed through feedburner. Please update your feed urls to: http://feeds.feedburner.com/Acts_as_martian
No Comments »