amiel/bitmasker

View on GitHub
lib/bitmasker/bitmask_scope.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Bitmasker
  class BitmaskScope
    include ActiveModel::AttributeMethods
    attribute_method_prefix 'with_'
    attribute_method_prefix 'with_any_'
    attribute_method_prefix 'without_'

    class_attribute :model_class
    class_attribute :field_name
    class_attribute :table_name
    class_attribute :mask_name
    class_attribute :bitmask_attributes

    def self.make(model_class, field_name, mask_name, bitmask_attributes)
      klass = Class.new(self) do
        define_attribute_methods [mask_name]

        def self.to_s
          "#{superclass}(#{model_class}##{field_name})"
        end
      end

      klass.model_class = model_class
      klass.table_name = model_class.table_name
      klass.field_name = field_name
      klass.mask_name = mask_name
      klass.bitmask_attributes = bitmask_attributes.stringify_keys

      klass
    end

    def bitmask(mask = 0)
      Bitmask.new(bitmask_attributes, mask)
    end

    # REVIEW: This (the unused _ attribute) tells me I have the design wrong
    def with_attribute(_, *attributes)
      # TODO: Test lots of databases
      bitmask_query attributes, "#{table_name}.#{field_name} & :mask = :mask"
    end

    def with_any_attribute(_, *attributes)
      # TODO: Test lots of databases
      bitmask_query attributes, "#{table_name}.#{field_name} & :mask <> 0"
    end

    def without_attribute(_, *attributes)
      # TODO: Test lots of databases
      bitmask_query attributes, "#{table_name}.#{field_name} & :mask = 0 OR #{table_name}.#{field_name} IS NULL"
    end

    private

    def bitmask_query(attributes, query)
      mask = bitmask
      mask.set_array(Array.wrap(attributes).flatten.map(&:to_s))

      return null_query if mask.to_i.zero?

      # TODO: Test lots of databases
      model_class.where(query, mask: mask.to_i)
    end

    def null_query
      model_class.where('1 = 0')
    end
  end
end