module KellyLSB::BitSwitch::ClassMethods

Public Instance Methods

bitswitch(column, hash = {}) click to toggle source

Generate switch methods

# File lib/bitswitch.rb, line 237
def bitswitch(column, hash = {})

  # Set column method name
  columne = column.to_s + '='

  # Instance methods
  send(:include, Module.new {

    # BitSwitch access method
    send(:define_method, column) do |*args|
      val = read_attribute(column)

      # If nil make 0
      val = 0 if val.nil?

      # Make sure the column value is a Fixnum
      unless val.is_a?(Fixnum)
        raise KellyLSB::BitSwitch::Error,
          "Column: #{column} is not an integer!"
      end

      # Convert the Fixnum to a BitSwitch
      val = val.to_switch hash

      # Return the value of a specific key if requested
      return val[args.first] unless args[0].nil?

      # Return the switch
      val
    end

    # BitSwitch set method
    send(:define_method, columne) do |args|
      val = read_attribute(column)

      # If nil make 0
      val = 0 if val.nil?

      # Get the input data
      if args.is_a?(Array)
        input = args[0]
        truncate = args[1]
      else
        input = args
        truncate = false
      end

      # Make sure the value is an integer
      unless val.is_a?(Fixnum)
        raise KellyLSB::BitSwitch::Error,
          "Column: #{column} is not an integer!"
      end

      # Make sure the first input is a hash
      unless input.is_a?(Hash)
        raise KellyLSB::BitSwitch::Error,
          "Input: We are expecting at least one argument that is a Hash"
      end

      # Convert Fixnum -> BitSwitch -> Hash
      val = val.to_switch(hash).to_hash

      # Convert all keys to symbols
      input.delete_if do |key, val|
        if key.is_a?(String)
          input[key.to_sym] = val
          true
        else
          false
        end
      end

      # If we are requested to truncate other keys
      if truncate == true

        # Get list of unset keys and set them to false
        remove = val.keys.collect(&:to_sym) - input.keys.collect(&:to_sym)
        remove.each { |key| input[key] = false }
      end

      # Merge in the changes then convert to BitSwitch
      val = val.merge(input).to_switch(hash)

      # Dont save if this is a new model
      return false if new_record?

      # Write the updated value
      update_column(column, val.to_i)

      # Return the switch
      return self.send(column)
    end
  })

  # Scoping methods
  send(:extend, Module.new {
    send(:define_method, column) do |*args|

      # Require at least one argument
      if args.empty?
        raise KellyLSB::BitSwitch::Error,
          "Missing arguments! We were expecing at least one label or bit to query by."
      end

      # Invert the label hash
      bits = hash.invert

      # Type of condition
      if args.first.is_a?(String) && ['AND', 'OR'].include?(args.first.to_s.upcase)
        delimiter = args.shift
      else
        delimiter = 'AND'
      end

      # Empty conditions
      conditions = Array.new

      # Build conditions
      if args.first.is_a?(Hash)
        args.first.each do |slug, tf|
          bit = bits[slug.to_s]
          conditions << "POW(2, #{bit}) & #{self.table_name}.#{column}" + (tf ? ' > 0' : ' <= 0')
        end
      else
        args.each do |slug|
          bit = bits[slug.to_s]
          conditions << "POW(2, #{bit}) & #{self.table_name}.#{column} > 0"
        end
      end

      # If we have query conditions go ahead and return the updated scope
      return self.where(conditions.join(" #{delimiter} ")) unless conditions.empty?

      # Return self
      self
    end

    send(:define_method, "#{column}_labels") do |*args|
      hash.values
    end
  })
end