module ActiveScaffold::Finder::ClassMethods

Public Instance Methods

condition_for_column(column, value, text_search = :full) click to toggle source

Generates an SQL condition for the given ActiveScaffold column based on that column’s database type (or form_ui … for virtual columns?). TODO: this should reside on the column, not the controller

# File lib/active_scaffold/finder.rb, line 39
def condition_for_column(column, value, text_search = :full)
  like_pattern = like_pattern(text_search)
  if self.respond_to?("condition_for_#{column.name}_column")
    return self.send("condition_for_#{column.name}_column", column, value, like_pattern)
  end
  return unless column and column.search_sql and not value.blank?
  search_ui = column.search_ui || column.column.try(:type)
  begin
    sql, *values = if search_ui && self.respond_to?("condition_for_#{search_ui}_type")
      self.send("condition_for_#{search_ui}_type", column, value, like_pattern)
    else
      if column.search_sql.instance_of? Proc
        column.search_sql.call(value)
      else
        case search_ui
        when :boolean, :checkbox
          ["%{search_sql} = ?", column.column ? column.column.type_cast(value) : value]
        when :integer, :decimal, :float
          condition_for_numeric(column, value)
        when :string, :range
          condition_for_range(column, value, like_pattern)
        when :date, :time, :datetime, :timestamp
          condition_for_datetime(column, value)
        when :select, :multi_select, :country, :usa_state, :chosen, :multi_chosen
          ["%{search_sql} in (?)", Array(value)]
        else
          if column.column.nil? || column.column.text?
            ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
          else
            ["%{search_sql} = ?", column.column.type_cast(value)]
          end
        end
      end
    end
    return nil unless sql

    conditions = [column.search_sql.collect { |search_sql| sql % {:search_sql => search_sql} }.join(' OR ')]
    conditions += values*column.search_sql.size if values.present?
    conditions
  rescue Exception => e
    logger.error Time.now.to_s + "#{e.inspect} -- on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{self.name}"
    raise e
  end
end
condition_for_datetime(column, value, like_pattern = nil) click to toggle source
# File lib/active_scaffold/finder.rb, line 181
def condition_for_datetime(column, value, like_pattern = nil)
  conversion = datetime_conversion_for_condition(column)
  from_value = condition_value_for_datetime(value[:from], conversion)
  to_value = condition_value_for_datetime(value[:to], conversion)

  if from_value.nil? and to_value.nil?
    nil
  elsif !from_value
    ["%{search_sql} <= ?", to_value.to_s(:db)]
  elsif !to_value
    ["%{search_sql} >= ?", from_value.to_s(:db)]
  else
    ["%{search_sql} BETWEEN ? AND ?", from_value.to_s(:db), to_value.to_s(:db)]
  end
end
condition_for_null_type(column, value, like_pattern = nil) click to toggle source
# File lib/active_scaffold/finder.rb, line 205
def condition_for_null_type(column, value, like_pattern = nil)
  case value.to_sym
  when :null
    ["%{search_sql} is null", []]
  when :not_null
    ["%{search_sql} is not null", []]
  else
    nil
  end
end
condition_for_numeric(column, value) click to toggle source
# File lib/active_scaffold/finder.rb, line 84
def condition_for_numeric(column, value)
  if !value.is_a?(Hash)
    ["%{search_sql} = ?", condition_value_for_numeric(column, value)]
  elsif ActiveScaffold::Finder::NullComparators.include?(value[:opt])
    condition_for_null_type(column, value[:opt])
  elsif value[:from].blank? or not ActiveScaffold::Finder::NumericComparators.include?(value[:opt])
    nil
  elsif value[:opt] == 'BETWEEN'
    ["(%{search_sql} BETWEEN ? AND ?)", condition_value_for_numeric(column, value[:from]), condition_value_for_numeric(column, value[:to])]
  else
    ["%{search_sql} #{value[:opt]} ?", condition_value_for_numeric(column, value[:from])]
  end
end
condition_for_range(column, value, like_pattern = nil) click to toggle source
# File lib/active_scaffold/finder.rb, line 98
def condition_for_range(column, value, like_pattern = nil)
  if !value.is_a?(Hash)
    if column.column.nil? || column.column.text?
      ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
    else
      ["%{search_sql} = ?", column.column.type_cast(value)]
    end
  elsif ActiveScaffold::Finder::NullComparators.include?(value[:opt])
    condition_for_null_type(column, value[:opt], like_pattern)
  elsif value[:from].blank?
    nil
  elsif ActiveScaffold::Finder::StringComparators.values.include?(value[:opt])
    ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", value[:opt].sub('?', value[:from])]
  elsif value[:opt] == 'BETWEEN'
    ["(%{search_sql} BETWEEN ? AND ?)", value[:from], value[:to]]
  elsif ActiveScaffold::Finder::NumericComparators.include?(value[:opt])
    ["%{search_sql} #{value[:opt]} ?", value[:from]]
  else
    nil
  end
