scoped_search 4.1.11 → 4.1.13

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: 7a3be22acffe6a33b115957197a5eca96b2e9cf2abe445b657b302663594abbe
4
- data.tar.gz: a97d2cd9a8db87347dee308b1340e87967a887dd6b3c5c7b2124f7d9859a9843
3
+ metadata.gz: fbd1e857e3b248d6d585ac7c41679b536949dc945f4c7e067b2b16e86ce325b2
4
+ data.tar.gz: b4c1dc92102ff1d56b1f23406b16cf0523c6bdc17ab336ee92300993de5fd7f9
5
5
  SHA512:
6
- metadata.gz: 840841fa78847d12b0f71ef620dbc73905e34a48325e10864ac56bf4aa746659d12f5cbbb18847c660e019bd42700700afb04928e7ccd48871bfe427ca8ba824
7
- data.tar.gz: 11839160ebde29751173c28c718546d92a4378c316f081632f18eef2d688d569a4f1277caf14c40c4896f2168a7e4f42d0717af7482d430de13e9b04b3e77b9a
6
+ metadata.gz: 1fe965444b5640251c4ca34c5fc1fca34857f2c8cc96a8f759096165391490ca7deb7f5b3f9d8ed8c9219a5ddfa82716660e2739adb5d7dfdb2d61ca95a968a7
7
+ data.tar.gz: f9ce7c87b95ceec5f3f5d1cc46f6401ef121870bf58a8cffe1b07cb7de597b8bcd3bcd1069e98ef52007df2209b26998f3f1c05a9719eefe17351d1f6da1eebf
@@ -509,7 +509,7 @@ module ScopedSearch
509
509
  raise ScopedSearch::QueryNotSupported, "Field '#{lhs.value}' not recognized for searching!" unless field
510
510
 
511
511
  # see if the value passes user defined validation
512
- if operator == :in
512
+ if [:in, :notin].include?(operator)
513
513
  rhs.value.split(',').each { |v| validate_value(field, v) }
514
514
  else
515
515
  validate_value(field, rhs.value)
@@ -31,7 +31,7 @@ module ScopedSearch::QueryLanguage::Parser
31
31
  next_token if !root_node && peek_token == :lparen # skip starting :lparen
32
32
  expressions << parse_logical_expression until peek_token.nil? || peek_token == :rparen
33
33
  next_token if !root_node && peek_token == :rparen # skip final :rparen
34
-
34
+
35
35
  return ScopedSearch::QueryLanguage::AST::LogicalOperatorNode.new(DEFAULT_SEQUENCE_OPERATOR, expressions, root_node)
36
36
  end
37
37
 
@@ -42,18 +42,23 @@ module ScopedSearch::QueryLanguage::Parser
42
42
  when :lparen; parse_expression_sequence
43
43
  when :not; parse_logical_not_expression
44
44
  when :null, :notnull; parse_null_expression
45
+ when *LOGICAL_INFIX_OPERATORS; parse_logical_infix_expression
45
46
  else; parse_comparison
46
47
  end
47
48
 
48
49
  if LOGICAL_INFIX_OPERATORS.include?(peek_token)
49
- operator = next_token
50
- rhs = parse_logical_expression
51
- ScopedSearch::QueryLanguage::AST::LogicalOperatorNode.new(operator, [lhs, rhs])
50
+ parse_logical_infix_expression([lhs])
52
51
  else
53
52
  lhs
54
53
  end
55
54
  end
56
55
 
56
+ def parse_logical_infix_expression(previous = [])
57
+ operator = next_token
58
+ rhs = parse_logical_expression
59
+ ScopedSearch::QueryLanguage::AST::LogicalOperatorNode.new(operator, previous + [rhs])
60
+ end
61
+
57
62
  # Parses a NOT expression
58
63
  def parse_logical_not_expression
59
64
  next_token # = skip NOT operator
@@ -80,7 +85,24 @@ module ScopedSearch::QueryLanguage::Parser
80
85
 
81
86
  # Parses a prefix comparison, i.e. without an explicit field: <operator> <value>
82
87
  def parse_prefix_comparison
