module Bitroleable

Constants

VERSION

Public Instance Methods

roleable(*attrs) click to toggle source
# File lib/bitroleable.rb, line 7
def roleable(*attrs)
  column = nil
  roles_list = []
  roles_options = HashWithIndifferentAccess.new({multi: false})
  incorrect_params_message = 'Incorrect number of parameters (ex: roleable :roles, [:admin, :user])'
  incorrect_options_message = 'Incorrect roles options. Possible options: :multi - support multi roles'
  attrs.each_with_index do |attr, index|
    fail StandardError, incorrect_params_message if index > 2
    column = attr.to_sym if index == 0 && (attr.is_a?(String) || attr.is_a?(Symbol))
    roles_list = attr.map(&:to_sym) if index == 1 && attr.is_a?(Array)
    roles_options = roles_options.merge(attr) if index == 2 && attr.is_a?(Hash)
  end
  fail StandardError, incorrect_params_message if column.nil? || roles_list.empty?
  fail StandardError, incorrect_options_message unless roles_options.keys.map(&:to_sym).sort == [:multi]

  roles_list.each do |role|
    define_method("#{role}?".to_sym) do
      role?(role)
    end

    define_method("#{role}!".to_sym) do
      if roles_options[:multi]
        self[column] |= self.class.role_value(role)
      else
        self[column] = self.class.role_value(role)
      end
      save!
    end

    define_method("#{column}=".to_sym) do |value|
      return self[column] = 0 if value.blank?
      if roles_options[:multi]
        roles = value.is_a?(Array) ? value : [value]
        self[column] = 0
        roles.each do |role_name|
          self[column] |= self.class.role_value(role_name)
        end
      else
        self[column] = self.class.role_value(value)
      end
    end

    define_method("#{column}".to_sym) do
      if roles_options[:multi]
        return [] if self[column].nil? || self[column] == 0
        result = []
        roles_list.each do |role_name|
          result << role_name if self[column] & self.class.role_value(role_name) > 0
        end
        result
      else
        return nil if self[column].nil? || self[column] == 0
        roles_list.each do |role_name|
          return role_name if self[column] == self.class.role_value(role_name)
        end
        nil
      end
    end
  end

  define_method(:role?) do |role|
    self[column] & self.class.role_value(role) > 0
  end

  define_singleton_method :role_value do |role|
    index = roles_list.index(role.to_sym)
    fail StandardError, "The role #{role} doesn't exist" if index.nil?
    2**index
  end

  define_singleton_method :roles_list do
    roles_list
  end

  define_singleton_method :role? do |role|
    roles_list.include?(role.to_sym)
  end

  define_singleton_method :where_role do |*roles|
    roles = roles.flatten.compact.map(&:to_sym)
    tbl_column = "#{self.table_name}.#{column}"
    query = roles.map { |role| "(#{tbl_column} & %d > 0)" % role_value(role) }.join(' OR ')
    where(query)
  end
end