blacklight_advanced_search 6.0.2 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.rubocop.yml +15 -0
  4. data/.rubocop_todo.yml +351 -0
  5. data/.solr_wrapper.yml +5 -0
  6. data/.travis.yml +4 -7
  7. data/Gemfile +18 -11
  8. data/Rakefile +24 -34
  9. data/VERSION +1 -1
  10. data/app/controllers/advanced_controller.rb +5 -7
  11. data/app/controllers/blacklight_advanced_search/advanced_controller.rb +5 -8
  12. data/app/helpers/advanced_helper.rb +4 -6
  13. data/blacklight_advanced_search.gemspec +11 -8
  14. data/lib/blacklight_advanced_search.rb +29 -34
  15. data/lib/blacklight_advanced_search/advanced_query_parser.rb +12 -13
  16. data/lib/blacklight_advanced_search/advanced_search_builder.rb +28 -32
  17. data/lib/blacklight_advanced_search/catalog_helper_override.rb +11 -34
  18. data/lib/blacklight_advanced_search/controller.rb +1 -1
  19. data/lib/blacklight_advanced_search/filter_parser.rb +7 -9
  20. data/lib/blacklight_advanced_search/parsing_nesting_parser.rb +5 -8
  21. data/lib/blacklight_advanced_search/redirect_legacy_params_filter.rb +23 -25
  22. data/lib/blacklight_advanced_search/render_constraints_override.rb +46 -33
  23. data/lib/blacklight_advanced_search/version.rb +0 -1
  24. data/lib/generators/blacklight_advanced_search/assets_generator.rb +4 -8
  25. data/lib/generators/blacklight_advanced_search/blacklight_advanced_search_generator.rb +0 -2
  26. data/lib/generators/blacklight_advanced_search/install_generator.rb +9 -5
  27. data/lib/generators/blacklight_advanced_search/templates/advanced_controller.rb +0 -2
  28. data/lib/parsing_nesting/grammar.rb +22 -25
  29. data/lib/parsing_nesting/tree.rb +156 -168
  30. data/solr/conf/_rest_managed.json +3 -0
  31. data/solr/conf/admin-extra.html +31 -0
  32. data/solr/conf/elevate.xml +36 -0
  33. data/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  34. data/solr/conf/protwords.txt +21 -0
  35. data/solr/conf/schema.xml +635 -0
  36. data/solr/conf/scripts.conf +24 -0
  37. data/solr/conf/solrconfig.xml +411 -0
  38. data/solr/conf/spellings.txt +2 -0
  39. data/solr/conf/stopwords.txt +58 -0
  40. data/solr/conf/stopwords_en.txt +58 -0
  41. data/solr/conf/synonyms.txt +31 -0
  42. data/solr/conf/xslt/example.xsl +132 -0
  43. data/solr/conf/xslt/example_atom.xsl +67 -0
  44. data/solr/conf/xslt/example_rss.xsl +66 -0
  45. data/solr/conf/xslt/luke.xsl +337 -0
  46. data/solr/sample_solr_documents.yml +2692 -0
  47. data/spec/features/blacklight_advanced_search_form_spec.rb +0 -2
  48. data/spec/helpers/advanced_helper_spec.rb +0 -2
  49. data/spec/integration/blacklight_stub_spec.rb +0 -2
  50. data/spec/lib/advanced_search_builder_spec.rb +7 -14
  51. data/spec/lib/blacklight_advanced_search/render_constraints_override_spec.rb +39 -0
  52. data/spec/lib/deep_merge_spec.rb +109 -34
  53. data/spec/lib/filter_parser_spec.rb +8 -14
  54. data/spec/parsing_nesting/build_tree_spec.rb +73 -81
  55. data/spec/parsing_nesting/consuming_spec.rb +2 -12
  56. data/spec/parsing_nesting/to_solr_spec.rb +93 -130
  57. data/spec/spec_helper.rb +0 -3
  58. data/spec/test_app_templates/app/controllers/catalog_controller.rb +3 -3
  59. data/spec/test_app_templates/lib/generators/test_app_generator.rb +3 -3
  60. metadata +63 -13
  61. data/spec/spec.opts +0 -4
