filter_param 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +21 -0
- data/README.md +138 -0
- data/lib/filter_param/ast/attribute.rb +15 -0
- data/lib/filter_param/ast/expressions.rb +37 -0
- data/lib/filter_param/ast/group.rb +13 -0
- data/lib/filter_param/ast/literal.rb +26 -0
- data/lib/filter_param/ast/literals/boolean.rb +38 -0
- data/lib/filter_param/ast/literals/date.rb +33 -0
- data/lib/filter_param/ast/literals/date_time.rb +36 -0
- data/lib/filter_param/ast/literals/decimal.rb +29 -0
- data/lib/filter_param/ast/literals/integer.rb +39 -0
- data/lib/filter_param/ast/literals/null.rb +19 -0
- data/lib/filter_param/ast/literals/string.rb +43 -0
- data/lib/filter_param/ast/node.rb +15 -0
- data/lib/filter_param/ast/scope.rb +12 -0
- data/lib/filter_param/definition.rb +157 -0
- data/lib/filter_param/field.rb +45 -0
- data/lib/filter_param/operator.rb +46 -0
- data/lib/filter_param/operators/and.rb +13 -0
- data/lib/filter_param/operators/case_insensitive_equal.rb +16 -0
- data/lib/filter_param/operators/contains.rb +18 -0
- data/lib/filter_param/operators/ends_with.rb +18 -0
- data/lib/filter_param/operators/equal.rb +21 -0
- data/lib/filter_param/operators/field_filter_operator.rb +40 -0
- data/lib/filter_param/operators/greater_than.rb +16 -0
- data/lib/filter_param/operators/greater_than_equal.rb +16 -0
- data/lib/filter_param/operators/group.rb +17 -0
- data/lib/filter_param/operators/less_than.rb +16 -0
- data/lib/filter_param/operators/less_than_equal.rb +16 -0
- data/lib/filter_param/operators/not.rb +16 -0
- data/lib/filter_param/operators/not_equal.rb +19 -0
- data/lib/filter_param/operators/or.rb +13 -0
- data/lib/filter_param/operators/present.rb +21 -0
- data/lib/filter_param/operators/starts_with.rb +18 -0
- data/lib/filter_param/parser.rb +144 -0
- data/lib/filter_param/scope.rb +24 -0
- data/lib/filter_param/transformer.rb +37 -0
- data/lib/filter_param/transpiler.rb +114 -0
- data/lib/filter_param/version.rb +5 -0
- data/lib/filter_param.rb +70 -0
- metadata +143 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
module FilterParam
|
2
|
+
class Field
|
3
|
+
TYPES = %i[boolean string integer decimal date datetime].freeze
|
4
|
+
|
5
|
+
attr_reader :type, :name
|
6
|
+
|
7
|
+
def initialize(name, type, options = {})
|
8
|
+
@name = name
|
9
|
+
@type = field_type(type)
|
10
|
+
@rename = field_rename(options[:rename])
|
11
|
+
@value_transformer = options[:value]
|
12
|
+
end
|
13
|
+
|
14
|
+
def transform_value(value)
|
15
|
+
return value if value_transformer.blank?
|
16
|
+
|
17
|
+
value_transformer.call(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def actual_name
|
21
|
+
rename.presence || name
|
22
|
+
end
|
23
|
+
|
24
|
+
def allow_operator?(operator)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :value_transformer, :rename
|
31
|
+
|
32
|
+
def field_rename(rename)
|
33
|
+
return rename.call(name) if rename.is_a?(Proc)
|
34
|
+
|
35
|
+
rename
|
36
|
+
end
|
37
|
+
|
38
|
+
def field_type(type)
|
39
|
+
type = (type.presence || :string).to_sym
|
40
|
+
return type if type.in?(TYPES)
|
41
|
+
|
42
|
+
raise UnknownType.new("Unknown type '#{type}' for field '#{name}'. Allowed types: #{TYPES}.")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module FilterParam
|
2
|
+
class Operator
|
3
|
+
class << self
|
4
|
+
def type
|
5
|
+
return :unary if method(:sql).arity == 1
|
6
|
+
|
7
|
+
:binary
|
8
|
+
end
|
9
|
+
|
10
|
+
def operator_tag(operator_tag)
|
11
|
+
@operator_tag ||= operator_tag
|
12
|
+
end
|
13
|
+
|
14
|
+
def register(operator_clazz)
|
15
|
+
operator_tag = operator_clazz.tag
|
16
|
+
registry[operator_tag] = operator_clazz
|
17
|
+
end
|
18
|
+
|
19
|
+
def tag
|
20
|
+
@operator_tag
|
21
|
+
end
|
22
|
+
|
23
|
+
def for(operator_tag)
|
24
|
+
registry[operator_tag]
|
25
|
+
end
|
26
|
+
|
27
|
+
def binaries
|
28
|
+
with_type(:binary).map(&:tag)
|
29
|
+
end
|
30
|
+
|
31
|
+
def unaries
|
32
|
+
with_type(:unary).map(&:tag)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def with_type(type)
|
38
|
+
registry.values.select { |op| op < self && op.type == type }
|
39
|
+
end
|
40
|
+
|
41
|
+
def registry
|
42
|
+
@@registry ||= {}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class CaseInsensitiveEqual < FieldFilterOperator
|
4
|
+
operator_tag :eq_ci
|
5
|
+
operand_data_type :string
|
6
|
+
|
7
|
+
def self.sql(field, literal)
|
8
|
+
super
|
9
|
+
|
10
|
+
"lower(#{field.actual_name}) = #{sql_quote(literal.value.downcase)}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
FilterParam::Operator.register(FilterParam::Operators::CaseInsensitiveEqual)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class Contains < FieldFilterOperator
|
4
|
+
operator_tag :co
|
5
|
+
operand_data_type :string
|
6
|
+
|
7
|
+
def self.sql(field, literal)
|
8
|
+
super
|
9
|
+
|
10
|
+
pattern = "%#{literal.value}%"
|
11
|
+
|
12
|
+
"#{field.actual_name} LIKE #{sql_quote(pattern)}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
FilterParam::Operator.register(FilterParam::Operators::Contains)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class EndsWith < FieldFilterOperator
|
4
|
+
operator_tag :ew
|
5
|
+
operand_data_type :string
|
6
|
+
|
7
|
+
def self.sql(field, literal)
|
8
|
+
super
|
9
|
+
|
10
|
+
pattern = "%#{literal.value}"
|
11
|
+
|
12
|
+
"#{field.actual_name} LIKE #{sql_quote(pattern)}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
FilterParam::Operator.register(FilterParam::Operators::EndsWith)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class Equal < FieldFilterOperator
|
4
|
+
operator_tag :eq
|
5
|
+
|
6
|
+
def self.sql(field, literal)
|
7
|
+
super
|
8
|
+
|
9
|
+
return "#{field.actual_name} IS NULL" if literal.value.nil?
|
10
|
+
|
11
|
+
"#{field.actual_name} = #{sql_quote(literal.value)}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.negated_sql(field, literal)
|
15
|
+
Operators::NotEqual.sql(field, literal)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
FilterParam::Operator.register(FilterParam::Operators::Equal)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class FieldFilterOperator < Operator
|
4
|
+
class << self
|
5
|
+
attr_reader :operand_data_types
|
6
|
+
|
7
|
+
def operand_data_type(*data_types)
|
8
|
+
@operand_data_types ||= Set.new
|
9
|
+
@operand_data_types.merge(data_types)
|
10
|
+
@operand_data_types
|
11
|
+
end
|
12
|
+
|
13
|
+
def sql(field, literal)
|
14
|
+
validate_field!(field)
|
15
|
+
validate_literal!(literal)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def validate_field!(field)
|
21
|
+
field.allow_operator?(tag)
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_literal!(literal)
|
25
|
+
return if literal.nil?
|
26
|
+
return if operand_data_types.nil?
|
27
|
+
return if literal.data_type.in?(operand_data_types)
|
28
|
+
|
29
|
+
raise FilterParam::InvalidLiteral.new(
|
30
|
+
"Unexpected #{literal.data_type} operand for operator '#{tag}'."
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def sql_quote(value)
|
35
|
+
ActiveRecord::Base.connection.quote(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class GreaterThan < FieldFilterOperator
|
4
|
+
operator_tag :gt
|
5
|
+
operand_data_type :string, :integer, :decimal, :date, :datetime
|
6
|
+
|
7
|
+
def self.sql(field, literal)
|
8
|
+
super
|
9
|
+
|
10
|
+
"#{field.actual_name} > #{sql_quote(literal.value)}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
FilterParam::Operator.register(FilterParam::Operators::GreaterThan)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class GreaterThanEqual < FieldFilterOperator
|
4
|
+
operator_tag :ge
|
5
|
+
operand_data_type :string, :integer, :decimal, :date, :datetime
|
6
|
+
|
7
|
+
def self.sql(field, literal)
|
8
|
+
super
|
9
|
+
|
10
|
+
"#{field.actual_name} >= #{sql_quote(literal.value)}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
FilterParam::Operator.register(FilterParam::Operators::GreaterThanEqual)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class Group < Operator
|
4
|
+
operator_tag :group
|
5
|
+
|
6
|
+
def self.sql(expression)
|
7
|
+
"(#{expression})"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.negated_sql(expression)
|
11
|
+
"NOT (#{expression})"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
FilterParam::Operator.register(FilterParam::Operators::Group)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class LessThan < FieldFilterOperator
|
4
|
+
operator_tag :lt
|
5
|
+
operand_data_type :string, :integer, :decimal, :date, :datetime
|
6
|
+
|
7
|
+
def self.sql(field, literal)
|
8
|
+
super
|
9
|
+
|
10
|
+
"#{field.actual_name} < #{sql_quote(literal.value)}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
FilterParam::Operator.register(FilterParam::Operators::LessThan)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class LessThanEqual < FieldFilterOperator
|
4
|
+
operator_tag :le
|
5
|
+
operand_data_type :string, :integer, :decimal, :date, :datetime
|
6
|
+
|
7
|
+
def self.sql(field, literal)
|
8
|
+
super
|
9
|
+
|
10
|
+
"#{field.actual_name} <= #{sql_quote(literal.value)}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
FilterParam::Operator.register(FilterParam::Operators::LessThanEqual)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class Not < Operator
|
4
|
+
operator_tag :not
|
5
|
+
|
6
|
+
def self.sql(expression_operator, *expression_operands)
|
7
|
+
operator = Operator.for(expression_operator)
|
8
|
+
return operator.negated_sql(*expression_operands) if operator.respond_to?(:negated_sql)
|
9
|
+
|
10
|
+
"NOT #{operator.sql(*expression_operands)}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
FilterParam::Operator.register(FilterParam::Operators::Not)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class NotEqual < FieldFilterOperator
|
4
|
+
operator_tag :neq
|
5
|
+
|
6
|
+
def self.sql(field, literal)
|
7
|
+
return "#{field.actual_name} IS NOT NULL" if literal.value.nil?
|
8
|
+
|
9
|
+
"#{field.actual_name} != #{sql_quote(literal.value)}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.negated_sql(field, literal)
|
13
|
+
Operators::Equal.sql(field, literal)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
FilterParam::Operator.register(FilterParam::Operators::NotEqual)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class Present < FieldFilterOperator
|
4
|
+
operator_tag :pr
|
5
|
+
|
6
|
+
def self.sql(field)
|
7
|
+
return "#{field.actual_name} IS NOT NULL" unless field.type == :string
|
8
|
+
|
9
|
+
"(#{field.actual_name} IS NOT NULL AND TRIM(#{field.actual_name}) != '')"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.negated_sql(field)
|
13
|
+
return "#{field.actual_name} IS NULL" unless field.type == :string
|
14
|
+
|
15
|
+
"(#{field.actual_name} IS NULL OR TRIM(#{field.actual_name}) = '')"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
FilterParam::Operator.register(FilterParam::Operators::Present)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FilterParam
|
2
|
+
module Operators
|
3
|
+
class StartsWith < FieldFilterOperator
|
4
|
+
operator_tag :sw
|
5
|
+
operand_data_type :string
|
6
|
+
|
7
|
+
def self.sql(field, literal)
|
8
|
+
super
|
9
|
+
|
10
|
+
pattern = "#{literal.value}%"
|
11
|
+
|
12
|
+
"#{field.actual_name} LIKE #{sql_quote(pattern)}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
FilterParam::Operator.register(FilterParam::Operators::StartsWith)
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module FilterParam
|
2
|
+
class Parser < Parslet::Parser
|
3
|
+
rule(:space) { match("\s").repeat(1).ignore }
|
4
|
+
rule(:space?) { space.maybe }
|
5
|
+
rule(:dot) { str(".") }
|
6
|
+
rule(:lparen) { str("(") }
|
7
|
+
rule(:rparen) { str(")") }
|
8
|
+
rule(:escape_seq) { str('\\').present? >> str('\\') >> any }
|
9
|
+
rule(:double_quote) { str('"') }
|
10
|
+
rule(:single_quote) { str("'") }
|
11
|
+
rule(:digit) { match("[0-9]") }
|
12
|
+
rule(:zero) { str("0") }
|
13
|
+
rule(:non_zero_digit) { match("[1-9]") }
|
14
|
+
rule(:sig_number) { non_zero_digit >> digit.repeat(1).maybe }
|
15
|
+
rule(:zero_nonsig) { zero.repeat(0).maybe.ignore }
|
16
|
+
rule(:hyphen) { str("-") }
|
17
|
+
rule(:identifier) { match("[a-zA-Z_]") >> digit.maybe }
|
18
|
+
rule(:table) { identifier.repeat(1) >> dot }
|
19
|
+
rule(:attribute) { (table.maybe >> identifier.repeat(1)).as(:attribute) }
|
20
|
+
rule(:scope_name) { identifier.repeat(1) }
|
21
|
+
|
22
|
+
# Literals / types
|
23
|
+
rule(:null) { str("null").as(:null) }
|
24
|
+
rule(:boolean) { (str("true") | str("false")).as(:boolean) }
|
25
|
+
|
26
|
+
rule(:integer) do
|
27
|
+
(
|
28
|
+
(hyphen.maybe >> zero_nonsig >> sig_number) |
|
29
|
+
(hyphen.maybe.ignore >> zero >> zero_nonsig)
|
30
|
+
).as(:integer)
|
31
|
+
end
|
32
|
+
|
33
|
+
rule(:decimal) do
|
34
|
+
(
|
35
|
+
(hyphen.maybe >> zero_nonsig >> sig_number >> dot >> digit.repeat(1)) |
|
36
|
+
(hyphen.maybe >> zero >> zero_nonsig >> dot >> digit.repeat(1))
|
37
|
+
).as(:decimal)
|
38
|
+
end
|
39
|
+
|
40
|
+
rule(:string) do
|
41
|
+
(single_quote >> (escape_seq | match("[^\']")).repeat.as(:string) >> single_quote) |
|
42
|
+
(double_quote >> (escape_seq | match("[^\"]")).repeat.as(:string) >> double_quote)
|
43
|
+
end
|
44
|
+
rule(:date_yyyy) { digit.repeat(4) }
|
45
|
+
rule(:date_mm) { (zero >> non_zero_digit) | (str("1") >> match("[0-2]")) }
|
46
|
+
rule(:date_md) { (zero >> non_zero_digit) | (match("[1-2]") >> digit) | (str("3") >> match("[0-1]")) }
|
47
|
+
rule(:date_iso8601) { date_yyyy >> hyphen >> date_mm >> hyphen >> date_md }
|
48
|
+
rule(:date) { quoted date_iso8601.as(:date) }
|
49
|
+
rule(:time_hh_mi) do
|
50
|
+
(((zero | str("1")) >> digit) | (str("2") >> match("[0-3]"))) >>
|
51
|
+
str(":").maybe >> ((zero >> digit) | (match("[1-5]") >> digit))
|
52
|
+
end
|
53
|
+
rule(:time_hh_mi_ss) { time_hh_mi >> str(":") >> ((zero >> digit) | (match("[1-5]") >> digit)) }
|
54
|
+
rule(:time_hh_mi_ss_sss) { time_hh_mi_ss >> dot >> digit.repeat(3, 6) }
|
55
|
+
rule(:time_tz) { str("Z") | (match("[\+\-]") >> time_hh_mi) }
|
56
|
+
rule(:datetime_iso8601) { date_iso8601 >> str("T") >> (time_hh_mi_ss_sss | time_hh_mi_ss) >> time_tz }
|
57
|
+
rule(:datetime) { quoted datetime_iso8601.as(:datetime) }
|
58
|
+
|
59
|
+
# Operations
|
60
|
+
rule(:op_attr_binary) do
|
61
|
+
binary_attr_operators.as(:operator)
|
62
|
+
end
|
63
|
+
rule(:op_attr_unary) { (unary_attr_operators).as(:operator) }
|
64
|
+
rule(:op_logic_binary) { (str("and") | str("or")).as(:operator) }
|
65
|
+
rule(:op_logic_unary) { str("not").as(:operator) }
|
66
|
+
|
67
|
+
# Expressions
|
68
|
+
rule(:literal) { (null | boolean | decimal | integer | datetime | date | string) }
|
69
|
+
rule(:literal_paren) { lparen >> space? >> (literal | literal_paren) >> space? >> rparen }
|
70
|
+
rule(:attr_value) { literal_paren | (space >> (literal | literal_paren)) }
|
71
|
+
rule(:attr_exp) { (attribute >> space >> (op_attr_unary | (op_attr_binary >> attr_value.as(:val)))).as(:exp) }
|
72
|
+
rule(:group) do
|
73
|
+
empty_group.ignore |
|
74
|
+
(lparen >> space? >> (binary_exp | unary_exp | attr_exp) >> space? >> rparen).as(:group) |
|
75
|
+
(lparen >> space? >> group >> space? >> rparen)
|
76
|
+
end
|
77
|
+
rule(:empty_group) do
|
78
|
+
(lparen >> space? >> empty_group >> space? >> rparen) | (lparen >> space? >> rparen)
|
79
|
+
end
|
80
|
+
rule(:empty_exp) { (space | str("")).ignore }
|
81
|
+
rule(:scope_args) { literal >> space? >> (str(",") >> space? >> literal).repeat(0) }
|
82
|
+
rule(:scope) { scope_name.as(:scope) >> lparen >> space? >> scope_args.maybe.as(:args) >> space? >> rparen }
|
83
|
+
rule(:primary) { group | attr_exp | scope }
|
84
|
+
|
85
|
+
rule(:unary_exp) do
|
86
|
+
(op_logic_unary >> (space | lparen.present?) >> primary.as(:right)).as(:exp) | primary
|
87
|
+
end
|
88
|
+
rule(:binary_exp) do
|
89
|
+
(
|
90
|
+
unary_exp.as(:left) >> space >> op_logic_binary >> ((space | lparen.present?) >> exp).as(:right)
|
91
|
+
).as(:exp) | unary_exp
|
92
|
+
end
|
93
|
+
|
94
|
+
rule(:exp) { (space? >> binary_exp >> space?) }
|
95
|
+
rule(:exp_root) { exp | empty_exp }
|
96
|
+
root(:exp_root)
|
97
|
+
|
98
|
+
def parse(expression, options = {})
|
99
|
+
super(expression, options)
|
100
|
+
rescue Parslet::ParseFailed => e
|
101
|
+
parse_cause = e.parse_failure_cause.children.last
|
102
|
+
|
103
|
+
raise_parse_error!(parse_cause)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def quoted(atom_or_seq)
|
109
|
+
(single_quote >> atom_or_seq >> single_quote) | (double_quote >> atom_or_seq >> double_quote)
|
110
|
+
end
|
111
|
+
|
112
|
+
def binary_attr_operators
|
113
|
+
@@binary_attr_ops = operators_to_atoms(Operators::FieldFilterOperator.binaries.map(&:to_s))
|
114
|
+
end
|
115
|
+
|
116
|
+
def unary_attr_operators
|
117
|
+
@@unary_attr_ops = operators_to_atoms(Operators::FieldFilterOperator.unaries.map(&:to_s))
|
118
|
+
end
|
119
|
+
|
120
|
+
def operators_to_atoms(operators)
|
121
|
+
operators.sort_by(&:length)
|
122
|
+
.reverse
|
123
|
+
.map { |tag| str(tag) }
|
124
|
+
.reduce(:|)
|
125
|
+
end
|
126
|
+
|
127
|
+
def raise_parse_error!(parse_cause)
|
128
|
+
parse_cause = parse_cause.to_s
|
129
|
+
invalid_expression = "Filter expression syntax error."
|
130
|
+
|
131
|
+
if parse_cause.start_with?("Expected ")
|
132
|
+
parse_cause = invalid_expression
|
133
|
+
else
|
134
|
+
unexpected_token = "Unexpected token"
|
135
|
+
|
136
|
+
parse_cause.sub!("Don't know what to do with", unexpected_token)
|
137
|
+
parse_cause.sub!(/(Failed to match).*.(at line 1)/, "#{unexpected_token} at")
|
138
|
+
parse_cause.sub!(/(at line 1)/, "at")
|
139
|
+
end
|
140
|
+
|
141
|
+
raise ParseError.new(parse_cause)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module FilterParam
|
2
|
+
class Scope
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name, options = {})
|
6
|
+
@name = name
|
7
|
+
@rename = scope_rename(options[:rename])
|
8
|
+
end
|
9
|
+
|
10
|
+
def actual_name
|
11
|
+
rename.presence || name
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :rename
|
17
|
+
|
18
|
+
def scope_rename(rename)
|
19
|
+
return rename.call(name) if rename.is_a?(Proc)
|
20
|
+
|
21
|
+
rename
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module FilterParam
|
2
|
+
class Transformer < Parslet::Transform
|
3
|
+
include AST
|
4
|
+
|
5
|
+
rule(null: simple(:null)) { Literals::Null.instance }
|
6
|
+
rule(string: simple(:value)) { Literals::String.new(value) }
|
7
|
+
rule(boolean: simple(:value)) { value == "true" ? Literals::Boolean::TRUE : Literals::Boolean::FALSE }
|
8
|
+
rule(integer: simple(:value)) { Literals::Integer.new(value) }
|
9
|
+
rule(decimal: simple(:value)) { Literals::Decimal.new(value) }
|
10
|
+
rule(date: simple(:value)) { Literals::Date.new(value) }
|
11
|
+
rule(datetime: simple(:value)) { Literals::DateTime.new(value) }
|
12
|
+
rule(exp: simple(:exp)) { exp }
|
13
|
+
rule(group: simple(:exp)) { Group.new(exp) }
|
14
|
+
rule(scope: simple(:name), args: simple(:scope_arg)) do
|
15
|
+
scope_args = scope_arg.nil? ? [] : [scope_arg]
|
16
|
+
|
17
|
+
AST::Scope.new(name, scope_args)
|
18
|
+
end
|
19
|
+
rule(scope: simple(:name), args: sequence(:scope_args)) { AST::Scope.new(name, scope_args) }
|
20
|
+
rule(operator: simple(:operator), right: simple(:operand)) do
|
21
|
+
Expressions::UnaryExpression.new(operator, operand)
|
22
|
+
end
|
23
|
+
rule(attribute: simple(:attribute_name), operator: simple(:operator)) do
|
24
|
+
attribute = Attribute.new(attribute_name)
|
25
|
+
|
26
|
+
Expressions::UnaryExpression.new(operator, attribute)
|
27
|
+
end
|
28
|
+
rule(left: simple(:left), operator: simple(:operator), right: simple(:right)) do
|
29
|
+
Expressions::BinaryExpression.new(operator, left, right)
|
30
|
+
end
|
31
|
+
rule(attribute: simple(:attribute_name), operator: simple(:operator), val: simple(:literal)) do
|
32
|
+
attribute = Attribute.new(attribute_name)
|
33
|
+
|
34
|
+
Expressions::BinaryExpression.new(operator, attribute, literal)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|