end
condition_for_record_select_type(column, value, like_pattern = nil) click to toggle source
# File lib/active_scaffold/finder.rb, line 197
def condition_for_record_select_type(column, value, like_pattern = nil)
  if value.is_a?(Array)
    ["%{search_sql} IN (?)", value]
  else
    ["%{search_sql} = ?", value]
  end
end
condition_value_for_datetime(value, conversion = :to_time) click to toggle source
# File lib/active_scaffold/finder.rb, line 120
def condition_value_for_datetime(value, conversion = :to_time)
  if value.is_a? Hash
    Time.zone.local(*[:year, :month, :day, :hour, :minute, :second].collect {|part| value[part].to_i}) rescue nil
  elsif value.respond_to?(:strftime)
    if conversion == :to_time
      # Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
      # https://github.com/rails/rails/pull/2453
      value.to_time.in_time_zone
    else
      value.send(conversion)
    end
  elsif conversion == :to_date
    Date.strptime(value, I18n.t('date.formats.default')) rescue nil
  else
    parts = Date._parse(value)
    format = I18n.translate 'time.formats.picker', :default => '' if ActiveScaffold.js_framework == :jquery
    if format.blank?
      time_parts = [[:hour, '%H'], [:min, '%M'], [:sec, '%S']].collect {|part, format_part| format_part if parts[part].present?}.compact
      format = "#{I18n.t('date.formats.default')} #{time_parts.join(':')} #{'%z' if parts[:offset].present?}"
    else
      format += ' %z' if parts[:offset].present? && format !~ %r%z/
    end
    time = DateTime.strptime(value, format)
    time = Time.zone.local_to_utc(time).in_time_zone unless parts[:offset]
    time = time.send(conversion) unless conversion == :to_time
    time
  end unless value.nil? || value.blank?
end
condition_value_for_numeric(column, value) click to toggle source
# File lib/active_scaffold/finder.rb, line 149
def condition_value_for_numeric(column, value)
  return value if value.nil?
  value = i18n_number_to_native_format(value) if [:i18n_number, :currency].include?(column.options[:format])
  case (column.search_ui || column.column.type)
  when :integer   then value.to_i rescue value ? 1 : 0
  when :float     then value.to_f
  when :decimal   then ActiveRecord::ConnectionAdapters::Column.value_to_decimal(value)
  else
    value
  end
end
create_conditions_for_columns(tokens, columns, text_search = :full) click to toggle source

Takes a collection of search terms (the tokens) and creates SQL that searches all specified ActiveScaffold columns. A row will match if each token is found in at least one of the columns.

# File lib/active_scaffold/finder.rb, line 11
def create_conditions_for_columns(tokens, columns, text_search = :full)
  # if there aren't any columns, then just return a nil condition
  return unless columns.length > 0
  like_pattern = like_pattern(text_search)

  tokens = [tokens] if tokens.is_a? String

  where_clauses = []
  columns.each do |column|
    Array(column.search_sql).each do |search_sql|
      where_clauses << "#{search_sql} #{(column.column.nil? || column.column.text?) ? ActiveScaffold::Finder.like_operator : '='} ?"
    end
  end
  phrase = where_clauses.join(' OR ')

  tokens.collect do |value|
    columns.inject([phrase]) do |condition, column|
      Array(column.search_sql).size.times do
        condition.push((column.column.nil? || column.column.text?) ? like_pattern.sub('?', value) : column.column.type_cast(value))
      end
      condition
    end
  end
end
datetime_conversion_for_condition(column) click to toggle source
# File lib/active_scaffold/finder.rb, line 173
def datetime_conversion_for_condition(column)
  if column.column
    column.column.type == :date ? :to_date : :to_time
  else
    :to_time
  end
end
i18n_number_to_native_format(value) click to toggle source
# File lib/active_scaffold/finder.rb, line 161
def i18n_number_to_native_format(value)
  native = '.'
  delimiter = I18n.t('number.format.delimiter')
  separator = I18n.t('number.format.separator')
  return value if value.blank? || !value.is_a?(String)
  unless delimiter == native && !value.include?(separator) && value !~ %r\.\d{3}$/
    value.gsub(%r[^0-9\-#{I18n.t('number.format.separator')}]/, '').gsub(I18n.t('number.format.separator'), native)
  else
    value
  end
end
like_pattern(text_search) click to toggle source
# File lib/active_scaffold/finder.rb, line 216
def like_pattern(text_search)
  case text_search
    when :full then '%?%'
    when :start then '?%'
    when :end then '%?'
    else '?'
  end
end