@@ -1,18 +1,17 @@
1
1
  require 'parsing_nesting/grammar'
2
2
  module ParsingNesting::Tree
3
-
4
3
  # Get parslet output for string (parslet output is json-y objects), and
5
4
  # transform to an actual abstract syntax tree made up of more semantic
6
- # ruby objects, Node's. The top one will always be a List.
7
- #
5
+ # ruby objects, Node's. The top one will always be a List.
6
+ #
8
7
  # Call #to_query on resulting Node in order to transform to Solr query,
9
8
  # optionally passing in Solr params to be used as LocalParams in nested
10
- # dismax queries.
9
+ # dismax queries.
11
10
  #
12
11
  # Our approach here works, but as we have to put in special cases
13
12
  # it starts getting messy. Ideally we might want to actually transform
14
13
  # the Object graph (abstract syntax tree) instead of trying to handle
15
- # special cases in #to_query.
14
+ # special cases in #to_query.
16
15
  # For instance, transform object graph for a problematic pure-negative
17
16
  # clause to the corresponding object graph without that (-a AND -b) ==>
18
17
  # (NOT (a OR b). Transform (NOT NOT a) to (a). That would probably be
@@ -21,117 +20,115 @@ module ParsingNesting::Tree
21
20
  # multiple levels. But it's working for now.
22
21
  #
23
22
  # the #negate method was an experiment in transforming parse tree in
24
- # place, but isn't being used. But it's left as a sign post.
25
- def self.parse(string, query_parser='dismax')
23
+ # place, but isn't being used. But it's left as a sign post.
24
+ def self.parse(string, query_parser = 'dismax')
26
25
  to_node_tree(ParsingNesting::Grammar.new.parse(string), query_parser)
27
26
  end
28
-
29
27
 
30
28
  # theoretically Parslet's Transform could be used for this, but I think the
31
29
  # manner in which I'm parsing to Parslet labelled hash isn't exactly what
32
30
  # Parslet Transform is set up to work with, I couldn't figure it out. But
33
31
  # easy enough to do 'manually'.
34
32
  def self.to_node_tree(tree, query_parser)
35
- if tree.kind_of? Array
33
+ if tree.is_a? Array
36
34
  # at one point I was normalizing top-level lists of one item to just
37
35
  # be that item, no list wrapper. But having the list wrapper
38
- # at the top level is actually useful for Solr output.
39
- List.new( tree.collect {|i| to_node_tree(i, query_parser)}, query_parser)
40
- elsif tree.kind_of? Hash
36
+ # at the top level is actually useful for Solr output.
37
+ List.new(tree.collect { |i| to_node_tree(i, query_parser) }, query_parser)
38
+ elsif tree.is_a? Hash
41
39
  if list = tree[:list]
42
- List.new( list.collect {|i| to_node_tree(i, query_parser)}, query_parser)
40
+ List.new(list.collect { |i| to_node_tree(i, query_parser) }, query_parser)
43
41
  elsif tree.has_key?(:and_list)
44
- AndList.new( tree[:and_list].collect{|i| to_node_tree(i, query_parser) }, query_parser)
42
+ AndList.new(tree[:and_list].collect { |i| to_node_tree(i, query_parser) }, query_parser)
45
43
  elsif tree.has_key?(:or_list)
46
- OrList.new( tree[:or_list].collect{|i| to_node_tree(i, query_parser) }, query_parser )
44
+ OrList.new(tree[:or_list].collect { |i| to_node_tree(i, query_parser) }, query_parser)
47
45
  elsif not_payload = tree[:not_expression]
48
- NotExpression.new( to_node_tree(not_payload, query_parser) )
46
+ NotExpression.new(to_node_tree(not_payload, query_parser))
49
47
  elsif tree.has_key?(:mandatory)
50
- MandatoryClause.new( to_node_tree(tree[:mandatory], query_parser))
48
+ MandatoryClause.new(to_node_tree(tree[:mandatory], query_parser))
51
49
  elsif tree.has_key?(:excluded)
