rql_parser 0.0.1alpha4 → 1.0.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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/README.md +34 -7
- data/lib/rql_parser/_grammar.ebnf +4 -4
- data/lib/rql_parser/base_interaction.rb +4 -4
- data/lib/rql_parser/services/from_params.rb +1 -0
- data/lib/rql_parser/services/parse.rb +58 -96
- data/lib/rql_parser/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ee91e4a5b5797d766aaa0bd4f5f82160a81185f135f0b2f1ca6ad1a3e744531
|
4
|
+
data.tar.gz: fc194c4a5a716976196c256b64c7120f488043a6eee80045f853a944d1580a56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f6e94f69597fad8bf30bc36afb5f17fdbece5b048773796d6d83bf9f2dcd6bd906468fb786c9e202d85b2fa5bf66244f5d56f981750ae3f9f90988d38069f77
|
7
|
+
data.tar.gz: a0e259cfa2d4ece901a93c658c26d252e0e939632e6fbd37ef61e2051b3e027bfa70e684c82950c20a754a7bfde03b709e61f9beb4ed61320fb70545630bfdc7
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -10,11 +10,15 @@ gem 'rql_parser'
|
|
10
10
|
|
11
11
|
And then execute:
|
12
12
|
|
13
|
-
|
13
|
+
```
|
14
|
+
$ bundle
|
15
|
+
```
|
14
16
|
|
15
17
|
Or install it yourself as:
|
16
18
|
|
17
|
-
|
19
|
+
```
|
20
|
+
$ gem install rql_parser
|
21
|
+
```
|
18
22
|
|
19
23
|
## Usage
|
20
24
|
|
@@ -23,18 +27,41 @@ See [ActiveInteraction](https://github.com/AaronLasseigne/active_interaction), a
|
|
23
27
|
```ruby
|
24
28
|
# In controller:
|
25
29
|
output = RqlParser.from_params(params.to_unsafe_hash)
|
26
|
-
# To raise an error instead of returning an object:
|
27
|
-
output = RqlParser.from_params!(params.to_unsafe_hash)
|
28
30
|
|
29
31
|
# Parse RQL instead of params:
|
30
32
|
rql = 'eq(hello,world)&ruby=eq=awesome' # your RQL query here
|
31
33
|
output = RqlParser.parse(rql)
|
32
|
-
# To raise an error instead of returning an object:
|
33
|
-
output = RqlParser.parse!(rql)
|
34
34
|
```
|
35
35
|
|
36
|
-
`output.result` yields a binary tree representing the query
|
36
|
+
`output.result` yields a binary tree representing the query:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
rql = 'eq(hello,world)&ruby=eq=awesome' # your RQL query here
|
40
|
+
output = RqlParser.parse(rql)
|
41
|
+
output.valid?
|
42
|
+
#=> true
|
43
|
+
output.result
|
44
|
+
#=> { type: :function,
|
45
|
+
# identifier: :and,
|
46
|
+
# args: [{ type: :function,
|
47
|
+
# identifier: :eq,
|
48
|
+
# args: [{ arg: 'hello' },
|
49
|
+
# { arg: 'world' }] },
|
50
|
+
# { type: :function,
|
51
|
+
# identifier: :eq,
|
52
|
+
# args: [{ arg: 'ruby' },
|
53
|
+
# { arg: 'awesome' }] }] }
|
54
|
+
```
|
37
55
|
|
56
|
+
`output.errors` yields an `ActiveModel::Errors`-like object (if any):
|
57
|
+
```ruby
|
58
|
+
rql = 'i=have=errors' # your invalid RQL query here
|
59
|
+
output = RqlParser.parse(rql)
|
60
|
+
output.valid?
|
61
|
+
#=> false
|
62
|
+
output.errors.full_messages
|
63
|
+
#=> 'Rql has invalid shorthands'
|
64
|
+
```
|
38
65
|
## Development
|
39
66
|
|
40
67
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -4,9 +4,9 @@ group = '(', or_exp, ')' ;
|
|
4
4
|
|
5
5
|
or_exp = and_exp, { or_delim, and_exp } ;
|
6
6
|
|
7
|
-
or_delim = '|'
|
7
|
+
or_delim = '|' | ';' ;
|
8
8
|
|
9
|
-
and_exp = (function |
|
9
|
+
and_exp = (function | group | and_strict), { ',', (function | group | and_strict) } ;
|
10
10
|
|
11
11
|
function = identifier, '(', [arg, { ',', arg }], ')' ;
|
12
12
|
|
@@ -14,8 +14,8 @@ and_strict = (function | group), { '&', (function | group) } ;
|
|
14
14
|
|
15
15
|
identifier = /[a-z]+/
|
16
16
|
|
17
|
-
arg = and_exp |
|
17
|
+
arg = and_exp | arg_array | value ;
|
18
18
|
|
19
19
|
value = /[_0-9a-zA-Z]+/ ;
|
20
20
|
|
21
|
-
|
21
|
+
arg_array = '(', value, { ',', value }, ')' ;
|
@@ -3,11 +3,11 @@ module RqlParser
|
|
3
3
|
class BaseInteraction < ActiveInteraction::Base
|
4
4
|
private
|
5
5
|
|
6
|
-
def perform(
|
7
|
-
if
|
8
|
-
|
6
|
+
def perform(outcome)
|
7
|
+
if outcome.valid?
|
8
|
+
outcome.result
|
9
9
|
else
|
10
|
-
errors.merge!(
|
10
|
+
errors.merge!(outcome.errors)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -1,10 +1,19 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module RqlParser
|
4
2
|
module Services
|
3
|
+
# Service that performs the parse operation
|
5
4
|
class Parse < BaseInteraction
|
6
5
|
string :rql
|
7
6
|
|
7
|
+
DELIMITERS = '&,;'.freeze
|
8
|
+
OR_DELIMITERS = /[;|]/.freeze
|
9
|
+
COMMA_DELIMITER = /,/.freeze
|
10
|
+
AND_STRICT_DELIMITERS = /&/.freeze
|
11
|
+
BRACES_REGEX = /\A\(.+\)\z/.freeze
|
12
|
+
FUNCTION_REGEX = /\A[a-z]+\(.+\)\z/.freeze
|
13
|
+
FUNCTION_IDENTIFIER = /\A([a-z]+)/.freeze
|
14
|
+
FUNCTION_ARGS = /\A[a-z]+\((.+)\)\z/.freeze
|
15
|
+
VALUE_REGEX = /\A[_0-9a-zA-Z]+\z/.freeze
|
16
|
+
|
8
17
|
def execute
|
9
18
|
formatted = perform(Format.run(inputs))
|
10
19
|
expression(formatted) || errors.add(:rql) unless errors.any?
|
@@ -17,101 +26,45 @@ module RqlParser
|
|
17
26
|
end
|
18
27
|
|
19
28
|
def group(str)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
if res
|
24
|
-
res = { type: :group, args: res } if res[:args].size > 1
|
25
|
-
end
|
26
|
-
end
|
27
|
-
res
|
29
|
+
return false unless BRACES_REGEX.match?(str)
|
30
|
+
|
31
|
+
or_expression(str[1..-2]) || false
|
28
32
|
end
|
29
33
|
|
30
34
|
def or_expression(str)
|
31
|
-
|
32
|
-
|
33
|
-
temp = ''
|
34
|
-
while split.any?
|
35
|
-
temp += split.shift
|
36
|
-
temp_and_exp = and_expression(temp)
|
37
|
-
if temp_and_exp
|
38
|
-
res.push(temp_and_exp)
|
39
|
-
temp = ''
|
40
|
-
else
|
41
|
-
temp += '|'
|
42
|
-
end
|
43
|
-
end
|
44
|
-
return false if temp.present?
|
35
|
+
result = repeat_pattern(str, OR_DELIMITERS) { |s| and_expression(s) }
|
36
|
+
return false unless result
|
45
37
|
|
46
|
-
|
38
|
+
result.size > 1 ? { type: :function, identifier: :or, args: result } : result.first
|
47
39
|
end
|
48
40
|
|
49
41
|
def and_expression(str)
|
50
|
-
|
51
|
-
|
52
|
-
temp = ''
|
53
|
-
while split.any?
|
54
|
-
temp += split.shift
|
55
|
-
temp_and_exp = function(temp) || group(temp) || and_strict(temp)
|
56
|
-
if temp_and_exp
|
57
|
-
res.push(temp_and_exp)
|
58
|
-
temp = ''
|
59
|
-
else
|
60
|
-
temp += ','
|
61
|
-
end
|
62
|
-
end
|
63
|
-
return false if temp.present?
|
42
|
+
result = repeat_pattern(str, COMMA_DELIMITER) { |s| function(s) || group(s) || and_strict(s) }
|
43
|
+
return false unless result
|
64
44
|
|
65
|
-
|
45
|
+
result.size > 1 ? { type: :function, identifier: :and, args: result } : result.first
|
66
46
|
end
|
67
47
|
|
68
48
|
def and_strict(str)
|
69
|
-
|
70
|
-
|
71
|
-
temp = ''
|
72
|
-
while split.any?
|
73
|
-
temp += split.shift
|
74
|
-
temp_and_exp = function(temp) || group(temp)
|
75
|
-
if temp_and_exp
|
76
|
-
res.push(temp_and_exp)
|
77
|
-
temp = ''
|
78
|
-
else
|
79
|
-
temp += ','
|
80
|
-
end
|
81
|
-
end
|
82
|
-
return false if temp.present?
|
49
|
+
result = repeat_pattern(str, AND_STRICT_DELIMITERS) { |s| function(s) || group(s) }
|
50
|
+
return false unless result
|
83
51
|
|
84
|
-
|
52
|
+
result.size > 1 ? { type: :function, identifier: :and, args: result } : result.first
|
85
53
|
end
|
86
54
|
|
87
55
|
def function(str)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
56
|
+
return false unless FUNCTION_REGEX.match?(str)
|
57
|
+
|
58
|
+
identifier = str.match(FUNCTION_IDENTIFIER)[1]
|
59
|
+
args = args(str.match(FUNCTION_ARGS)[1])
|
60
|
+
|
61
|
+
return false unless args
|
62
|
+
|
63
|
+
{ type: :function, identifier: identifier.to_sym, args: args }
|
96
64
|
end
|
97
65
|
|
98
66
|
def args(str)
|
99
|
-
|
100
|
-
split = str.split(',')
|
101
|
-
temp = ''
|
102
|
-
while split.any?
|
103
|
-
temp += split.shift
|
104
|
-
temp_arg = arg(temp)
|
105
|
-
if temp_arg
|
106
|
-
res.push(temp_arg)
|
107
|
-
temp = ''
|
108
|
-
else
|
109
|
-
temp += ','
|
110
|
-
end
|
111
|
-
end
|
112
|
-
return false if temp.present?
|
113
|
-
|
114
|
-
res
|
67
|
+
repeat_pattern(str, COMMA_DELIMITER) { |s| arg(s) }
|
115
68
|
end
|
116
69
|
|
117
70
|
def arg(str)
|
@@ -119,33 +72,42 @@ module RqlParser
|
|
119
72
|
end
|
120
73
|
|
121
74
|
def array_of_values(str)
|
122
|
-
return false unless
|
75
|
+
return false unless BRACES_REGEX.match?(str)
|
123
76
|
|
124
|
-
|
125
|
-
|
126
|
-
temp = ''
|
127
|
-
while split.any?
|
128
|
-
temp += split.shift
|
129
|
-
temp_arg = value(temp)
|
130
|
-
if temp_arg
|
131
|
-
res.push(temp_arg)
|
132
|
-
temp = ''
|
133
|
-
else
|
134
|
-
temp += ','
|
135
|
-
end
|
136
|
-
end
|
137
|
-
return false if temp.present?
|
77
|
+
result = repeat_pattern(str[1..-2], COMMA_DELIMITER) { |s| value(s) }
|
78
|
+
return false unless result
|
138
79
|
|
139
|
-
{ arg_array:
|
80
|
+
{ arg_array: result }
|
140
81
|
end
|
141
82
|
|
142
83
|
def value(str)
|
143
|
-
if str.match?(
|
84
|
+
if str.match?(VALUE_REGEX)
|
144
85
|
{ arg: str }
|
145
86
|
else
|
146
87
|
false
|
147
88
|
end
|
148
89
|
end
|
90
|
+
|
91
|
+
def repeat_pattern(str, split_regex)
|
92
|
+
split = str.split(split_regex)
|
93
|
+
result = []
|
94
|
+
expression = ''
|
95
|
+
while split.any?
|
96
|
+
expression += split.shift
|
97
|
+
bin_tree = yield(expression)
|
98
|
+
if bin_tree
|
99
|
+
result.push(bin_tree)
|
100
|
+
expression = ''
|
101
|
+
else
|
102
|
+
expression += compose_char(split_regex)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
expression.blank? && result
|
106
|
+
end
|
107
|
+
|
108
|
+
def compose_char(regex)
|
109
|
+
DELIMITERS.match(regex)[0]
|
110
|
+
end
|
149
111
|
end
|
150
112
|
end
|
151
113
|
end
|
data/lib/rql_parser/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rql_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aliaksei Kharkou
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-12-
|
11
|
+
date: 2018-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_interaction
|
@@ -110,9 +110,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
110
110
|
version: '0'
|
111
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
112
|
requirements:
|
113
|
-
- - "
|
113
|
+
- - ">="
|
114
114
|
- !ruby/object:Gem::Version
|
115
|
-
version:
|
115
|
+
version: '0'
|
116
116
|
requirements: []
|
117
117
|
rubyforge_project:
|
118
118
|
rubygems_version: 2.7.6
|