module Incline::Extensions::ActiveRecordBase::ClassMethods

Additional static methods for the ActiveRecord::Base class.

Public Instance Methods

[](*value) click to toggle source

Gets one item based on the value.

Always returns the first item found or nil if there were no matches.

See get_id for how the value is processed. The only difference is numeric IDs must be valid for the model to be returned.

# Assuming this data set:
#   ID   |  NAME
# -------|--------------------
#   1    |  Juliet
#   2    |  Romeo
#   3    |  4321
#   4    |  1234

[5]           # returns nil
[1,3,5,7]     # returns { 1 => Juliet }
['Romeo']     # returns { 2 => Romeo }
['#1234']     # returns { 4 => 1234 }
# File lib/incline/extensions/active_record_base.rb, line 222
def [](*value)
  get(*value)&.first
end
default_sort_method() click to toggle source

Gets the default sort method for this model.

In order of preference this will be 'code', 'name', or 'to_s'.

Specifying a search_attribute before using this method will change this behavior.

class MyModel
  attr_accessor :id, :name, :employee_id
end
MyModel.default_sort_method   # 'name'

class MyModel
  attr_accessor :id, :name, :employee_id
  search_attribute :employee_id
end
MyModel.default_sort_method   # 'employee_id'
# File lib/incline/extensions/active_record_base.rb, line 74
def default_sort_method
  @default_sort_method ||= search_attributes.find{|a| attribute_names.include?(a)} || 'to_s'
end
get(*value) click to toggle source

Gets one or more items based on the value.

Always returns an array of items or nil if no results were found.

See get_id for how the value is processed. The only difference is numeric IDs must be valid for the models to be returned.

# Assuming this data set:
#   ID   |  NAME
# -------|--------------------
#   1    |  Juliet
#   2    |  Romeo
#   3    |  4321
#   4    |  1234

get(5)        # returns nil
get(1,3,5,7)  # returns [ { 1 => Juliet }, { 3 => 4321 } ]
get('Romeo')  # returns [ { 2 => Romeo } ]
get('#1234')  # returns [ { 4 => 1234 } ]
# File lib/incline/extensions/active_record_base.rb, line 182
def get(*value)
  return nil if value.blank?

  value = value.first if value.count == 1

  return value if value.class == self

  result = search_for(value)
  first_sort = search_attributes.find{|a| attribute_names.include?(a)}
  if first_sort
    result = result.order(first_sort, :id)
  else
    result = result.order(:id)
  end
  result = result.to_a
  return nil if result.blank?
  result
end
get_id(*value) click to toggle source

Gets the ID(s) based on the specified value.

If a valid model is provided, then the :id attribute is returned.

An integer simply gets returned. No validation is done to ensure it is a valid ID. A string representing an integer gets converted and returned. No validation is done.

get_id(1234) => 1234
get_id('1234') => 1234

All further searches are against lowercased :code or :name attributes if either is a valid attribute. You can use the search_attribute method to add another attribute to compare against.

A symbol is converted to a string and a humanized string.

get_id(:one_two_three) => searches for 'one_two_three' or 'one two three'.

Strings are lowercased and compared against lowercased :code or :name if either of those is a valid attribute.

get_id('Alpha Bravo') => searches for 'alpha bravo'

If a string begins with a # and contains only numbers after that, then it will allow you to search for a non-ID field that only contains numbers. The value is searched as both the supplied value and only the numeric digits.

get_id('#1234') => searches for '#1234' or '1234'

Arrays are mapped as above.

In all cases, if one result is found that value is returned, if more than one is found then an array is returned, and if no results are found, nil is returned. If the model does not include an :id attributes, then nil will always be returned because there can be no valid results.

# Assuming this data set:
#   ID   |  NAME
# -------|--------------------
#   1    |  Juliet
#   2    |  Romeo
#   3    |  4321
#   4    |  1234

get_id(1,3,5,7,'Romeo','#1234') # returns [ 1, 3, 5, 7, 2, 4 ]
# 1, 3, 5, & 7 are not verified, but :name is searched for "romeo", "#1234", and "1234" to add 2 & 4.