52
- ExcludedClause.new( to_node_tree(tree[:excluded], query_parser))
50
+ ExcludedClause.new(to_node_tree(tree[:excluded], query_parser))
53
51
  elsif phrase = tree[:phrase]
54
- Phrase.new( phrase )
52
+ Phrase.new(phrase)
55
53
  elsif tree.has_key?(:token)
56
- Term.new( tree[:token].to_s )
54
+ Term.new(tree[:token].to_s)
57
55
  end
58
56
  end
59
57
  end
60
-
58
+
61
59
  class Node
62
60
  # this default to_query works well for anything that is embeddable in
63
- # a standard way.
64
- # non-embeddable nodes will have to override and do it different.
61
+ # a standard way.
62
+ # non-embeddable nodes will have to override and do it different.
65
63
  def to_query(solr_params)
66
64
  build_nested_query([self], solr_params)
67
65
  end
68
-
66
+
69
67
  protected # some utility methods
70
-
68
+
71
69
  # Builds a query from a list of Node's that have #to_embed, and some
72
70
  # solr params to embed as LocalParams.
73
71
  #
74
72
  # By default will create a nested _query_, handling escaping appropriately.
75
- # but pass in :always_nested=>false, and it will sometimes be an ordinary
76
- # query where possible. (possibly still with LocalParams).
73
+ # but pass in :always_nested=>false, and it will sometimes be an ordinary
74
+ # query where possible. (possibly still with LocalParams).
77
75
  #
78
76
  # LocalParams will be default have "!dismax" added to them, but set
79
77
  # :force_deftype to something else (or nil) if you want.
80
78
  #
81
79
  # Also takes care of simple "pure negative" queries like "-one -two",
82
80
  # converting them to a nested NOT query that will be handled appropriately.
83
- # those simple negatives can't be handled right by dismax otherwise.
84
- def build_nested_query(embeddables, solr_params={}, options = {})
85
- options = {:always_nested => true,
86
- :force_deftype => "dismax"}.merge(options)
87
-
81
+ # those simple negatives can't be handled right by dismax otherwise.
82
+ def build_nested_query(embeddables, solr_params = {}, options = {})
83
+ options = { :always_nested => true,
84
+ :force_deftype => "dismax" }.merge(options)
85
+
88
86
  # if it's pure negative, we need to transform
89
- if embeddables.find_all{|n| n.kind_of?(ExcludedClause)}.length == embeddables.length
90
- negated = NotExpression.new( List.new(embeddables.collect {|n| n.operand}, options[:force_deftype] ))
91
- solr_params = solr_params.merge(:mm => "1")
92
- return negated.to_query(solr_params)
87
+ if embeddables.find_all { |n| n.is_a?(ExcludedClause) }.length == embeddables.length
88
+ negated = NotExpression.new(List.new(embeddables.collect { |n| n.operand }, options[:force_deftype]))
89
+ solr_params = solr_params.merge(:mm => "1")
90
+ return negated.to_query(solr_params)
93
91
  else
94
-
95
- inner_query = build_local_params(solr_params, options[:force_deftype]) +
96
- embeddables.collect {|n| n.to_embed}.join(" ")
97
-
98
- if options[:always_nested]
92
+
93
+ inner_query = build_local_params(solr_params, options[:force_deftype]) +
94
+ embeddables.collect { |n| n.to_embed }.join(" ")
95
+
96
+ if options[:always_nested]
99
97
  return '_query_:"' + bs_escape(inner_query) + '"'
100
98
  else
101
99
  return inner_query
102
100
  end
103
101
 
104
- end
102
+ end
105
103
  end
106
104
 
107
105
  # Pass in nil 2nd argument if you DON'T want to embed
108
106
  # "!dismax" in your local params. Used by #to_single_query_params
109
107
  def build_local_params(hash = {}, force_deftype = "dismax")
110
108
  # we insist on dismax for our embedded queries, or whatever
111
- # other defType supplied in 2nd argument.
109
+ # other defType supplied in 2nd argument.
112
110
  hash = hash.dup
