Module: Ransack::Constants

Defined in:
lib/ransack/constants.rb

Constant Summary collapse

OR =
'or'.freeze
AND =
'and'.freeze
CAP_SEARCH =
'Search'.freeze
SEARCH =
'search'.freeze
SEARCHES =
'searches'.freeze
ATTRIBUTE =
'attribute'.freeze
ATTRIBUTES =
'attributes'.freeze
COMBINATOR =
'combinator'.freeze
TWO_COLONS =
'::'.freeze
UNDERSCORE =
'_'.freeze
LEFT_PARENTHESIS =
'('.freeze
Q =
'q'.freeze
I =
'i'.freeze
DOT_ASTERIX =
'.*'.freeze
STRING_JOIN =
'string_join'.freeze
ASSOCIATION_JOIN =
'association_join'.freeze
STASHED_JOIN =
'stashed_join'.freeze
JOIN_NODE =
'join_node'.freeze
TRUE_VALUES =
[true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
FALSE_VALUES =
[false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
BOOLEAN_VALUES =
(TRUE_VALUES + FALSE_VALUES).freeze
AND_OR =
['and'.freeze, 'or'.freeze].freeze
IN_NOT_IN =
['in'.freeze, 'not_in'.freeze].freeze
SUFFIXES =
['_any'.freeze, '_all'.freeze].freeze
AREL_PREDICATES =
[
  'eq'.freeze, 'not_eq'.freeze,
  'matches'.freeze, 'does_not_match'.freeze,
  'lt'.freeze, 'lteq'.freeze,
  'gt'.freeze, 'gteq'.freeze,
  'in'.freeze, 'not_in'.freeze
].freeze
A_S_I =
['a'.freeze, 's'.freeze, 'i'.freeze].freeze
EQ =
'eq'.freeze
NOT_EQ =
'not_eq'.freeze
EQ_ANY =
'eq_any'.freeze
NOT_EQ_ALL =
'not_eq_all'.freeze
CONT =
'cont'.freeze
RANSACK_SLASH_SEARCHES =
'ransack/searches'.freeze
RANSACK_SLASH_SEARCHES_SLASH_SEARCH =
'ransack/searches/search'.freeze
DISTINCT =
'DISTINCT '.freeze
MAX_KEY_DEPTH =

The most _-separated segments a single condition or sort key may have before Ransack rejects it as invalid instead of parsing it. Working out whether a key is an attribute, an _and_/_or_ compound or a path through associations is superlinear in this count, and the key comes straight from the query string, so an unbounded one is a CPU-exhaustion denial of service (GHSA-j3f8-w227-4hh8). No real search key is this long: OR-ing every column of a very wide table stays well under it.

200
DERIVED_PREDICATES =
[
  [CONT, {
    arel_predicate: 'matches'.freeze,
    formatter: proc { |v| "%#{escape_wildcards(v)}%" }
    }
  ],
  ['not_cont'.freeze, {
    arel_predicate: 'does_not_match'.freeze,
    formatter: proc { |v| "%#{escape_wildcards(v)}%" }
    }
  ],
  ['i_cont'.freeze, {
    arel_predicate: 'matches'.freeze,
    formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" },
    case_insensitive: true
    }
  ],
  ['not_i_cont'.freeze, {
    arel_predicate: 'does_not_match'.freeze,
    formatter: proc { |v| "%#{escape_wildcards(v.downcase)}%" },
    case_insensitive: true
    }
  ],
  ['start'.freeze, {
    arel_predicate: 'matches'.freeze,
    formatter: proc { |v| "#{escape_wildcards(v)}%" }
    }
  ],
  ['not_start'.freeze, {
    arel_predicate: 'does_not_match'.freeze,
    formatter: proc { |v| "#{escape_wildcards(v)}%" }
    }
  ],
  ['end'.freeze, {
    arel_predicate: 'matches'.freeze,
    formatter: proc { |v| "%#{escape_wildcards(v)}" }
    }
  ],
  ['not_end'.freeze, {
    arel_predicate: 'does_not_match'.freeze,
    formatter: proc { |v| "%#{escape_wildcards(v)}" }
    }
  ],
  ['true'.freeze, {
    arel_predicate: proc { |v| v ? EQ : NOT_EQ },
    compounds: false,
    type: :boolean,
    validator: proc { |v| BOOLEAN_VALUES.include?(v) },
    formatter: proc { |v| true }
    }
  ],
  ['not_true'.freeze, {
    arel_predicate: proc { |v| v ? NOT_EQ : EQ },
    compounds: false,
    type: :boolean,
    validator: proc { |v| BOOLEAN_VALUES.include?(v) },
    formatter: proc { |v| true }
    }
  ],
  ['false'.freeze, {
    arel_predicate: proc { |v| v ? EQ : NOT_EQ },
    compounds: false,
    type: :boolean,
    validator: proc { |v| BOOLEAN_VALUES.include?(v) },
    formatter: proc { |v| false }
    }
  ],
  ['not_false'.freeze, {
    arel_predicate: proc { |v| v ? NOT_EQ : EQ },
    compounds: false,
    type: :boolean,
    validator: proc { |v| BOOLEAN_VALUES.include?(v) },
    formatter: proc { |v| false }
    }
  ],
  ['present'.freeze, {
    arel_predicate: proc { |v| v ? NOT_EQ_ALL : EQ_ANY },
    compounds: false,
    type: :boolean,
    validator: proc { |v| BOOLEAN_VALUES.include?(v) },
    formatter: proc { |v| [nil, ''.freeze].freeze }
    }
  ],
  ['blank'.freeze, {
    arel_predicate: proc { |v| v ? EQ_ANY : NOT_EQ_ALL },
    compounds: false,
    type: :boolean,
    validator: proc { |v| BOOLEAN_VALUES.include?(v) },
    formatter: proc { |v| [nil, ''.freeze].freeze }
    }
  ],
  ['null'.freeze, {
    arel_predicate: proc { |v| v ? EQ : NOT_EQ },
    compounds: false,
    type: :boolean,
    validator: proc { |v| BOOLEAN_VALUES.include?(v) },
    formatter: proc { |v| nil }
    }
  ],
  ['not_null'.freeze, {
    arel_predicate: proc { |v| v ? NOT_EQ : EQ },
    compounds: false,
    type: :boolean,
    validator: proc { |v| BOOLEAN_VALUES.include?(v) },
    formatter: proc { |v| nil } }
  ],
  ['length_eq'.freeze, {
    arel_predicate: 'eq'.freeze,
    type: :integer
    }
  ],
  ['length_lt'.freeze, {
    arel_predicate: 'lt'.freeze,
    type: :integer
    }
  ],
  ['length_lteq'.freeze, {
    arel_predicate: 'lteq'.freeze,
    type: :integer
    }
  ],
  ['length_gt'.freeze, {
    arel_predicate: 'gt'.freeze,
    type: :integer
    }
  ],
  ['length_gteq'.freeze, {
    arel_predicate: 'gteq'.freeze,
    type: :integer
    }
  ]
].freeze
LIKE_ESCAPE_CHARACTER =

The character used to escape LIKE wildcards. Emitted as an explicit ESCAPE clause alongside every LIKE / NOT LIKE predicate, so the behaviour does not depend on a backend's default escape character — MySQL and PostgreSQL default to backslash, SQLite has no default at all.

'\\'.freeze

Class Method Summary collapse

Class Method Details

.escape_wildcards(unescaped) ⇒ Object

Escapes the LIKE wildcards % and _, and the escape character itself, so they are matched literally. Paired with the ESCAPE clause added in Condition#format_predicate; escaping without it silently does nothing on SQLite. See https://github.com/activerecord-hackery/ransack/issues/1581



206
207
208
# File 'lib/ransack/constants.rb', line 206

def escape_wildcards(unescaped)
  unescaped.to_s.gsub(/([\\%_])/) { "#{LIKE_ESCAPE_CHARACTER}#{$1}" }
end