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 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