Class: Basepack::FilterQL

Inherits:
Object
  • Object
show all
Defined in:
lib/basepack/filter_ql.rb

Defined Under Namespace

Classes: Builder, ParseError, Parser, Transformer

Constant Summary collapse

PREDICATE_MAP =
{
  "="        => 'eq',
  "!="       => 'not_eq',
  ">="       => 'gteq',
  "<="       => 'lteq',
  ">"        => 'gt',
  "<"        => 'lt',
  "like"     => 'matches',
  "notlike"  => 'does_not_match',
  "cont"     => 'cont',
  "notcont"  => 'not_cont',
  "start"    => 'start',
  "notstart" => 'not_start',
  "end"      => 'end',
  "notend"   => 'not_end',
  "one_of"   => 'one_of',
  "blank"    => 'blank',
  "notblank" => 'present',
  "null"     => 'null',
  "notnull"  => 'not_null',
}.freeze
PREDICATE_MAP_REVERSE =
{
  'eq'             => "=",
  'not_eq'         => "!=",
  'gteq'           => ">=",
  'lteq'           => "<=",
  'gt'             => ">",
  'lt'             => "<",
  'matches'        => "like",
  'does_not_match' => "not like",
  'cont'           => "cont",
  'not_cont'       => "not cont",
  'start'          => "start",
  'not_start'      => "not start",
  'end'            => "end",
  'not_end'        => "not end",
  'one_of'         => "one_of",
  'blank'          => "is blank",
  'present'        => "is not blank",
  'null'           => "is null",
  'not_null'       => "is not null",
  'true'           => "= true",
  'false'          => "= false",
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = nil) ⇒ FilterQL

Returns a new instance of FilterQL.



184
185
186
187
188
# File 'lib/basepack/filter_ql.rb', line 184

def initialize(options = nil)
  @parser = Parser.new
  @transformer = Transformer.new
  @options = (options || {}).freeze
end

Class Method Details

.conditions_to_ql(conditions) ⇒ Object



216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/basepack/filter_ql.rb', line 216

def self.conditions_to_ql(conditions)
  conditions.map do |name, predicate_name, value|
    if predicates[predicate_name][:type] == :boolean
      "#{name} #{PREDICATE_MAP_REVERSE[predicate_name]}"
    else
      case value
      when String then value = value_to_string(value)
      when Hash   then value = value_to_jshash(value)
      end
      "#{name} #{PREDICATE_MAP_REVERSE[predicate_name]} #{value}"
    end
  end.join(" and ")
end

.predicatesObject



230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/basepack/filter_ql.rb', line 230

def self.predicates
  @predicates ||= begin
    Hash[Ransack.predicates.map do |k, predicate|
      [k, {
        name:     k,
        label:    Ransack::Translate.predicate(k),
        type:     predicate.type,
        compound: predicate.compound,
        wants_array: predicate.wants_array,
      }]
    end.compact]
  end
end

.string_to_value(string) ⇒ Object



255
256
257
# File 'lib/basepack/filter_ql.rb', line 255

def self.string_to_value(string)
  string.gsub(/\\(.)/, '\1')
end

.testObject

Basepack::FilterQL.test



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/basepack/filter_ql.rb', line 204

def self.test
  new(
    functions: {
      user: proc {|builder, arg| arg.inspect }
    }
  ).parse(
    "a0 = user() and a01 = user('I') and a02 = user({ x: 1}) and a03 = user([1,2,'3']) and " +
    "a1 = 3 and a2 like 'asd' and a3 not like 'asd' and a4 is null and a5 is not null and a6 != 'as\\'d' and a7 = \"str\\\"s\" and " +
    "a8 = { key:'value', key2:1 } and a9 = ['str', 4, 3.5] ",
  )
end

.value_to_jshash(value) ⇒ Object



248
249
250
251
252
253
# File 'lib/basepack/filter_ql.rb', line 248

def self.value_to_jshash(value)
  res = value.inject([]) do |r,(k,v)|
    r << "#{k}: #{v.inspect}"
  end.join(", ")
  "{ #{res} }"
end

.value_to_string(value) ⇒ Object



244
245
246
# File 'lib/basepack/filter_ql.rb', line 244

def self.value_to_string(value)
  "'#{value.gsub(/(['\\])/, '\\\\\1')}'"
end

Instance Method Details

#parse(query, options = nil) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/basepack/filter_ql.rb', line 190

def parse(query, options = nil)
  builder = Builder.new(query, @options)
  begin
    ast = @parser.parse(query, reporter: Parslet::ErrorReporter::Deepest.new)
    @transformer.apply(ast, builder: builder)
  rescue Parslet::ParseFailed => e
    #puts e.cause.ascii_tree
    deepest = deepest_cause(e.cause)
    line, column = deepest.source.line_and_column(deepest.pos)
    builder.raise_error_for_pos("Neočekáváný vstup", deepest.pos, line, column)
  end
end