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 41
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 and column.column[:type])
  begin
    if search_ui && self.respond_to?("condition_for_#{search_ui}_type")
      self.send("condition_for_#{search_ui}_type", column, value, like_pattern)
    else
      unless column.search_sql.instance_of? Proc
        case search_ui
          when :boolean, :checkbox
            {column.search_sql => active_scaffold_config.model.db.send(:typecast_value_boolean, 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
            {column.search_sql => Array(value)}
          else
            if column.column.nil? || column.column[:type] == :string
              column.search_sql.ilike(like_pattern.sub('?', value))
            else
              {column.search_sql => active_scaffold_config.model.new.send(:typecast_value, column.name, value)}
            end
        end
      else
        column.search_sql.call(value)
      end
    end
  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 156
def condition_for_datetime(column, value, like_pattern = nil)
  conversion = column.column[:type] == :date ? :to_date : :to_time
  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
    column.search_sql.sql_expr <= to_value.to_s(:db)
  elsif !to_value
    column.search_sql.sql_expr >= from_value.to_s(:db)
  else
    {column.search_sql => 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 176
def condition_for_null_type(column, value, like_pattern = nil)
  case value.to_sym
  when :null
    {column.search_sql => nil}
  when :not_null
    ~{column.search_sql => nil}
  else
    nil
  end
end
condition_for_numeric(column, value) click to toggle source
# File lib/active_scaffold/finder.rb, line 81
def condition_for_numeric(column, value)
  if !value.is_a?(Hash)
    {column.search_sql => condition_value_for_numeric(column, value)}
  elsif value[:from].blank? or not ActiveScaffold::Finder::NumericComparators.include?(value[:opt])
    nil
  elsif value[:opt] == 'BETWEEN'
    {column.search_sql => condition_value_for_numeric(column, value[:from])..condition_value_for_numeric(column, value[:to])}
  else
    Sequel::SQL::PlaceholderLiteralString.new("? #{value[:opt]} ?", [column.search_sql, 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 93
def condition_for_range(column, value, like_pattern = nil)
  if !value.is_a?(Hash)
    if column.column.nil? || column.column[:type] == :string
      column.search_sql.ilike(like_pattern.sub('?', value))
    else
      {column.search_sql => active_scaffold_config.model.new.send(:typecast_value, column.name, 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])
    column.search_sql.ilike(value[:opt].sub('?', value[:from]))
  elsif value[:opt] == 'BETWEEN'
    {column.search_sql => value[:from]..value[:to]}
  elsif ActiveScaffold::Finder::NumericComparators.include?(value[:opt])
    Sequel::SQL::PlaceholderLiteralString.new("? #{value[:opt]} ?", [column.search_sql, 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 172
def condition_for_record_select_type(column, value, like_pattern = nil)
  {column.search_sql => value}
end
condition_value_for_datetime(value, conversion = :to_time) click to toggle source
# File lib/active_scaffold/finder.rb, line 115
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)
    value.send(conversion)
  elsif conversion == :to_date
    Date.strptime(value, I18n.t('date.formats.default')) rescue nil
  else
    parts = Date._parse(value)
    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?}"
    time = DateTime.strptime(value, format)
    time = Time.zone.local_to_utc(time) unless parts[:offset]
    time.in_time_zone.send(conversion) rescue nil
  end unless value.nil? || value.blank?
end
condition_value_for_numeric(column, value) click to toggle source
# File lib/active_scaffold/finder.rb, line 132
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 active_scaffold_config.model.db.send(:typecast_value_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 7
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

  # for typecasting purposes
  obj = active_scaffold_config.model.new
  orig_raise_on_typecast_failure = active_scaffold_config.model.raise_on_typecast_failure
  active_scaffold_config.model.raise_on_typecast_failure = true

  result = tokens.collect do |token|
    pattern = like_pattern.sub('?', token)
    columns.collect do |column|
      if column.column.nil? or column.column[:type] == :string
        column.search_sql.ilike(pattern)
      else
        begin
          {column.search_sql => obj.send(:typecast_value, column.name, token)}
        rescue Sequel::InvalidValue
        end
      end
    end.compact.inject {|a,b| (a | b)}
  end.inject {|a,b| (a & b)}

  # restore original setting
  active_scaffold_config.model.raise_on_typecast_failure = orig_raise_on_typecast_failure

  result
end
i18n_number_to_native_format(value) click to toggle source
# File lib/active_scaffold/finder.rb, line 144
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 !~ /\.\d{3}$/
    value.gsub(/[^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 187
def like_pattern(text_search)
  case text_search
    when :full then '%?%'
    when :start then '?%'
    when :end then '%?'
    else '?'
  end
end