get_id('#4321') # returns 3 since there is only one valid result.
# File lib/incline/extensions/active_record_base.rb, line 141
def get_id(*value)
  return nil if value.blank?
  return nil unless attribute_names.include?('id')

  value = value.first if value.count == 1

  return value.map{|v| get_id(v)} if value.is_a?(::Array)

  return value.id if value.class == self
  return value if value.is_a?(::Integer)
  return value.to_i if value.to_s =~ /\A\d+\z/

  result = search_for(value).order(:id).pluck(:id).to_a

  return nil if result.blank?
  return result.first if result.count == 1

  result
end
search_attribute(attrib) click to toggle source

Specifies an attribute to search in the get_id, get, and [] methods.

The default attributes to search are 'code' and 'name'. If you specify an additional attribute it will be prefixed to the list. This affects sorting behavior, but not necessarily the search behavior since if the 'code' or 'name' attributes are also present, they will be searched as well.

class MyModel
  attr_accessor :id, :name, :employee_id
  search_attribute :employee_id
end
# File lib/incline/extensions/active_record_base.rb, line 90
def search_attribute(attrib)
  unless attrib.blank?
    attrib = attrib.to_s.strip
    search_attributes.insert(0, attrib) unless search_attributes.include?(attrib)
  end
end

Private Instance Methods

search_attributes() click to toggle source
# File lib/incline/extensions/active_record_base.rb, line 228
def search_attributes
  @search_attributes ||= %w(code name)
end
search_for(values) click to toggle source
# File lib/incline/extensions/active_record_base.rb, line 232
def search_for(values)
  # make sure we have an array that we can safely modify.
  values = values.is_a?(::Array) ? values.dup : [ values ]
  values.reject!{|v| v.blank?}
  values.flatten!

  # return a query that will return no records.
  return self.where('(1 = 0)') if values.blank?

  # extract potential IDs.
  ids =       values
                  .select{|v| !v.is_a?(::Symbol) && v.to_s =~ /\A\d+\z/}
                  .map{|v| v.to_i}

  # convert models to IDs to force reloading them.
  ids +=      values
                  .select{|v| v.class == self}
                  .map{|v| v.id}

  # only unique IDs are kept.
  ids.uniq!

  # and then extract string/symbol values for further searching.
  # purely numeric values and blank values are discarded.
  values =    values
                  .select{|v| (v.is_a?(::String) && !v.blank? && !(v =~ /\A\d+\z/)) || (v.is_a?(::Symbol) && !v.to_s.blank?)}
                  .map{|v| (v.is_a?(::String) && v =~ /\A#\d+\z/) ? [ v, v[1..-1] ] : v}
                  .map{|v| v.is_a?(::Symbol) ? [ v.to_s.downcase, v.to_s.humanize.downcase ] : v.to_s.humanize.downcase}
                  .flatten

  # only unique values are kept.
  values.uniq!

  # get the attributes to search.
  attribs =   search_attributes
                  .select{|attr| attribute_names.include?(attr)}
                  .map{|a| a.to_s.gsub("'", "''").gsub('"','""') }

  # do a sanity check.
  raise 'No valid values to search for.' if ids.blank? && values.blank?
  raise 'No ID attribute to search.' unless ids.blank? || attribute_names.include?('id')
  raise 'No attributes to search.' unless values.blank? || attribs.any?

  filters = []
  filter_values = []
  tbl = table_name.to_s.gsub("'", "''").gsub('"','""')

  # include the ID filters.
  ids.each do |id|
    filters << "(\"#{tbl}\".\"id\" = ?)"
    filter_values << id
  end

  # include the value filters.
  values.each do |value|
    attribs.each do |attr|
      filters << "(LOWER(\"#{tbl}\".\"#{attr}\") = ?)"
      filter_values << value
    end
  end

  filters = filters.join(' OR ')

  self.where(filters, *filter_values)
end