Showing posts with label Ruby Newbie. Show all posts
Showing posts with label Ruby Newbie. Show all posts
Friday, January 16, 2009
Testing Values Returned From ActiveRecord
Be really careful about testing values returned from the db by ActiveRecord. How you fetch it affects the data type. ActiveRecord typecasts the column values returned from the database... sometimes.
This bit me hard today:
If you get a model back from active record e.g. with a MyClazz.find() ActiveRecord::Base will read the dictionary tables and typecast the returned attribute to what you expect. If you use one of the connection class methods, it doesn't do this, it just returns everything as a string.
Comparisons to integer, for example, will FAIL.
Watch this:
# run the console
[chrisa@ibs-chrisa-ux1 lims_m4]$ script/console
Loading development environment (Rails 2.1.1)
# fetch an arbitrary slide
>> s = Slide.find(81247588)
=> #<slide id: 81247588,
slide_group_id: 81246827,
...>
# now, I'm expecting an integer for slide_group_id, so I test it with the == operator:
>> s.slide_group_id == 81246827
=> true
# how nice, principle of least astonishment and all...
# now let's get that same record using the Base.connection method:
>> hash = ActiveRecord::Base.connection.select_one("SELECT * FROM slides WHERE id = 81247588")
=> { "id"=>"81247588",
"slide_group_id"=>"81246827",
...}
>> sgi = hash['slide_group_id']
=> "81246827"
# Whoa. It's a string:
>> sgi == 81246827
=> false
>>
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.
Tuesday, March 4, 2008
Nil desperandum!
Which means literally, "Despair of Nothing!" Makes you wonder if Horace ever worked in software.
When is a null object reference not a null object reference? Why, when you can dereference it, of course.
If a Ruby method returns nil and I try to dereference it thinking I have a valid object reference, it's going to throw, right? Raise an exception? Hurl?
Erm, no. Actually, nil is an object. Let's have some fun with irb:
[chrisa@doppio ~]$ irb
Can I convert it to an integer?
irb(main):001:0> nil.to_i
=> 0
Yep. At least it's 0. How 'bout a string?
irb(main):002:0> nil.to_s
=> ""
Of course, it's an object, silly! And an array, etc.
irb(main):003:0> nil.to_a
=> []
This next one is really cool. Let's say you got this object reference back from ActiveRecord using MyModel.find(:first). Since you've dutifully followed the convention, you expect to get it's accession number by dereferencing the automagic property "id":
irb(main):004:0> nil.id
(irb):2: warning: Object#id will be deprecated; use Object#object_id
=> 4
Love that deprecation warning. We all hunt down and fix those right away, right? I spent a pleasant afternoon staring at the source of that one. Remember to read those web server logs carefully, kids!
When is a null object reference not a null object reference? Why, when you can dereference it, of course.
If a Ruby method returns nil and I try to dereference it thinking I have a valid object reference, it's going to throw, right? Raise an exception? Hurl?
Erm, no. Actually, nil is an object. Let's have some fun with irb:
[chrisa@doppio ~]$ irb
Can I convert it to an integer?
irb(main):001:0> nil.to_i
=> 0
Yep. At least it's 0. How 'bout a string?
irb(main):002:0> nil.to_s
=> ""
Of course, it's an object, silly! And an array, etc.
irb(main):003:0> nil.to_a
=> []
This next one is really cool. Let's say you got this object reference back from ActiveRecord using MyModel.find(:first). Since you've dutifully followed the convention, you expect to get it's accession number by dereferencing the automagic property "id":
irb(main):004:0> nil.id
(irb):2: warning: Object#id will be deprecated; use Object#object_id
=> 4
Love that deprecation warning. We all hunt down and fix those right away, right? I spent a pleasant afternoon staring at the source of that one. Remember to read those web server logs carefully, kids!
Fun with Ruby Conditional Evaluation
The first in a series of postings for the unwary Ruby Newbie (Rubie?).
Is a numeric value of 0 true or false? To a C or Perl programmer it's obviously false. In Java it's a trick question (try it). But even those who've never programmed in C or C++ seem to share a general expectation of "false, duh."
What about Ruby? It's easy enough to find out. We'll use irb (for "interactive ruby") to put the question to the interpreter directly:
[chrisa@doppio ~]$ irb
irb(main):001:0> if (0) then puts "true" end
true
Which is to say that any numeric value will test true. How about an empty string?
irb(main):001:0> if ("") then puts "true" end
(irb):1: warning: string literal in condition
true
Well, that's a little bit friendlier but still, I can see getting quietly horked by that one. Contrast this with Perl's behavior. The interactive debugger is a bit less friendly, so we just execute the test directly from the command line:
[chrisa@doppio ~]$ perl -e 'if (0) {print "true\n"} ;'
[chrisa@doppio ~]$
[chrisa@doppio ~]$ perl -e 'if ("") {print "true\n"} ;'
[chrisa@doppio ~]$
Doesn't quite match the "Principle of Least Astonishment" pattern...
Is a numeric value of 0 true or false? To a C or Perl programmer it's obviously false. In Java it's a trick question (try it). But even those who've never programmed in C or C++ seem to share a general expectation of "false, duh."
What about Ruby? It's easy enough to find out. We'll use irb (for "interactive ruby") to put the question to the interpreter directly:
[chrisa@doppio ~]$ irb
irb(main):001:0> if (0) then puts "true" end
true
Which is to say that any numeric value will test true. How about an empty string?
irb(main):001:0> if ("") then puts "true" end
(irb):1: warning: string literal in condition
true
Well, that's a little bit friendlier but still, I can see getting quietly horked by that one. Contrast this with Perl's behavior. The interactive debugger is a bit less friendly, so we just execute the test directly from the command line:
[chrisa@doppio ~]$ perl -e 'if (0) {print "true\n"} ;'
[chrisa@doppio ~]$
[chrisa@doppio ~]$ perl -e 'if ("") {print "true\n"} ;'
[chrisa@doppio ~]$
Doesn't quite match the "Principle of Least Astonishment" pattern...
Subscribe to:
Posts (Atom)