Reputation: 405
I'm having problems working with a mysql bit in Rails and ActiveRecord. We store a bit for the published-state of Localities.
`published` bit(1) NOT NULL
I scaffolded it as published:binary
in rails.
Locality.first.published
returns "\x01"
.
How do I get rails to treat this field as a boolean?
There is a staled Ticket but hacking ActiveRecord is not really an option. https://rails.lighthouseapp.com/projects/8994/tickets/6102-activerecord-boolean-support-with-bit1-mysql-data-type
Upvotes: 8
Views: 2544
Reputation: 21374
Update for Rails 5: the new attributes API is made for handling situations just like this. First you define a subclass of ActiveRecord::Type::Value
that handles deserialize
ing from bit to boolean, and cast
ing back from boolean to bit:
module Values
class BitBoolean < ActiveRecord::Type::Value
BIT_FALSE = "\x00"
BIT_TRUE = "\x01"
def cast(value)
value ? BIT_TRUE : BIT_FALSE
end
def deserialize(value)
value == BIT_TRUE
end
end
end
Then you define the attribute on your model with the attribute
helper:
class MyModel < ApplicationRecord
attribute :published, Values::BitBoolean.new
end
Upvotes: 4
Reputation: 1139
Thank you for your help @Mattherick.
With your help I have built something easier:
def highlight
self.read_attribute(:highlight) == "\x01" ? true : false
end
def highlight=(value)
content_value = (value == false || value == 0 || value == "0") ? "\x00" : "\x01"
self.write_attribute(:highlight,content_value)
end
The highlight
is the name of the field stored as BIT
on database. And this solution also works with checkbox
on view without the need of changes:
<div class="field">
<%= f.label :highlight %>
<%= f.check_box :highlight %>
</div>
Upvotes: 1
Reputation: 11637
Heres an extension method based on @Mattherick's answer above:
lib/extensions/active_record/bit_boolean.rb
module Extensions::ActiveRecord
module BitBoolean
extend ActiveSupport::Concern
class_methods do
def alias_bit_to_boolean(attribute)
define_method("#{attribute}?") do
self.send(attribute) == "\x01" ? true : false
end
end
end
end
end
ActiveRecord::Base.send(:include, Extensions::ActiveRecord::BitBoolean)
and require in an initializer:
config/initializers/extensions.rb
require File.join(Rails.root, 'lib/extensions/active_record/bit_boolean.rb')
Which then can be used:
class Locality < ActiveRecord::Base
alias_bit_to_boolean :published
end
This will produce a locality.published?
method.
Upvotes: 1
Reputation: 4375
You can overwrite the attribute reader of your published attribute:
class Locality < ActiveRecord::Base
# overwrite the attribute reader of the published attribute
def published
self.read_attribute(:published) == "\x01" ? true : false
end
end
UPDATE
Or generate a method for your boolean return value
class Locality < ActiveRecord::Base
def to_boolean
self.published == "\x01" ? true : false
end
end
So you can call:
Locality.first.published.to_boolean => true || false
But I think the first solution (overwriting attribute reader) is better.
Upvotes: 6