I was about to write a really long question asking how to do this, then
I figured it out. I figured I’d post it in case anyone else runs into
it.
Lets say you have the following hypothetical table associated with the
model Attendee:
create_table :attendees do |t|
t.column :name, :string
t.column :first_time?, :string, :limit => 3, :default => “no”
t.column :first_place?, :boolean, :default => false
end
I altered between using a string and a boolean for the sake of the
example. Chances are you’d use one or the other.
Now in Erb:
a = Attendee.new
=> #<Attendee:0x493421c @attributes={“first_place?”=>false,
“name”=>nil, “first_time?”=>“no”}, @new_record=true>
By convention, ActiveRecord automatically creates a “#{field_name}?”
method for each field in your table which returns true or false
depending on if the field is valued. So if we entered
a.methods
We’d see among the results “name” and “name?”, correlating to the name
column we created.
a.name?
=> falsea.name = “CDB”
=> “CDB”a.name?
=> true
However, ActiveRecord does not create these methods if your field ends
in with a “?”! In the list of methods mentioned above we would see only
“first_place?” and “first_time?”, not “first_place??” nor “first_time??”
as I expected. It turns out that ActiveRecord skips this step because it
sees there is already a method with this name.
a.first_place?
=> falsea.first_time?
=> “no”
The first example is misleading as it returns a boolean just as our
name? method does. This is because the field itself is a Boolean field.
In fact, there really is no distinction between the name? method that
ActiveRecord generates and the first_place? method that correlates to
our field.
a.first_place?.class
=> TrueClassa.first_place?.class.ancestors
=> [TrueClass, Object, Base64::Deprecated, Base64, Kernel]a.name?.class
=> TrueClassa.name?.class.ancestors
=> [TrueClass, Object, Base64::Deprecated, Base64, Kernel]
The tricky part here is that ActiveRecord, for some reason, won’t let
you set a new value to the first_time? and first_place? fields as you
would normally do.
a.first_place? = true
SyntaxError: compile error
(irb):69: syntax error, unexpected ‘=’, expecting $end
a.first_place? = true
^
from (irb):69
from :0a.first_time? = “yes”
SyntaxError: compile error
(irb):70: syntax error, unexpected ‘=’, expecting $end
a.first_time? = “yes”
^
from (irb):70
from :0
So how do we do it? Using an alternate accessor syntax:
a[“first_time?”]
=> “no”a[“first_time?”] = “yes”
=> “yes”a[“first_place?”]
=> truea[“first_place?”] = false
=> false
I hope that saves someone else a bit of sanity. It had me scratching my
head for a while. I’m not sure if this is a “bug” or a “feature”, but I
plan on reporting it just in case.
~ Chris