module SmartEnum::ActiveRecordCompatibility::QueryMethods

Simulate ActiveRecord Query API

Constants

BIG_DECIMAL
BOOLEAN
INTEGER
STRING
SYMBOL

Public Instance Methods

all() click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 104
def all
  values
end
cast_primary_key(id_input) click to toggle source

Given an “id-like” parameter (usually the first argument to find), cast it to the same type used to index the PK lookup hash.

# File lib/smart_enum/active_record_compatibility.rb, line 173
def cast_primary_key(id_input)
  return if id_input == nil
  id_attribute = attribute_set[:id]
  fail "no :id attribute defined on #{self}" if !id_attribute
  types = id_attribute.types
  # if the type is already compatible, return it.
  return id_input if types.any? { |t| id_input.instance_of?(t) }
  case types
  when INTEGER then Integer(id_input)
  when STRING  then id_input.to_s
  when SYMBOL  then id_input.to_s.to_sym
  else
    fail "incompatible type: got #{id_input.class}, need #{types.inspect} or something castable to that"
  end
end
cast_query_attrs(raw_attrs) click to toggle source

Ensure that the attrs we query by are compatible with the internal types, casting where possible. This allows us to e.g.

find_by(id: '1', key: :blah)

even when types differ like we can in ActiveRecord.

# File lib/smart_enum/active_record_compatibility.rb, line 137
def cast_query_attrs(raw_attrs)
  SmartEnum::Utilities.symbolize_hash_keys(raw_attrs).each_with_object({}) do |(k, v), new_attrs|
    if v.instance_of?(Array)
      fail "SmartEnum can't query with array arguments yet.  Got #{raw_attrs.inspect}"
    end
    if (attr_def = attribute_set[k])
      if attr_def.types.any?{|type| v.instance_of?(type) }
        # No need to cast
        new_attrs[k] = v
      elsif (v.nil? && attr_def.types != BOOLEAN)
        # Querying by nil is a legit case, unless the type is boolean
        new_attrs[k] = v
      elsif attr_def.types == STRING
        new_attrs[k] = String(v)
      elsif attr_def.types == INTEGER
        if v.instance_of?(String) && v.empty? # support querying by (id: '')
          new_attrs[k] = nil
        else
          new_attrs[k] = Integer(v)
        end
      elsif attr_def.types == BOOLEAN
        # TODO should we treat "f", 0, etc as false?
        new_attrs[k] = !!v
      elsif attr_def.types == BIG_DECIMAL
        new_attrs[k] = BigDecimal(v)
      else
        fail "Passed illegal query arguments for #{k}: #{v} is a #{v.class}, need #{attr_def.types} or a cast rule (got #{raw_attrs.inspect})"
      end
    else
      fail "#{k} is not an attribute of #{self}! (got #{raw_attrs.inspect})"
    end
  end
end
count() click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 124
def count
  values.count
end
find(id, raise_on_missing: true) click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 74
def find(id, raise_on_missing: true)
  self[cast_primary_key(id)].tap do |result|
    if !result && raise_on_missing
      fail ActiveRecord::RecordNotFound.new("Couldn't find #{self} with 'id'=#{id}")
    end
  end
end
find_by(uncast_attrs) click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 82
def find_by(uncast_attrs)
  attrs = cast_query_attrs(uncast_attrs)
  if attrs.size == 1 && attrs.has_key?(:id)
    return find(attrs[:id], raise_on_missing: false)
  end
  all.detect do |instance|
    instance.attributes.slice(*attrs.keys) == attrs
  end
end
find_by!(attrs) click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 92
def find_by!(attrs)
  find_by(attrs).tap do |result|
    if !result
      fail ActiveRecord::RecordNotFound.new("Couldn't find #{self} with #{attrs.inspect}")
    end
  end
end
first(num=nil) click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 108
def first(num=nil)
  if num
    values.first(num)
  else
    values.first
  end
end
last(num=nil) click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 116
def last(num=nil)
  if num
    values.last(num)
  else
    values.last
  end
end
none() click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 100
def none
  []
end
where(uncast_attrs) click to toggle source
# File lib/smart_enum/active_record_compatibility.rb, line 67
def where(uncast_attrs)
  attrs = cast_query_attrs(uncast_attrs)
  all.select do |instance|
    instance.attributes.slice(*attrs.keys) == attrs
  end.tap(&:freeze)
end