113
111
  if force_deftype
114
112
  hash[:defType] = force_deftype
115
113
  hash.delete("defType") # avoid weird colision with hard to debug results
116
- end
117
-
118
- if (hash.size > 0)
114
+ end
115
+
116
+ if !hash.empty?
119
117
  defType = hash.delete(:defType) || hash.delete("defType")
120
- "{!" + (defType ? "#{defType} " : "") + hash.collect {|k,v| "#{k}=#{ v.to_s.include?(" ") ? "'"+v+"'" : v }"}.join(" ") + "}"
118
+ "{!" + (defType ? "#{defType} " : "") + hash.collect { |k, v| "#{k}=#{v.to_s.include?(" ") ? "'" + v + "'" : v}" }.join(" ") + "}"
121
119
  else
122
- #no local params!
120
+ # no local params!
123
121
  ""
124
122
  end
125
123
  end
126
-
127
- def bs_escape(val, char='"')
124
+
125
+ def bs_escape(val, char = '"')
128
126
  # crazy double escaping to actually get a single backslash
129
127
  # in there without triggering regexp capture reference
130
128
  val.gsub(char, '\\\\' + char)
131
129
  end
132
130
  end
133
-
134
-
131
+
135
132
  class List < Node
136
133
  attr_accessor :list
137
134
  attr_reader :query_parser
@@ -139,143 +136,139 @@ module ParsingNesting::Tree
139
136
  @query_parser = query_parser
140
137
  self.list = aList
141
138
  end
139
+
142
140
  def can_embed?
143
141
  false
144
142
  end
145
-
146
- def simple_pure_negative?
147
- (list.find_all {|i| i.kind_of? ExcludedClause }.length) == list.length
148
- end
149
-
150
- def to_query(solr_params={})
143
+
144
+ def simple_pure_negative?
145
+ list.find_all { |i| i.is_a? ExcludedClause }.length == list.length
146
+ end
147
+
148
+ def to_query(solr_params = {})
151
149
  queries = []
152
-
153
- (embeddable, gen_full_query) = list.partition {|i| i.respond_to?(:can_embed?) && i.can_embed?}
154
-
150
+
151
+ (embeddable, gen_full_query) = list.partition { |i| i.respond_to?(:can_embed?) && i.can_embed? }
152
+
155
153
  unless embeddable.empty?
156
154
  queries << build_nested_query(embeddable, solr_params, force_deftype: query_parser)
157
155
  end
158
-
156
+
159
157
  gen_full_query.each do |node|
160
158
  queries << node.to_query(solr_params)
161
159
  end
162
-
160
+
163
161
  queries.join(" AND ")
164
162
  end
165
163
 
166
164
  # Returns a Hash, assumes this will be the ONLY :q, used for
167
165
  # parsing 'simple search' to Solr. Pass in params that need to
168
- # be LOCAL solr params (using "{foo=bar}" embedded in query).
166
+ # be LOCAL solr params (using "{foo=bar}" embedded in query).
169
167
  # Params that should be sent to Solr seperately are caller's responsibility,
170
- # merge em into the returned hash.
168
+ # merge em into the returned hash.
171
169
  #
172
170
  # For very simple queries, this will produce an ordinary Solr q
173
171
  # much like would be produced ordinarily. But for AND/OR/NOT, will
174
- # sometimes include multiple nested queries instead.
172
+ # sometimes include multiple nested queries instead.
175
173
  #
176
174
  # This method will still sometimes return a single nested _query_, that
177
175
  # could theoretically really be ordinary query possibly with localparams.
178
176
  # It still works, but isn't optimizing for a simpler query, because
179
177
  # it's using much of the same code used for combining multiple fields
180
178
  # that need nested queries. Maybe we'll optimize later, but the code
181
- # gets tricky.
179
+ # gets tricky.
182
180
  def to_single_query_params(solr_local_params)
183
181
  # Can it be expressed in a single dismax?
