Module: Bitten

Defined in:
lib/bitten.rb,
lib/bitten/version.rb

Constant Summary collapse

TRUE_VALUES =

Lower-case string values that should be considered truthy. This allows us to handle both actual true and false values as well as truthy string values coming from HTML forms.

%w[true 1 t].freeze
VERSION =
'0.0.1'

Instance Method Summary collapse

Instance Method Details

#has_bits(*flags) ⇒ Object

Define the different flags to be stored in the host model's bits column. Every flag given gets its own query method (e.g. published?) and setter method (e.g. published=). All flags are stored in a single integer column in the host object.

Note that you can add more flags to the model if you want. If you do, it will default to false unless explicitly set otherwise. Do make sure to only add new flags to the end of the list, as order is vitally important.

Note: this assumes the host object defines bits and bits=, with a default value of 0. This does not have to be an ActiveRecord attribute, any attr_accessor will do, as longs as the default value is set.

To use the convenience scopes, the host object has to be an ActiveRecord object in order to define the class-level scope method. To disable these, pass the scopes: false option.

Examples:

Basic usage

ActiveRecord::Migration.create_table do |t|
  t.integer :bits, null: false, default: 0
end

class User < ActiveRecord::Base
  include Bitten
  has_bits :editor, :author, :reviewer
end

user = User.new editor: true
user.editor? # => true
user.author? # => false
user.reviewer? # => false
user.author = true
user.author? # => true
user.save
User.author # => [user]
User.editor # => [user]
User.reviewer # => []
User.not_reviewer # => [user]

Provide options

class User < ActiveRecord::Base
  include Bitten
  has_bits :admin, :manager, scopes: false
end


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/bitten.rb', line 53

def has_bits(*flags)
  options = flags.last.is_a?(Hash) ? flags.pop : {}
  options = { scopes: true, column: 'bits' }.merge(options)

  if options[:scopes]
    flags.each_with_index do |flag, index|
      define_scopes flag, options[:column], 1 << index
    end
  end

  mod = Module.new do
    define_method :bitten_bits do
      send options[:column]
    end

    define_method :bitten_bits= do |val|
      send :"#{options[:column]}=", val
    end

    flags.each_with_index do |flag, index|
      bit = 1 << index

      define_method :"#{flag}?" do
        bitten_bits & bit != 0
      end

      define_method :"#{flag}=" do |value|
        if TRUE_VALUES.include? value.to_s.downcase
          self.bitten_bits |= bit
        else
          self.bitten_bits &= ~bit
        end
      end
    end
  end

  include mod
end