rql_parser 0.0.1alpha4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75d3efb4c2d93963bc05ad192108c57e964d3224b11d5bf0291bd69a2f652ffe
4
- data.tar.gz: 99270a824bf63557ce93e29b1db68491f971c5189119b8431356b0cb2444cafc
3
+ metadata.gz: 3ee91e4a5b5797d766aaa0bd4f5f82160a81185f135f0b2f1ca6ad1a3e744531
4
+ data.tar.gz: fc194c4a5a716976196c256b64c7120f488043a6eee80045f853a944d1580a56
5
5
  SHA512:
6
- metadata.gz: 7598c3cd3007ac838fce30b144f0a3c181313a95855496697d538efbb608b9096779d8a06f6ea2d2fbece42128c65ac4a70aec9b39e6d6cfb8fe3603b079500b
7
- data.tar.gz: acc40e93395fcbd2012fa7961c291d46bee87eaa75969155f0cfa8797cca99f94fe9a0c1a25e4d5955148acfba0bcd591dbf86a47222bd8bc78201290da38643
6
+ metadata.gz: 1f6e94f69597fad8bf30bc36afb5f17fdbece5b048773796d6d83bf9f2dcd6bd906468fb786c9e202d85b2fa5bf66244f5d56f981750ae3f9f90988d38069f77
7
+ data.tar.gz: a0e259cfa2d4ece901a93c658c26d252e0e939632e6fbd37ef61e2051b3e027bfa70e684c82950c20a754a7bfde03b709e61f9beb4ed61320fb70545630bfdc7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ #### 1.0.0
4
+
5
+ - Make function identifiers symbols instead of strings
6
+ - Remove binary tree `group` node
7
+ - Prettify parser code
8
+ - Tweak grammar
9
+
10
+ ---
11
+
3
12
  #### 0.0.1alpha4
4
13
 
5
14
  - Restructure gem files
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rql_parser (0.0.1alpha3)
4
+ rql_parser (1.0.0)
5
5
  active_interaction (~> 3.6.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -10,11 +10,15 @@ gem 'rql_parser'
10
10
 
11
11
  And then execute:
12
12
 
13
- $ bundle
13
+ ```
14
+ $ bundle
15
+ ```
14
16
 
15
17
  Or install it yourself as:
16
18
 
17
- $ gem install rql_parser
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 | and_strict | group), { ',', (function | and_strict | group) } ;
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 | array_of_values | value ;
17
+ arg = and_exp | arg_array | value ;
18
18
 
19
19
  value = /[_0-9a-zA-Z]+/ ;
20
20
 
21
- array_of_values = '(', value, { ',', value }, ')' ;
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(interaction)
7
- if interaction.valid?
8
- interaction.result
6
+ def perform(outcome)
7
+ if outcome.valid?
8
+ outcome.result
9
9
  else
10
- errors.merge!(interaction.errors)
10
+ errors.merge!(outcome.errors)
11
11
  end
12
12
  end
13
13
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module RqlParser
4
4
  module Services
5
+ # Creates a RQL binary tree directly from params hash
5
6
  class FromParams < BaseInteraction
6
7
  hash :params, strip: false
7
8
 
@@ -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
- res = false
21
- if /\A\(.+\)\z/.match?(str)
22
- res = or_expression(str[1..-2])
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
- res = []
32
- split = str.split(/[;|]/)
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
- res.size > 1 ? { type: :function, identifier: 'or', args: res } : res.first
38
+ result.size > 1 ? { type: :function, identifier: :or, args: result } : result.first
47
39
  end
48
40
 
49
41
  def and_expression(str)
50
- res = []
51
- split = str.split(/,/)
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
- res.size > 1 ? { type: :function, identifier: 'and', args: res } : res.first
45
+ result.size > 1 ? { type: :function, identifier: :and, args: result } : result.first
66
46
  end
67
47
 
68
48
  def and_strict(str)
69
- res = []
70
- split = str.split(/&/)
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
- res.size > 1 ? { type: :function, identifier: 'and', args: res } : res.first
52
+ result.size > 1 ? { type: :function, identifier: :and, args: result } : result.first
85
53
  end
86
54
 
87
55
  def function(str)
88
- res = false
89
- if /\A[a-z]+\(.+\)\z/.match?(str)
90
- identifier = str.match(/\A([a-z]+)/)[1]
91
- args = str.match(/\A[a-z]+\((.+)\)\z/)[1]
92
- temp_args = args(args)
93
- res = { type: :function, identifier: identifier, args: temp_args } if temp_args
94
- end
95
- res
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
- res = []
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 /\A\(.+\)\z/.match?(str)
75
+ return false unless BRACES_REGEX.match?(str)
123
76
 
124
- res = []
125
- split = str[1..-2].split(',')
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: res }
80
+ { arg_array: result }
140
81
  end
141
82
 
142
83
  def value(str)
143
- if str.match?(/\A[_0-9a-zA-Z]+\z/)
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
@@ -1,3 +1,3 @@
1
1
  module RqlParser
2
- VERSION = '0.0.1alpha4'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
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.1alpha4
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-26 00:00:00.000000000 Z
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: 1.3.1
115
+ version: '0'
116
116
  requirements: []
117
117
  rubyforge_project:
118
118
  rubygems_version: 2.7.6