184
-
185
- if list.find_all {|i| i.respond_to?(:can_embed?) && i.can_embed? }.length == list.length
186
- {
187
- #build_local_params(solr_local_params, nil) + list.collect {|n| n.to_embed}.join(" "),
188
- :q => build_nested_query(list, solr_local_params, :always_nested => false, :force_deftype => nil),
182
+
183
+ if list.find_all { |i| i.respond_to?(:can_embed?) && i.can_embed? }.length == list.length
184
+ {
185
+ # build_local_params(solr_local_params, nil) + list.collect {|n| n.to_embed}.join(" "),
186
+ :q => build_nested_query(list, solr_local_params, :always_nested => false, :force_deftype => nil),
189
187
  :defType => query_parser
190
- }
188
+ }
191
189
  else
192
190
  # Can't be expressed in a single dismax, do it the normal way
193
- {
191
+ {
194
192
  :q => self.to_query(solr_local_params),
195
- :defType => "lucene"
193
+ :defType => "lucene"
196
194
  }
197
195
  end
198
196
  end
199
-
197
+
200
198
  def negate
201
- List.new(list.collect {|i| i.negate})
199
+ List.new(list.collect { |i| i.negate })
202
200
  end
203
201
  end
204
-
202
+
205
203
  class AndList < List
206
-
207
204
  # We make an and-list embeddable only if all it's elements
208
205
  # are embeddable, then no problem we just embed them all
209
206
  # as Solr '+' mandatory, and achieve the AND.
210
207
  # For now, pure negative is considered not embeddable, although
211
208
  # theoretically it could sometimes be embedded if transformed
212
- # properly.
209
+ # properly.
213
210
  def can_embed?
214
- (! simple_pure_negative?) && ! list.collect {|i| i.can_embed?}.include?(false)
211
+ !simple_pure_negative? && !list.collect { |i| i.can_embed? }.include?(false)
215
212
  end
216
-
213
+
217
214
  # Only if all operands are embeddable.
218
215
  # Trick is if they were bare terms/phrases, we add a '+' on
219
216
  # front, but if they already were +/-, then we don't need to,
220
- # and leaving them along will have desired semantics.
217
+ # and leaving them along will have desired semantics.
221
218
  # This works even on "-", because dismax mm seems to not consider "-"
222
- # clauses, they are always required regardless of mm.
219
+ # clauses, they are always required regardless of mm.
223
220
  def to_embed
224
221
  list.collect do |operand|
225
222
  s = operand.to_embed
226
223
  if s =~ /^\+/ || s =~ /^\-/
227
224
  s
228
225
  else
229
- '+'+s
226
+ '+' + s
230
227
  end
231
228
  end.join(" ")
232
229
  end
233
-
230
+
234
231
  # for those that aren't embeddable, or pure negative
235
232
  def to_query(local_params)
236
233
  if simple_pure_negative?
237
234
  # Can do it in one single nested dismax, if we're simple arguments
238
- # that are pure negative.
235
+ # that are pure negative.
239
236
  # build_nested_query will handle negating the pure negative for
240
- # us.
237
+ # us.
241
238
  build_nested_query(list, local_params)
242
- else
243
- "( " +
244
- list.collect do |i|
245
- i.to_query(local_params)
246
- end.join(" AND ") +
247
- " )"
239
+ else
240
+ "( " +
241
+ list.collect do |i|
242
+ i.to_query(local_params)
243
+ end.join(" AND ") +
244
+ " )"
248
245
  end
249
246
  end
250
-
251
- # convent logical property here, not(a AND b) === not(a) OR not(b)
247
+
248
+ # convent logical property here, not(a AND b) === not(a) OR not(b)
252
249
  def negate
253
- OrList.new( list.collect {|n| n.negate} )
250
+ OrList.new(list.collect { |n| n.negate })
254
251
  end
255
-
256
252
  end
257
-
258
-
259
- class OrList < List
260
-
253
+
254
+ class OrList < List
261
255
  # never embeddable
262
256
  def can_embed?
263
257
  false
264
258
  end
265
-
266
-
259
+
267
260
  def to_query(local_params)
268
261
  # Okay, we're never embeddable as such, but sometimes we can
269
262
  # turn our operands into one single nested dismax query with mm=1, when
270
263
  # all our operands are 'simple', other times we need to actually do
271
- # two seperate nested queries seperated by lucene OR.
264
+ # two seperate nested queries seperated by lucene OR.
272
265
  # If all our children are embeddable but _not_ an "AndList", we can
273
266
  # do the one query part. The AndList is theoretically embeddable, but
274
- # not in a way compatible with flattening an OR to one query.
267
+ # not in a way compatible with flattening an OR to one query.
275
268
  # Sorry, this part is one of the least clean part of this code!
276
269
 
277
- not_flattenable = list.find {|i| ! (i.can_embed? && ! i.kind_of?(AndList) )}
278
-
270
+ not_flattenable = list.find { |i| !(i.can_embed? && !i.is_a?(AndList)) }
271
+
279
272
  if not_flattenable
280
273
  to_multi_queries(local_params)
281
274
  elsif simple_pure_negative?
@@ -284,156 +277,153 @@ module ParsingNesting::Tree
284
277
  to_one_dismax_query(local_params)
285
278
  end
286
279
  end
287
-
280
+
288
281
  # build_nested_query isn't smart enough to handle refactoring
289
- # a simple pure negative "OR", that needs an mm of 100%.
282
+ # a simple pure negative "OR", that needs an mm of 100%.
290
283
  # Let's just do it ourselves. What we're doing makes more sense
291
284
  # if you remember that:
292
285
  # -a OR -b === NOT (a AND b)
293
286
  def to_simple_pure_negative_query(local_params)
294
287
  # take em out of their ExcludedClauses
295
- embeddables = list.collect {|n| n.operand}
288
+ embeddables = list.collect { |n| n.operand }
296
289
  # and insist on mm 100%
297
290
  solr_params = local_params.merge(:mm => "100%")
298
-
299
- # and put the NOT in front to preserve semantics.
300
- return 'NOT _query_:"' +
301
- bs_escape(build_local_params(solr_params) +
302
- embeddables.collect {|n| n.to_embed}.join(" ")) +
291
+
292
+ # and put the NOT in front to preserve semantics.
293
+ 'NOT _query_:"' +
294
+ bs_escape(build_local_params(solr_params) +
295
+ embeddables.collect { |n| n.to_embed }.join(" ")) +
303
296
  '"'
304
297
  end
305
-
306
- # all our arguments are 'simple' (terms and phrases with +/-),
307
- # put am all in one single dismax with mm forced to 1.
308
- def to_one_dismax_query(local_params)
298
+
299
+ # all our arguments are 'simple' (terms and phrases with +/-),
300
+ # put am all in one single dismax with mm forced to 1.
301
+ def to_one_dismax_query(local_params)
309
302
  build_nested_query(list, local_params.merge(:mm => "1"))
310
303
  end
311
-
312
- def to_multi_queries(local_params)
313
- "( " +
304
+
305
+ def to_multi_queries(local_params)
306
+ "( " +
314
307
  list.collect do |i|
315
- if i.kind_of?(NotExpression) || (i.respond_to?(:simple_pure_negative?) && i.simple_pure_negative?)
308
+ if i.is_a?(NotExpression) || (i.respond_to?(:simple_pure_negative?) && i.simple_pure_negative?)
316
309
  # need special handling to work around Solr 1.4.1's lack of handling
317
310
  # of pure negative in an OR
318
311
  "(*:* AND #{i.to_query(local_params)})"
319
312
  else
320
313
  i.to_query(local_params)
321
- end
314
+ end
322
315
  end.join(" OR ") +
323
- " )"
316
+ " )"
324
317
  end
325
-
318
+
326
319
  # convenient logical property here, not(a OR b) === not(a) AND not(b)
327
320
  def negate
328
- AndList.new( list.collect {|n| n.negate})
321
+ AndList.new(list.collect { |n| n.negate })
329
322
  end
330
-
331
323
  end
332
-
333
-
334
- class NotExpression
324
+
325
+ class NotExpression
335
326
  def initialize(exp)
336
327
  self.operand = exp
337
328
  end
338
329
  attr_accessor :operand
339
-
330
+
340
331
  # We have to do the weird thing with *:* AND NOT (real thing), because
341
332
  # Solr 1.4.1 seems not to be able to handle "x OR NOT y" otherwise, at least
342
333
  # in some cases, but does fine with
343
334
  # "x OR (*:* AND NOT y)", which should mean the same thing.
344
335
  def to_query(solr_params)
345
336
  # rescue double-nots to not treat them crazy-like and make the query
346
- # more work for Solr than it needs to be with a double-negative.
347
- if operand.kind_of?(NotExpression)
337
+ # more work for Solr than it needs to be with a double-negative.
338
+ if operand.is_a?(NotExpression)
348
339
  operand.operand.to_query(solr_params)
349
340
  else
350
341
  "NOT " + operand.to_query(solr_params)
351
342
  end
352
343
  end
353
-
344
+
354
345
  def can_embed?
355
346
  false
356
347
  end
357
-
348
+
358
349
  def negate
359
350
  operand
360
351
  end
361
352
  end
362
-
353
+
363
354
  class MandatoryClause < Node
364
355
  attr_accessor :operand
365
356
  def initialize(v)
366
357
  self.operand = v
367
358
  end
368
-
359
+
369
360
  def can_embed?
370
- #right now '+' clauses only apply to terms/phrases
371
- #which we can embed with a + in front.
361
+ # right now '+' clauses only apply to terms/phrases
362
+ # which we can embed with a + in front.
372
363
  true
373
364
  end
365
+
374
366
  def to_embed
375
367
  '+' + operand.to_embed
376
368
  end
377
369
 
378
370
  # negating mandatory to excluded is decent semantics, although
379
- # it's not strictly 'true', it's a choice.
371
+ # it's not strictly 'true', it's a choice.
380
372
  def negate
381
- ExcludedClause.new( operand )
373
+ ExcludedClause.new(operand)
382
374
  end
383
375
  end
384
-
376
+
385
377
  class ExcludedClause < Node
386
378
  attr_accessor :operand
387
-
379
+
388
380
  def initialize(v)
389
381
  self.operand = v
390
- end
391
-
382
+ end
383
+
392
384
  def can_embed?
393
- #right now '-' clauses only apply to terms/phrases, which
394
- #we can embed with a '-' in front.
385
+ # right now '-' clauses only apply to terms/phrases, which
386
+ # we can embed with a '-' in front.
395
387
  true
396
388
  end
397
-
389
+
398
390
  def to_embed
399
391
  '-' + operand.to_embed
400
392
  end
401
-
393
+
402
394
  # negating excluded to mandatory is a pretty decent choice
403
395
  def negate
404
- MandatoryClause.new( operand )
396
+ MandatoryClause.new(operand)
405
397
  end
406
-
398
+
407
399
  def simple_pure_negative?
408
400
  true
409
401
  end
410
-
411
402
  end
412
-
413
-
403
+
414
404
  class Phrase < Node
415
405
  attr_accessor :value
416
-
406
+
417
407
  def initialize(string)
418
408
  self.value = string
419
409
  end
420
-
410
+
421
411
  def can_embed?
422
412
  true
423
413
  end
424
-
414
+
425
415
  def to_embed
426
416
  '"' + value + '"'
427
417
  end
428
-
418
+
429
419
  def negate
430
420
  ExcludedClause.new(self)
431
421
  end
432
422
  end
433
-
423
+
434
424
  class Term < Node
435
- attr_accessor :value
436
-
425
+ attr_accessor :value
426
+
437
427
  def initialize(string)
438
428
  self.value = string
439
429
  end
@@ -441,15 +431,13 @@ module ParsingNesting::Tree
441
431
  def can_embed?
442
432
  true
443
433
  end
444
-
434
+
445
435
  def to_embed
446
436
  value
447
- end
448
-
437
+ end
438
+
449
439
  def negate
450
440
  ExcludedClause.new(self)
451
441
  end
452
442
  end
453
443
  end
454
-
455
-