Rene Koller
Rene Koller

Reputation: 405

Rails / ActiveRecord working with mysql BIT

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.publishedreturns "\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

Answers (4)

Josh Justice
Josh Justice

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 deserializeing from bit to boolean, and casting 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

bonafernando
bonafernando

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

cweston
cweston

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

Matthias
Matthias

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

Related Questions