Monday, June 30, 2008
A word of warning
And now a "a word of warning" (sic) from the ActiveRecord API docs:
Don‘t create associations that have the same name as instance methods of ActiveRecord::Base. Since the association adds a method with that name to its model, it will override the inherited method and break things. For instance, attributes and connection would be bad choices for association names.
Reading that was another one of those "No. Really." moments.
The second time an undetected collision of this type got me (note to self: don't name a database column "errors" unless you have an afternoon to spare) I really considered some shell (better yet Perl, heh) scripts to grep out all the method names from all the plugins + my code and check for collisions. I now instinctively avoid any obvious or intuitive sounding name for a column or method. Since we're up to a dozen+ plugins, it's probably in use already.
The interpreter could issue a warning of some kind when overloading and a test flag was set, I suppose. HEY! Agile means no whining. Suck it up and KEEP TYPING!
Wednesday, May 7, 2008
Ruby Constants Aren't
I don't know how I missed this but I did. This morning it bit us, hard.
Ruby constants aren't constant. Well, actually, the constant is a reference to an object. That's immutable, but you can use it to change the object it will refer to. Really. It's a feature:
"Ruby, unlike less flexible languages, lets you alter the value of a constant, although this will generate a warning message." - from Programming Ruby, by Dave Thomas
Umm, isn't that what's usually meant by "variable?" So you mean that...
irb(main):001:0> CONST = "foo"
=> "foo"
irb(main):002:0> CONST = "bar"
(irb):2: warning: already initialized constant CONST
=> "bar"
Yeah. Well, I vote we get rid of that annoying error message. It kinda spoils the thrill.
Ruby constants aren't constant. Well, actually, the constant is a reference to an object. That's immutable, but you can use it to change the object it will refer to. Really. It's a feature:
"Ruby, unlike less flexible languages, lets you alter the value of a constant, although this will generate a warning message." - from Programming Ruby, by Dave Thomas
Umm, isn't that what's usually meant by "variable?" So you mean that...
irb(main):001:0> CONST = "foo"
=> "foo"
irb(main):002:0> CONST = "bar"
(irb):2: warning: already initialized constant CONST
=> "bar"
Yeah. Well, I vote we get rid of that annoying error message. It kinda spoils the thrill.
Wednesday, April 9, 2008
Tweaking XML Rendering For ActiveScaffold
Another day, another few hours of googling Ruby blogs to figure out how to accomplish some seemingly trivial task. This time it's making some minor adjustments to the XML rendering of an ActiveRecord model when the controller is being supplied by ActiveScaffold. Don't get me wrong, I love automagically created applications. It's just that, sometimes, all you want to do is change... this one... freaking detail...
API says you can pass options to to_xml. As in:
my_model.to_xml :except => [:ugly_field]
Problem is, the to_xml method is getting called by the ActiveScaffold ApplicationController and you don't really want to edit that. If you just want to make a small adjustment and not rewrite the entire rendering method the best option you have is to overload this method in the model class, add the options to the passed array and call super()
def to_xml(options={})
options[:methods] = [:get_formatted_value, :other_value]
options[:except] = [:irrelevant_accession_number]
super(options)
end
Should have been obvious, I suppose, but I spent some of my (dwindling) wattage on it. Oh well, here's hoping the next rubie finds this on a google search for "activescaffold xml rendering."
API says you can pass options to to_xml. As in:
my_model.to_xml :except => [:ugly_field]
Problem is, the to_xml method is getting called by the ActiveScaffold ApplicationController and you don't really want to edit that. If you just want to make a small adjustment and not rewrite the entire rendering method the best option you have is to overload this method in the model class, add the options to the passed array and call super()
def to_xml(options={})
options[:methods] = [:get_formatted_value, :other_value]
options[:except] = [:irrelevant_accession_number]
super(options)
end
Should have been obvious, I suppose, but I spent some of my (dwindling) wattage on it. Oh well, here's hoping the next rubie finds this on a google search for "activescaffold xml rendering."
Wednesday, March 19, 2008
Ruby Method References In A One-Pass Interpreter
Ruby allows for some very clever techniques involving run-time code evaluation. I find these particularly handy when writing table driven code (e.g. file parsers). But coder beware. There's been a lot of discussion about whether Ruby blocks are truly closures but the fact that it's a one-pass interpreter (at least the reference implementation I'm using, ruby 1.8.5) can cause some unexpected results as well.
Consider this simple case: initializing a class property using a method reference. The Object.method call will succeed or fail (return nil) depending on when it's called in the class definition. First, let's put the initialization at the top of the class (where most of us would normally put it):
class TestMeth
@@method_ref = self.method(:meth)
def self.meth
puts "executing meth()"
end
def run_method
@@method_ref.call
end
end
tm = TestMeth.new
tm.run_method
Sorry, no can do:
[chrisa@doppio ~]$ ruby t.rb
t.rb:3:in `method': undefined method `meth' for class `Class' (NameError)
from t.rb:3
Now move the assignment to the other side of the method definition:
class TestMeth
def self.meth
puts "executing meth()"
end
@@method_ref = self.method(:meth)
def run_method
@@method_ref.call
end
end
tm = TestMeth.new
tm.run_method
That works:
[chrisa@doppio ~]$ ruby t.rb
executing meth()
The behavior is the same if you make @@method_ref a constant (i.e. 'METHOD_REF'). The interpreter hasn't gotten to that line of the file when the assignment is executed. If you're using method references, you either have to position them correctly in the file or assign them at run-time in an initializer method. Works, but not as you might expect.
Consider this simple case: initializing a class property using a method reference. The Object.method call will succeed or fail (return nil) depending on when it's called in the class definition. First, let's put the initialization at the top of the class (where most of us would normally put it):
class TestMeth
@@method_ref = self.method(:meth)
def self.meth
puts "executing meth()"
end
def run_method
@@method_ref.call
end
end
tm = TestMeth.new
tm.run_method
Sorry, no can do:
[chrisa@doppio ~]$ ruby t.rb
t.rb:3:in `method': undefined method `meth' for class `Class' (NameError)
from t.rb:3
Now move the assignment to the other side of the method definition:
class TestMeth
def self.meth
puts "executing meth()"
end
@@method_ref = self.method(:meth)
def run_method
@@method_ref.call
end
end
tm = TestMeth.new
tm.run_method
That works:
[chrisa@doppio ~]$ ruby t.rb
executing meth()
The behavior is the same if you make @@method_ref a constant (i.e. 'METHOD_REF'). The interpreter hasn't gotten to that line of the file when the assignment is executed. If you're using method references, you either have to position them correctly in the file or assign them at run-time in an initializer method. Works, but not as you might expect.
Subscribe to:
Posts (Atom)