blacklight_advanced_search 6.0.2 → 6.1.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/.rspec +2 -0
- data/.rubocop.yml +15 -0
- data/.rubocop_todo.yml +351 -0
- data/.solr_wrapper.yml +5 -0
- data/.travis.yml +4 -7
- data/Gemfile +18 -11
- data/Rakefile +24 -34
- data/VERSION +1 -1
- data/app/controllers/advanced_controller.rb +5 -7
- data/app/controllers/blacklight_advanced_search/advanced_controller.rb +5 -8
- data/app/helpers/advanced_helper.rb +4 -6
- data/blacklight_advanced_search.gemspec +11 -8
- data/lib/blacklight_advanced_search.rb +29 -34
- data/lib/blacklight_advanced_search/advanced_query_parser.rb +12 -13
- data/lib/blacklight_advanced_search/advanced_search_builder.rb +28 -32
- data/lib/blacklight_advanced_search/catalog_helper_override.rb +11 -34
- data/lib/blacklight_advanced_search/controller.rb +1 -1
- data/lib/blacklight_advanced_search/filter_parser.rb +7 -9
- data/lib/blacklight_advanced_search/parsing_nesting_parser.rb +5 -8
- data/lib/blacklight_advanced_search/redirect_legacy_params_filter.rb +23 -25
- data/lib/blacklight_advanced_search/render_constraints_override.rb +46 -33
- data/lib/blacklight_advanced_search/version.rb +0 -1
- data/lib/generators/blacklight_advanced_search/assets_generator.rb +4 -8
- data/lib/generators/blacklight_advanced_search/blacklight_advanced_search_generator.rb +0 -2
- data/lib/generators/blacklight_advanced_search/install_generator.rb +9 -5
- data/lib/generators/blacklight_advanced_search/templates/advanced_controller.rb +0 -2
- data/lib/parsing_nesting/grammar.rb +22 -25
- data/lib/parsing_nesting/tree.rb +156 -168
- data/solr/conf/_rest_managed.json +3 -0
- data/solr/conf/admin-extra.html +31 -0
- data/solr/conf/elevate.xml +36 -0
- data/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
- data/solr/conf/protwords.txt +21 -0
- data/solr/conf/schema.xml +635 -0
- data/solr/conf/scripts.conf +24 -0
- data/solr/conf/solrconfig.xml +411 -0
- data/solr/conf/spellings.txt +2 -0
- data/solr/conf/stopwords.txt +58 -0
- data/solr/conf/stopwords_en.txt +58 -0
- data/solr/conf/synonyms.txt +31 -0
- data/solr/conf/xslt/example.xsl +132 -0
- data/solr/conf/xslt/example_atom.xsl +67 -0
- data/solr/conf/xslt/example_rss.xsl +66 -0
- data/solr/conf/xslt/luke.xsl +337 -0
- data/solr/sample_solr_documents.yml +2692 -0
- data/spec/features/blacklight_advanced_search_form_spec.rb +0 -2
- data/spec/helpers/advanced_helper_spec.rb +0 -2
- data/spec/integration/blacklight_stub_spec.rb +0 -2
- data/spec/lib/advanced_search_builder_spec.rb +7 -14
- data/spec/lib/blacklight_advanced_search/render_constraints_override_spec.rb +39 -0
- data/spec/lib/deep_merge_spec.rb +109 -34
- data/spec/lib/filter_parser_spec.rb +8 -14
- data/spec/parsing_nesting/build_tree_spec.rb +73 -81
- data/spec/parsing_nesting/consuming_spec.rb +2 -12
- data/spec/parsing_nesting/to_solr_spec.rb +93 -130
- data/spec/spec_helper.rb +0 -3
- data/spec/test_app_templates/app/controllers/catalog_controller.rb +3 -3
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +3 -3
- metadata +63 -13
- data/spec/spec.opts +0 -4
@@ -1,12 +1,8 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
1
|
describe BlacklightAdvancedSearch::AdvancedSearchBuilder do
|
4
|
-
|
5
2
|
describe "#add_advanced_parse_q_to_solr" do
|
6
|
-
|
7
3
|
let(:blacklight_config) do
|
8
4
|
Blacklight::Configuration.new do |config|
|
9
|
-
config.advanced_search = {
|
5
|
+
config.advanced_search = {}
|
10
6
|
config.add_search_field "all_fields"
|
11
7
|
config.add_search_field "special_field" do |field|
|
12
8
|
field.advanced_parse = false
|
@@ -19,7 +15,7 @@ describe BlacklightAdvancedSearch::AdvancedSearchBuilder do
|
|
19
15
|
cattr_accessor :blacklight_config
|
20
16
|
include Blacklight::SearchHelper
|
21
17
|
include BlacklightAdvancedSearch::AdvancedSearchBuilder
|
22
|
-
def initialize
|
18
|
+
def initialize(blacklight_config)
|
23
19
|
self.blacklight_config = blacklight_config
|
24
20
|
end
|
25
21
|
end
|
@@ -30,20 +26,20 @@ describe BlacklightAdvancedSearch::AdvancedSearchBuilder do
|
|
30
26
|
let(:solr_params) { {} }
|
31
27
|
|
32
28
|
describe "a simple example" do
|
33
|
-
let(:params) { double("params", params: {:q => "one two AND three OR four"}
|
29
|
+
let(:params) { double("params", params: { :q => "one two AND three OR four" }) }
|
34
30
|
before { allow(obj).to receive(:scope).and_return(params) }
|
35
31
|
it "catches the query" do
|
36
|
-
obj.add_advanced_parse_q_to_solr(solr_params)
|
32
|
+
obj.add_advanced_parse_q_to_solr(solr_params)
|
37
33
|
expect(solr_params[:defType]).to eq("lucene")
|
38
34
|
# We're not testing succesful parsing here, just that it's doing
|
39
|
-
# something that looks like we expect with subqueries.
|
35
|
+
# something that looks like we expect with subqueries.
|
40
36
|
expect(solr_params[:q]).to start_with("_query_:")
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
44
40
|
describe "an unparseable example" do
|
45
41
|
let(:unparseable_q) { "foo bar\'s AND" }
|
46
|
-
let(:params) { double("params", params: {:q => unparseable_q}
|
42
|
+
let(:params) { double("params", params: { :q => unparseable_q }) }
|
47
43
|
before { allow(obj).to receive(:scope).and_return(params) }
|
48
44
|
it "passes through" do
|
49
45
|
obj.add_advanced_parse_q_to_solr(solr_params)
|
@@ -52,16 +48,13 @@ describe BlacklightAdvancedSearch::AdvancedSearchBuilder do
|
|
52
48
|
end
|
53
49
|
|
54
50
|
context "when advanced_parse is false" do
|
55
|
-
let(:params) { double("params", params: { :search_field => "special_field", :q => "one two AND three OR four" }
|
51
|
+
let(:params) { double("params", params: { :search_field => "special_field", :q => "one two AND three OR four" }) }
|
56
52
|
before { allow(obj).to receive(:scope).and_return(params) }
|
57
53
|
it "ignores fields" do
|
58
54
|
obj.add_advanced_parse_q_to_solr(solr_params)
|
59
55
|
expect(solr_params).not_to have_key(:q)
|
60
56
|
end
|
61
57
|
end
|
62
|
-
|
63
58
|
end
|
64
|
-
|
65
59
|
end
|
66
|
-
|
67
60
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
describe BlacklightAdvancedSearch::RenderConstraintsOverride, type: :helper do
|
2
|
+
let(:blacklight_config) do
|
3
|
+
Blacklight::Configuration.new do |config|
|
4
|
+
config.add_facet_field 'type'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:advanced_query) do
|
9
|
+
BlacklightAdvancedSearch::QueryParser.new(params, blacklight_config)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#render_constraints_filters" do
|
13
|
+
before do
|
14
|
+
allow(helper).to receive(:blacklight_config).and_return(blacklight_config)
|
15
|
+
allow(helper).to receive(:advanced_query).and_return(advanced_query)
|
16
|
+
allow(helper).to receive(:search_action_path) do |*args|
|
17
|
+
search_catalog_path(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
subject(:rendered) { helper.render_constraints_filters({}) }
|
22
|
+
|
23
|
+
context 'with an array of facet params' do
|
24
|
+
let(:params) { ActionController::Parameters.new f_inclusive: { 'type' => ['a'] } }
|
25
|
+
|
26
|
+
it "renders nothing" do
|
27
|
+
expect(rendered).to have_text 'Remove constraint Type: a'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with scalar facet limit params' do
|
32
|
+
let(:params) { ActionController::Parameters.new f_inclusive: { 'type' => 'a' } }
|
33
|
+
|
34
|
+
it "renders the scalar value" do
|
35
|
+
expect(rendered).to have_text 'Remove constraint Type: a'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/spec/lib/deep_merge_spec.rb
CHANGED
@@ -1,45 +1,120 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
describe 'BlacklightAdvancedSearch#deep_merge' do
|
2
|
+
let(:hash_X) do
|
3
|
+
{
|
4
|
+
'a' => 'a',
|
5
|
+
'b' => 'b',
|
6
|
+
'array1' => [1, 2],
|
7
|
+
'array2' => [3, 4],
|
8
|
+
'hash1' => { 'a' => 'a', 'array' => [1], 'b' => 'b' },
|
9
|
+
'hash2' => { 'a2' => 'a2', 'array2' => [12], 'b2' => 'b2' }
|
10
|
+
}
|
11
|
+
end
|
12
|
+
let(:hash_Y) do
|
13
|
+
{
|
14
|
+
'a' => 'NEW A',
|
15
|
+
'c' => 'NEW C',
|
16
|
+
'array1' => [3, 4],
|
17
|
+
'hash1' => { 'array' => [2], 'b' => 'NEW B' }
|
9
18
|
}
|
10
|
-
|
11
|
-
BlacklightAdvancedSearch.deep_merge!(@ahash, {
|
12
|
-
"a" => "NEW A",
|
13
|
-
"array1" => [3, 4],
|
14
|
-
"hash1" => {
|
15
|
-
"array" => [2],
|
16
|
-
"b" => "NEW B"
|
17
|
-
},
|
18
|
-
"c" => "NEW C"
|
19
|
-
})
|
20
19
|
end
|
20
|
+
let(:ahash) do
|
21
|
+
BlacklightAdvancedSearch.deep_merge(hash_x, hash_y)
|
22
|
+
end
|
23
|
+
|
24
|
+
RSpec.shared_examples 'Mergable Parameters' do # this name referenced below
|
25
|
+
it 'does not modify the param hashes' do
|
26
|
+
dup_x = hash_x.dup
|
27
|
+
dup_y = hash_y.dup
|
28
|
+
expect(ahash).not_to eq hash_x # this was the old behavior
|
29
|
+
expect(dup_x).to eq hash_x
|
30
|
+
expect(dup_y).to eq hash_y
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'leaves un-collided content alone' do
|
34
|
+
expect(ahash['b']).to eq('b')
|
35
|
+
expect(ahash['array2']).to eq([3, 4])
|
36
|
+
expect(ahash['hash2']).to eq('a2' => 'a2', 'array2' => [12], 'b2' => 'b2')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'adds new content' do
|
40
|
+
expect(ahash['c']).to eq('NEW C')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'merges a hash, recursive like' do
|
44
|
+
expect(ahash['hash1']).to eq('a' => 'a', 'array' => [1, 2], 'b' => 'NEW B')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'merges boolean values (false)' do
|
48
|
+
expect(BlacklightAdvancedSearch.deep_merge({ a: false }, a: true)).to eq(a: true)
|
49
|
+
expect(BlacklightAdvancedSearch.deep_merge({ a: true }, a: false)).to eq(a: false)
|
50
|
+
end
|
21
51
|
|
52
|
+
it 'does not merge nil values over existing keys' do
|
53
|
+
expect(BlacklightAdvancedSearch.deep_merge({ a: 1 }, a: nil)).to eq(a: 1)
|
54
|
+
end
|
22
55
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
56
|
+
it 'does merge nil values when the key is not yet present' do
|
57
|
+
expect(BlacklightAdvancedSearch.deep_merge({}, a: nil)).to eq(a: nil)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'does not merge empty strings over existing keys' do
|
61
|
+
expect(BlacklightAdvancedSearch.deep_merge({ a: 1 }, a: '')).to eq(a: 1)
|
62
|
+
expect(BlacklightAdvancedSearch.deep_merge({ a: nil }, a: '')).to eq(a: nil)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'does not merge empty strings when the key is not yet present' do
|
66
|
+
expect(BlacklightAdvancedSearch.deep_merge({}, a: '')).to eq(a: '')
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'Arrays' do
|
70
|
+
it 'merges an array' do
|
71
|
+
expect(ahash['array1']).to eq([1, 2, 3, 4])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'collapse to uniq values when merging' do
|
75
|
+
expect(BlacklightAdvancedSearch.deep_merge({ a: [1, 1, 2, 1] }, a: [3, 2])).to eq(a: [1, 2, 3])
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'does not collapse to uniq values if not merging' do
|
79
|
+
expect(BlacklightAdvancedSearch.deep_merge({ a: [1, 1, 2, 1] }, a: [])).to eq(a: [1, 1, 2, 1])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe Hash do
|
85
|
+
it_behaves_like 'Mergable Parameters' do
|
86
|
+
let(:hash_x) { hash_X }
|
87
|
+
let(:hash_y) { hash_Y }
|
88
|
+
end
|
27
89
|
end
|
28
90
|
|
29
|
-
|
30
|
-
|
91
|
+
describe HashWithIndifferentAccess do
|
92
|
+
it_behaves_like 'Mergable Parameters' do
|
93
|
+
let(:hash_x) { hash_X.with_indifferent_access }
|
94
|
+
let(:hash_y) { hash_Y.with_indifferent_access }
|
95
|
+
end
|
31
96
|
end
|
32
97
|
|
33
|
-
|
34
|
-
|
98
|
+
describe 'Mixed Hash and HWIA' do
|
99
|
+
it_behaves_like 'Mergable Parameters' do
|
100
|
+
let(:hash_x) { hash_X }
|
101
|
+
let(:hash_y) { hash_Y.with_indifferent_access }
|
102
|
+
end
|
103
|
+
|
104
|
+
it_behaves_like 'Mergable Parameters' do
|
105
|
+
let(:hash_x) { hash_X.with_indifferent_access }
|
106
|
+
let(:hash_y) { hash_Y }
|
107
|
+
end
|
35
108
|
end
|
36
109
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
110
|
+
# from http://apidock.com/rails/v4.2.1/Hash/deep_merge
|
111
|
+
describe 'reference example' do
|
112
|
+
it 'gives the same result as Rails Hash .deep_merge' do
|
113
|
+
h1 = { a: true, b: { c: [1, 2, 3] } }
|
114
|
+
h2 = { a: false, b: { x: [3, 4, 5] } }
|
115
|
+
merged = { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
|
116
|
+
expect(h1.deep_merge(h2)).to eq(merged)
|
117
|
+
expect(BlacklightAdvancedSearch.deep_merge(h1, h2)).to eq(merged)
|
118
|
+
end
|
43
119
|
end
|
44
|
-
|
45
|
-
end
|
120
|
+
end
|
@@ -1,28 +1,22 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
|
3
1
|
def setFilters(f)
|
4
2
|
@filters = f
|
5
3
|
end
|
6
4
|
|
5
|
+
## These should be reworked, but attr_reader actually breaks it.
|
6
|
+
# rubocop:disable Style/TrivialAccessors
|
7
7
|
def filters
|
8
8
|
@filters
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
11
|
describe "BlacklightAdvancedSearch::FilterParser" do
|
13
12
|
include BlacklightAdvancedSearch::FilterParser
|
14
|
-
|
13
|
+
|
15
14
|
describe "filter processing" do
|
16
15
|
it "should generate an appropriate fq param" do
|
17
|
-
setFilters(:format =>
|
18
|
-
|
16
|
+
setFilters(:format => %w(Book Thesis), :location => %w(Online Library))
|
19
17
|
fq_params = generate_solr_fq
|
20
|
-
|
21
|
-
expect(fq_params.find {|a| a =~ /
|
22
|
-
|
23
|
-
expect(fq_params.find {|a| a =~ /location\:\((\"Library\"|\"Online\") +OR +(\"Library\"|\"Online\")/}).not_to be_nil
|
24
|
-
|
25
|
-
|
26
|
-
end
|
18
|
+
expect(fq_params.find { |a| a =~ /format\:\((\"Book\"|\"Thesis\") +OR +(\"Thesis\"|\"Book\")/ }).not_to be_nil
|
19
|
+
expect(fq_params.find { |a| a =~ /location\:\((\"Library\"|\"Online\") +OR +(\"Library\"|\"Online\")/ }).not_to be_nil
|
20
|
+
end
|
27
21
|
end
|
28
|
-
end
|
22
|
+
end
|
@@ -1,58 +1,57 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
|
3
1
|
require 'parsing_nesting/grammar'
|
4
2
|
require 'parsing_nesting/tree'
|
5
3
|
|
6
|
-
|
7
4
|
module ParseTreeSpecHelper
|
8
5
|
include ParsingNesting::Tree
|
9
|
-
|
6
|
+
|
10
7
|
def parse(s)
|
11
8
|
ParsingNesting::Tree.parse(s)
|
12
9
|
end
|
13
|
-
|
14
|
-
# for things expected to be a one-element list,
|
10
|
+
|
11
|
+
# for things expected to be a one-element list,
|
15
12
|
# make sure they are and return the element
|
16
13
|
def parse_one_element(s)
|
17
14
|
l = parse(s)
|
18
|
-
expect(l).to be_kind_of(
|
15
|
+
expect(l).to be_kind_of(List)
|
19
16
|
expect(l.list.length).to eq(1)
|
20
|
-
|
17
|
+
l.list.first
|
21
18
|
end
|
22
|
-
|
19
|
+
|
23
20
|
def should_be_and_list(graph)
|
24
|
-
expect(graph).to be_kind_of(
|
21
|
+
expect(graph).to be_kind_of(AndList)
|
25
22
|
yield graph.list if block_given?
|
26
23
|
end
|
27
|
-
|
24
|
+
|
28
25
|
def should_be_list(graph)
|
29
|
-
expect(graph).to be_kind_of(
|
26
|
+
expect(graph).to be_kind_of(List)
|
30
27
|
yield graph.list if block_given?
|
31
28
|
end
|
32
|
-
|
29
|
+
|
33
30
|
def should_be_or_list(graph)
|
34
|
-
expect(graph).to be_kind_of(
|
31
|
+
expect(graph).to be_kind_of(OrList)
|
35
32
|
yield graph.list if block_given?
|
36
33
|
end
|
37
|
-
|
34
|
+
|
38
35
|
def should_be_term(graph, value)
|
39
|
-
expect(graph).to be_kind_of(
|
36
|
+
expect(graph).to be_kind_of(Term)
|
40
37
|
expect(graph.value).to eq(value)
|
41
38
|
end
|
42
|
-
|
39
|
+
|
43
40
|
def should_be_phrase(graph, value)
|
44
|
-
expect(graph).to be_kind_of(
|
41
|
+
expect(graph).to be_kind_of(Phrase)
|
45
42
|
expect(graph.value).to eq(value)
|
46
43
|
end
|
47
|
-
|
44
|
+
|
48
45
|
def should_be_mandatory(graph)
|
49
46
|
expect(graph).to be_kind_of(MandatoryClause)
|
50
47
|
yield graph.operand if block_given?
|
51
48
|
end
|
49
|
+
|
52
50
|
def should_be_excluded(graph)
|
53
51
|
expect(graph).to be_kind_of(ExcludedClause)
|
54
52
|
yield graph.operand if block_given?
|
55
53
|
end
|
54
|
+
|
56
55
|
def should_be_not_expression(graph)
|
57
56
|
expect(graph).to be_kind_of(NotExpression)
|
58
57
|
yield graph.operand if block_given?
|
@@ -60,38 +59,37 @@ module ParseTreeSpecHelper
|
|
60
59
|
end
|
61
60
|
|
62
61
|
describe "NestingParser" do
|
63
|
-
|
62
|
+
describe "Building an Object parse tree" do
|
64
63
|
include ParseTreeSpecHelper
|
65
|
-
|
66
|
-
|
64
|
+
|
67
65
|
it "should build for term list" do
|
68
|
-
should_be_list parse("one two three")
|
66
|
+
should_be_list parse("one two three") do |list|
|
69
67
|
expect(list.length).to eq(3)
|
70
68
|
should_be_term list[0], "one"
|
71
69
|
should_be_term list[1], "two"
|
72
|
-
should_be_term list[2], "three"
|
73
|
-
end
|
70
|
+
should_be_term list[2], "three"
|
71
|
+
end
|
74
72
|
end
|
75
|
-
|
73
|
+
|
76
74
|
it "should build AND list" do
|
77
75
|
should_be_and_list parse_one_element("one AND two AND three") do |list|
|
78
76
|
expect(list.length).to eq(3)
|
79
|
-
|
77
|
+
|
80
78
|
should_be_term list[0], "one"
|
81
79
|
should_be_term list[1], "two"
|
82
|
-
should_be_term list[2], "three"
|
80
|
+
should_be_term list[2], "three"
|
83
81
|
end
|
84
82
|
end
|
85
|
-
|
83
|
+
|
86
84
|
it "should build OR list" do
|
87
|
-
should_be_or_list parse_one_element("one OR two OR three") do |list|
|
85
|
+
should_be_or_list parse_one_element("one OR two OR three") do |list|
|
88
86
|
expect(list.length).to eq(3)
|
89
87
|
should_be_term list[0], "one"
|
90
88
|
should_be_term list[1], "two"
|
91
|
-
should_be_term list[2], "three"
|
92
|
-
end
|
89
|
+
should_be_term list[2], "three"
|
90
|
+
end
|
93
91
|
end
|
94
|
-
|
92
|
+
|
95
93
|
it "allows AND list of lists" do
|
96
94
|
should_be_and_list parse_one_element('(one two) AND (blue yellow)') do |and_list|
|
97
95
|
expect(and_list.length).to eq(2)
|
@@ -100,125 +98,123 @@ describe "NestingParser" do
|
|
100
98
|
should_be_term(list[1], "two")
|
101
99
|
end
|
102
100
|
should_be_list and_list[1]
|
103
|
-
end
|
101
|
+
end
|
104
102
|
end
|
105
|
-
|
103
|
+
|
106
104
|
it "should build for mandatory and excluded" do
|
107
105
|
should_be_list parse("+one -two") do |list|
|
108
106
|
expect(list.length).to eq(2)
|
109
|
-
|
107
|
+
|
110
108
|
should_be_mandatory list[0] do |operand|
|
111
109
|
should_be_term(operand, "one")
|
112
110
|
end
|
113
|
-
|
111
|
+
|
114
112
|
should_be_excluded list[1] do |operand|
|
115
113
|
should_be_term(operand, "two")
|
116
114
|
end
|
117
|
-
end
|
115
|
+
end
|
118
116
|
end
|
119
|
-
|
117
|
+
|
120
118
|
it "should build phrases" do
|
121
119
|
should_be_list parse('"quick brown" +"jumps over" -"lazy dog"') do |list|
|
122
120
|
expect(list.length).to eq(3)
|
123
|
-
|
121
|
+
|
124
122
|
should_be_phrase(list[0], "quick brown")
|
125
|
-
|
123
|
+
|
126
124
|
should_be_mandatory(list[1]) do |operand|
|
127
125
|
should_be_phrase(operand, "jumps over")
|
128
|
-
end
|
126
|
+
end
|
129
127
|
end
|
130
128
|
end
|
131
|
-
|
129
|
+
|
132
130
|
it "should leave phrase literals literal, including weird chars" do
|
133
|
-
phrase_content = "foo+bar -i: '(baz"
|
134
|
-
should_be_phrase parse_one_element("\"#{phrase_content}\""), phrase_content
|
131
|
+
phrase_content = "foo+bar -i: '(baz"
|
132
|
+
should_be_phrase parse_one_element("\"#{phrase_content}\""), phrase_content
|
135
133
|
end
|
136
|
-
|
134
|
+
|
137
135
|
it "should build for NOT on term" do
|
138
136
|
should_be_list parse("one two three NOT four") do |list|
|
139
137
|
should_be_not_expression list[3] do |operand|
|
140
138
|
should_be_term(operand, "four")
|
141
139
|
end
|
142
|
-
end
|
140
|
+
end
|
143
141
|
end
|
144
|
-
|
142
|
+
|
145
143
|
it "should build for NOT on phrase" do
|
146
144
|
should_be_list parse('one two three NOT "quick brown"') do |list|
|
147
145
|
should_be_not_expression list[3] do |operand|
|
148
146
|
should_be_phrase(operand, "quick brown")
|
149
147
|
end
|
150
|
-
end
|
148
|
+
end
|
151
149
|
end
|
152
|
-
|
150
|
+
|
153
151
|
it "should build NOT on expression" do
|
154
152
|
should_be_list parse('one two NOT (blue OR yellow)') do |list|
|
155
153
|
should_be_not_expression list[2] do |operand|
|
156
154
|
should_be_or_list(operand)
|
157
155
|
end
|
158
|
-
end
|
156
|
+
end
|
159
157
|
end
|
160
|
-
|
158
|
+
|
161
159
|
it "should build NOT preceded by binary op" do
|
162
160
|
should_be_or_list parse_one_element('one OR NOT two') do |list|
|
163
161
|
should_be_not_expression list[1] do |operand|
|
164
162
|
should_be_term(operand, "two")
|
165
163
|
end
|
166
|
-
end
|
164
|
+
end
|
167
165
|
end
|
168
|
-
|
169
|
-
|
166
|
+
|
170
167
|
it "should bind OR more tightly than AND" do
|
171
|
-
should_be_and_list parse_one_element("grey AND big OR small AND tail") do |list|
|
168
|
+
should_be_and_list parse_one_element("grey AND big OR small AND tail") do |list|
|
172
169
|
expect(list.length).to eq(3)
|
173
|
-
|
170
|
+
|
174
171
|
should_be_term list[0], "grey"
|
175
|
-
|
172
|
+
|
176
173
|
should_be_or_list list[1] do |or_list|
|
177
|
-
|
178
|
-
|
179
|
-
|
174
|
+
expect(or_list.length).to eq(2)
|
175
|
+
should_be_term or_list[0], "big"
|
176
|
+
should_be_term or_list[1], "small"
|
180
177
|
end
|
181
|
-
|
178
|
+
|
182
179
|
should_be_term list[2], "tail"
|
183
|
-
end
|
180
|
+
end
|
184
181
|
end
|
185
|
-
|
186
|
-
it "should parse AND'd lists" do
|
187
|
-
should_be_and_list parse_one_element("(foo bar one AND two) AND (three four ten OR twelve)") do |list|
|
188
|
-
expect(list.length).to eq(2)
|
189
|
-
|
182
|
+
|
183
|
+
it "should parse AND'd lists" do
|
184
|
+
should_be_and_list parse_one_element("(foo bar one AND two) AND (three four ten OR twelve)") do |list|
|
185
|
+
expect(list.length).to eq(2)
|
186
|
+
|
190
187
|
should_be_list(list[0]) do |first_half|
|
191
188
|
expect(first_half[0].value).to eq('foo')
|
192
189
|
expect(first_half[1].value).to eq("bar")
|
193
|
-
should_be_and_list(first_half[2])
|
190
|
+
should_be_and_list(first_half[2])
|
194
191
|
end
|
195
|
-
|
192
|
+
|
196
193
|
should_be_list(list[1]) do |second_half|
|
197
194
|
expect(second_half[0].value).to eq("three")
|
198
195
|
expect(second_half[1].value).to eq("four")
|
199
|
-
should_be_or_list second_half[2]
|
196
|
+
should_be_or_list second_half[2]
|
200
197
|
end
|
201
198
|
end
|
202
199
|
end
|
203
|
-
|
200
|
+
|
204
201
|
it "should build for a crazy complicated one" do
|
205
202
|
should_be_list parse("mark +twain AND huck OR fun OR ((jim AND river) AND (red -dogs))") do |list|
|
206
203
|
should_be_term list[0], "mark"
|
207
204
|
should_be_and_list list[1] do |and_list|
|
208
|
-
|
209
205
|
should_be_mandatory and_list[0] do |operand|
|
210
206
|
should_be_term operand, "twain"
|
211
207
|
end
|
212
|
-
|
208
|
+
|
213
209
|
should_be_or_list and_list[1] do |or_list|
|
214
210
|
should_be_term or_list[0], "huck"
|
215
211
|
should_be_term or_list[1], "fun"
|
216
|
-
|
212
|
+
|
217
213
|
should_be_and_list or_list[2] do |and_list|
|
218
214
|
expect(and_list.length).to eq(2)
|
219
|
-
|
215
|
+
|
220
216
|
should_be_and_list and_list[0]
|
221
|
-
|
217
|
+
|
222
218
|
should_be_list and_list[1] do |terms|
|
223
219
|
should_be_term terms[0], "red"
|
224
220
|
should_be_excluded terms[1] do |operand|
|
@@ -226,13 +222,9 @@ describe "NestingParser" do
|
|
226
222
|
end
|
227
223
|
end
|
228
224
|
end
|
229
|
-
|
230
225
|
end
|
231
226
|
end
|
232
227
|
end
|
233
228
|
end
|
234
|
-
|
235
229
|
end
|
236
230
|
end
|
237
|
-
|
238
|
-
|