83
- return ScopedSearch::QueryLanguage::AST::OperatorNode.new(next_token, [parse_value])
88
+ token = next_token
89
+ case token
90
+ when :in
91
+ parse_prefix_in(true)
92
+ when :notin
93
+ parse_prefix_in(false)
94
+ else
95
+ ScopedSearch::QueryLanguage::AST::OperatorNode.new(token, [parse_value])
96
+ end
97
+ end
98
+
99
+ def parse_prefix_in(inclusion)
100
+ cmp, log = inclusion ? [:eq, :or] : [:ne, :and]
101
+ leaves = parse_multiple_values.map do |x|
102
+ leaf = ScopedSearch::QueryLanguage::AST::LeafNode.new(x)
103
+ ScopedSearch::QueryLanguage::AST::OperatorNode.new(cmp, [leaf])
104
+ end
105
+ ScopedSearch::QueryLanguage::AST::LogicalOperatorNode.new(log, leaves)
84
106
  end
85
107
 
86
108
  # Parses an infix expression, i.e. <field> <operator> <value>
@@ -109,7 +131,7 @@ module ScopedSearch::QueryLanguage::Parser
109
131
  value = []
110
132
  value << current_token if String === next_token until peek_token.nil? || peek_token == :rparen
111
133
  next_token if peek_token == :rparen # consume the :rparen
112
- value.join(',')
134
+ value
113
135
  end
114
136
 
115
137
  # This can either be a constant value or a field name.
@@ -117,7 +139,7 @@ module ScopedSearch::QueryLanguage::Parser
117
139
  if String === peek_token
118
140
  ScopedSearch::QueryLanguage::AST::LeafNode.new(next_token)
119
141
  elsif ([:in, :notin].include? current_token)
120
- value = parse_multiple_values()
142
+ value = parse_multiple_values().join(',')
121
143
  ScopedSearch::QueryLanguage::AST::LeafNode.new(value)
122
144
  else
123
145
  raise ScopedSearch::QueryNotSupported, "Value expected but found #{peek_token.inspect}"
@@ -1,3 +1,3 @@
1
1
  module ScopedSearch
2
- VERSION = "4.1.11"
2
+ VERSION = "4.1.13"
3
3
  end
@@ -102,7 +102,27 @@ describe ScopedSearch::QueryLanguage::Parser do
102
102
  'set? a b null? c'.should parse_to([:and, [:notnull, 'a'], 'b', [:null, 'c']])
103
103
  end
104
104
 
105
+ it 'should parse logical operators with a single argument' do
106
+ '& a'.should parse_to('a')
107
+ '& & & & a & b & & &'.should parse_to([:and, 'a', 'b'])
108
+ end
109
+
110
+ it 'should parse in and not in operators with no lhs' do
111
+ '^ a'.should parse_to([:eq, 'a'])
112
+ '^ a b'.should parse_to([:or, [:eq, 'a'], [:eq, 'b']])
113
+ '^ a,b'.should parse_to([:or, [:eq, 'a'], [:eq, 'b']])
114
+
115
+ '^ (a b)'.should parse_to([:or, [:eq, 'a'], [:eq, 'b']])
116
+ '^ (a,b)'.should parse_to([:or, [:eq, 'a'], [:eq, 'b']])
117
+
118
+ '!^ a'.should parse_to([:ne, 'a'])
119
+ '!^ a b'.should parse_to([:and, [:ne, 'a'], [:ne, 'b']])
120
+ '!^ a,b'.should parse_to([:and, [:ne, 'a'], [:ne, 'b']])
121
+ '!^ (a b)'.should parse_to([:and, [:ne, 'a'], [:ne, 'b']])
122
+ '!^ (a,b)'.should parse_to([:and, [:ne, 'a'], [:ne, 'b']])
123
+ end
124
+
105
125
  it "should refuse to parse an empty not expression" do
106
126
  lambda { ScopedSearch::QueryLanguage::Compiler.parse('!()|*') }.should raise_error(ScopedSearch::QueryNotSupported)
107
- end
127
+ end
108
128
  end
@@ -68,6 +68,8 @@ describe ScopedSearch::QueryBuilder do
68
68
 
69
69
  lambda { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field ^ (1,2)') }.should_not raise_error
70
70
  lambda { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field ^ (1,a)') }.should raise_error(ScopedSearch::QueryNotSupported)
71
+ lambda { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field !^ (1,2)') }.should_not raise_error
72
+ lambda { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field !^ (1,a)') }.should raise_error(ScopedSearch::QueryNotSupported)
71
73
  end
72
74
 
73
75
  it "should display custom error from validator" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scoped_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.11
4
+ version: 4.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amos Benari
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-06-05 00:00:00.000000000 Z
13
+ date: 2024-12-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
167
  requirements: []
168
- rubygems_version: 3.4.13
168
+ rubygems_version: 3.3.27
169
169
  signing_key:
170
170
  specification_version: 4
171
171
  summary: Easily search you ActiveRecord models with a simple query language using