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,53 +1,30 @@
|
|
1
1
|
module BlacklightAdvancedSearch::CatalogHelperOverride
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def remove_advanced_keyword_query(field, my_params = params)
|
6
|
-
my_params = my_params.dup
|
7
|
-
my_params.delete(field)
|
8
|
-
return my_params
|
9
|
-
end
|
10
|
-
|
11
|
-
def remove_advanced_filter_group(field, my_params = params)
|
12
|
-
if (my_params[:f_inclusive])
|
13
|
-
my_params = my_params.dup
|
14
|
-
my_params[:f_inclusive] = my_params[:f_inclusive].dup
|
15
|
-
my_params[:f_inclusive].delete(field)
|
16
|
-
|
17
|
-
if my_params[:f_inclusive].empty?
|
18
|
-
my_params.delete :f_inclusive
|
19
|
-
end
|
20
|
-
end
|
21
|
-
my_params
|
22
|
-
end
|
23
|
-
|
24
2
|
# Special display for facet limits that include adv search inclusive
|
25
3
|
# or limits.
|
26
4
|
def facet_partial_name(display_facet = nil)
|
27
|
-
return "blacklight_advanced_search/facet_limit" if advanced_query && advanced_query.filters.keys.include?(
|
28
|
-
super
|
5
|
+
return "blacklight_advanced_search/facet_limit" if advanced_query && advanced_query.filters.keys.include?(display_facet.name)
|
6
|
+
super
|
29
7
|
end
|
30
8
|
|
31
9
|
def remove_advanced_facet_param(field, value, my_params = params)
|
32
|
-
my_params = my_params.
|
33
|
-
if (my_params[:f_inclusive] &&
|
10
|
+
my_params = Blacklight::SearchState.new(my_params, blacklight_config).to_h
|
11
|
+
if (my_params[:f_inclusive] &&
|
34
12
|
my_params[:f_inclusive][field] &&
|
35
13
|
my_params[:f_inclusive][field].include?(value))
|
36
|
-
|
14
|
+
|
37
15
|
my_params[:f_inclusive] = my_params[:f_inclusive].dup
|
38
16
|
my_params[:f_inclusive][field] = my_params[:f_inclusive][field].dup
|
39
17
|
my_params[:f_inclusive][field].delete(value)
|
40
|
-
|
41
|
-
my_params[:f_inclusive].delete(field) if my_params[:f_inclusive][field].
|
42
|
-
|
43
|
-
my_params.delete(:f_inclusive) if my_params[:f_inclusive].
|
18
|
+
|
19
|
+
my_params[:f_inclusive].delete(field) if my_params[:f_inclusive][field].empty?
|
20
|
+
|
21
|
+
my_params.delete(:f_inclusive) if my_params[:f_inclusive].empty?
|
44
22
|
end
|
45
23
|
|
46
|
-
my_params.delete_if do |key,
|
24
|
+
my_params.delete_if do |key, _value|
|
47
25
|
[:page, :id, :counter, :commit].include?(key)
|
48
26
|
end
|
49
|
-
|
27
|
+
|
50
28
|
my_params
|
51
29
|
end
|
52
|
-
|
53
30
|
end
|
@@ -12,7 +12,7 @@ module BlacklightAdvancedSearch::Controller
|
|
12
12
|
helper_method :is_advanced_search?, :advanced_query
|
13
13
|
end
|
14
14
|
|
15
|
-
def is_advanced_search?
|
15
|
+
def is_advanced_search?(req_params = params)
|
16
16
|
(req_params[:search_field] == blacklight_config.advanced_search[:url_key]) ||
|
17
17
|
req_params[:f_inclusive]
|
18
18
|
end
|
@@ -1,13 +1,11 @@
|
|
1
1
|
module BlacklightAdvancedSearch::FilterParser
|
2
2
|
# Returns an array of solr :fq params. taking advanced search inclusive
|
3
|
-
# facet value lists out of params.
|
3
|
+
# facet value lists out of params.
|
4
4
|
def generate_solr_fq
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
return filter_queries
|
5
|
+
filters.map do |solr_field, value_list|
|
6
|
+
"#{solr_field}:(" +
|
7
|
+
Array(value_list).collect { |v| '"' + v.gsub('"', '\"') + '"' }.join(" OR ") +
|
8
|
+
")"
|
9
|
+
end
|
12
10
|
end
|
13
|
-
end
|
11
|
+
end
|
@@ -1,18 +1,15 @@
|
|
1
1
|
require 'parsing_nesting/tree'
|
2
2
|
module BlacklightAdvancedSearch::ParsingNestingParser
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
keyword_queries.each do |field,query|
|
7
|
-
queries << ParsingNesting::Tree.parse(query, config.advanced_search[:query_parser]).to_query( local_param_hash(field, config) )
|
3
|
+
def process_query(_params, config)
|
4
|
+
queries = keyword_queries.map do |field, query|
|
5
|
+
ParsingNesting::Tree.parse(query, config.advanced_search[:query_parser]).to_query(local_param_hash(field, config))
|
8
6
|
end
|
9
|
-
queries.join(
|
7
|
+
queries.join(" #{keyword_op} ")
|
10
8
|
end
|
11
|
-
|
9
|
+
|
12
10
|
def local_param_hash(key, config)
|
13
11
|
field_def = config.search_fields[key]
|
14
12
|
|
15
13
|
(field_def[:solr_parameters] || {}).merge(field_def[:solr_local_parameters] || {})
|
16
14
|
end
|
17
|
-
|
18
15
|
end
|
@@ -1,32 +1,30 @@
|
|
1
1
|
# Returns a lambda that you can use with a before_filter in your
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
# CatalogController to catch and redirect query params using the old
|
3
|
+
# style, used prior to blacklight_advanced_search 5.0.
|
4
|
+
#
|
5
|
+
# This can be used to keep any old bookmarked URLs still working.
|
6
|
+
#
|
7
|
+
# before_filter BlacklightAdvancedSearch::RedirectLegacyParamsFilter, :only => :index
|
8
|
+
#
|
9
|
+
module BlacklightAdvancedSearch
|
10
|
+
class RedirectLegacyParamsFilter
|
11
|
+
def self.before(controller)
|
12
|
+
params = controller.send(:params)
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
if params[:f_inclusive] && params[:f_inclusive].respond_to?(:each_pair)
|
15
|
+
legacy_converted = false
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
legacy_converted = true
|
22
|
-
params[:f_inclusive][field] = value.keys
|
23
|
-
end
|
24
|
-
end
|
17
|
+
params[:f_inclusive].each_pair do |field, value|
|
18
|
+
next unless value.is_a? Hash
|
19
|
+
# old style! convert!
|
20
|
+
legacy_converted = true
|
21
|
+
params[:f_inclusive][field] = value.keys
|
22
|
+
end
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
end
|
24
|
+
if legacy_converted
|
25
|
+
controller.send(:redirect_to, params, :status => :moved_permanently)
|
29
26
|
end
|
30
27
|
end
|
31
28
|
end
|
32
|
-
end
|
29
|
+
end
|
30
|
+
end
|
@@ -2,19 +2,18 @@
|
|
2
2
|
# certain methods from RenderConstraintsHelper (newish in BL),
|
3
3
|
# to effect constraints rendering and search history rendering,
|
4
4
|
module BlacklightAdvancedSearch::RenderConstraintsOverride
|
5
|
-
|
6
5
|
def query_has_constraints?(localized_params = params)
|
7
6
|
if is_advanced_search? localized_params
|
8
7
|
true
|
9
8
|
else
|
10
|
-
!(localized_params[:q].blank?
|
9
|
+
!(localized_params[:q].blank? && localized_params[:f].blank? && localized_params[:f_inclusive].blank?)
|
11
10
|
end
|
12
11
|
end
|
13
12
|
|
14
|
-
#Over-ride of Blacklight method, provide advanced constraints if needed,
|
13
|
+
# Over-ride of Blacklight method, provide advanced constraints if needed,
|
15
14
|
# otherwise call super.
|
16
15
|
def render_constraints_query(my_params = params)
|
17
|
-
if (advanced_query.nil? || advanced_query.keyword_queries.empty?
|
16
|
+
if (advanced_query.nil? || advanced_query.keyword_queries.empty?)
|
18
17
|
return super(my_params)
|
19
18
|
else
|
20
19
|
content = []
|
@@ -28,58 +27,56 @@ module BlacklightAdvancedSearch::RenderConstraintsOverride
|
|
28
27
|
end
|
29
28
|
if (advanced_query.keyword_op == "OR" &&
|
30
29
|
advanced_query.keyword_queries.length > 1)
|
31
|
-
content.unshift content_tag(:span, "Any of:", class:'operator')
|
30
|
+
content.unshift content_tag(:span, "Any of:", class: 'operator')
|
32
31
|
content_tag :span, class: "inclusive_or appliedFilter well" do
|
33
32
|
safe_join(content.flatten, "\n")
|
34
33
|
end
|
35
34
|
else
|
36
|
-
safe_join(content.flatten, "\n")
|
35
|
+
safe_join(content.flatten, "\n")
|
37
36
|
end
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
41
|
-
#Over-ride of Blacklight method, provide advanced constraints if needed,
|
40
|
+
# Over-ride of Blacklight method, provide advanced constraints if needed,
|
42
41
|
# otherwise call super.
|
43
42
|
def render_constraints_filters(my_params = params)
|
44
43
|
content = super(my_params)
|
45
44
|
|
46
|
-
if
|
45
|
+
if advanced_query
|
47
46
|
advanced_query.filters.each_pair do |field, value_list|
|
48
47
|
label = facet_field_label(field)
|
49
48
|
content << render_constraint_element(label,
|
50
|
-
safe_join(value_list, " <strong class='text-muted constraint-connector'>OR</strong> ".html_safe),
|
51
|
-
:remove => search_action_path(
|
52
|
-
|
49
|
+
safe_join(Array(value_list), " <strong class='text-muted constraint-connector'>OR</strong> ".html_safe),
|
50
|
+
:remove => search_action_path(remove_advanced_filter_group(field, my_params).except(:controller, :action))
|
51
|
+
)
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
|
-
|
55
|
+
content
|
57
56
|
end
|
58
57
|
|
59
58
|
# override of BL method, so our inclusive facet selections
|
60
59
|
# are still recgonized for eg highlighting facet with selected
|
61
|
-
# values.
|
60
|
+
# values.
|
62
61
|
def facet_field_in_params?(field)
|
63
62
|
return true if super
|
64
63
|
|
65
64
|
# otherwise use our own logic.
|
66
|
-
query = BlacklightAdvancedSearch::QueryParser.new(params, self.blacklight_config
|
67
|
-
if query.filters.keys.include?(
|
68
|
-
return true
|
69
|
-
end
|
65
|
+
query = BlacklightAdvancedSearch::QueryParser.new(params, self.blacklight_config)
|
66
|
+
return true if query.filters.keys.include?(field)
|
70
67
|
|
71
|
-
|
68
|
+
false
|
72
69
|
end
|
73
70
|
|
74
71
|
def render_search_to_s_filters(my_params)
|
75
72
|
content = super(my_params)
|
76
73
|
|
77
|
-
advanced_query = BlacklightAdvancedSearch::QueryParser.new(my_params, blacklight_config
|
74
|
+
advanced_query = BlacklightAdvancedSearch::QueryParser.new(my_params, blacklight_config)
|
78
75
|
|
79
|
-
|
76
|
+
unless advanced_query.filters.empty?
|
80
77
|
advanced_query.filters.each_pair do |field, values|
|
81
78
|
# old-style, may still be in history
|
82
|
-
values = values.keys if values.
|
79
|
+
values = values.keys if values.is_a? Hash
|
83
80
|
|
84
81
|
label = facet_field_label(field)
|
85
82
|
|
@@ -89,27 +86,27 @@ module BlacklightAdvancedSearch::RenderConstraintsOverride
|
|
89
86
|
)
|
90
87
|
end
|
91
88
|
end
|
92
|
-
|
89
|
+
content
|
93
90
|
end
|
94
91
|
|
95
92
|
def render_search_to_s_q(my_params)
|
96
93
|
content = super(my_params)
|
97
94
|
|
98
|
-
advanced_query = BlacklightAdvancedSearch::QueryParser.new(my_params, blacklight_config
|
95
|
+
advanced_query = BlacklightAdvancedSearch::QueryParser.new(my_params, blacklight_config)
|
99
96
|
|
100
97
|
if (advanced_query.keyword_queries.length > 1 &&
|
101
98
|
advanced_query.keyword_op == "OR")
|
102
|
-
|
99
|
+
# Need to do something to make the inclusive-or search clear
|
103
100
|
|
104
|
-
|
105
|
-
|
106
|
-
|
101
|
+
display_as = advanced_query.keyword_queries.collect do |field, query|
|
102
|
+
h(search_field_def_for_key(field)[:label] + ": " + query)
|
103
|
+
end.join(" ; ")
|
107
104
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
elsif
|
105
|
+
content << render_search_to_s_element("Any of",
|
106
|
+
display_as,
|
107
|
+
:escape_value => false
|
108
|
+
)
|
109
|
+
elsif !advanced_query.keyword_queries.empty?
|
113
110
|
advanced_query.keyword_queries.each_pair do |field, query|
|
114
111
|
label = search_field_def_for_key(field)[:label]
|
115
112
|
|
@@ -117,7 +114,23 @@ module BlacklightAdvancedSearch::RenderConstraintsOverride
|
|
117
114
|
end
|
118
115
|
end
|
119
116
|
|
120
|
-
|
117
|
+
content
|
118
|
+
end
|
119
|
+
|
120
|
+
def remove_advanced_keyword_query(field, my_params = params)
|
121
|
+
my_params = Blacklight::SearchState.new(my_params, blacklight_config).to_h
|
122
|
+
my_params.delete(field)
|
123
|
+
my_params
|
121
124
|
end
|
122
125
|
|
126
|
+
def remove_advanced_filter_group(field, my_params = params)
|
127
|
+
if (my_params[:f_inclusive])
|
128
|
+
my_params = Blacklight::SearchState.new(my_params, blacklight_config).to_h
|
129
|
+
my_params[:f_inclusive] = my_params[:f_inclusive].dup
|
130
|
+
my_params[:f_inclusive].delete(field)
|
131
|
+
|
132
|
+
my_params.delete :f_inclusive if my_params[:f_inclusive].empty?
|
133
|
+
end
|
134
|
+
my_params
|
135
|
+
end
|
123
136
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copy BlacklightAdvancedSearch assets to public folder in current app.
|
1
|
+
# Copy BlacklightAdvancedSearch assets to public folder in current app.
|
2
2
|
# If you want to do this on application startup, you can
|
3
3
|
# add this next line to your one of your environment files --
|
4
4
|
# generally you'd only want to do this in 'development', and can
|
@@ -6,9 +6,8 @@
|
|
6
6
|
# require File.join(BlacklightAdvancedSearch.root, "lib", "generators", "blacklight", "assets_generator.rb")
|
7
7
|
# BlacklightAdvancedSearch::AssetsGenerator.start(["--force", "--quiet"])
|
8
8
|
|
9
|
-
|
10
9
|
# Need the requires here so we can call the generator from environment.rb
|
11
|
-
# as suggested above.
|
10
|
+
# as suggested above.
|
12
11
|
require 'rails/generators'
|
13
12
|
require 'rails/generators/base'
|
14
13
|
module BlacklightAdvancedSearch
|
@@ -26,12 +25,11 @@ module BlacklightAdvancedSearch
|
|
26
25
|
original_css = File.binread(application_css_location)
|
27
26
|
if original_css.include?("require 'blacklight_advanced_search'")
|
28
27
|
say_status("skipped", "insert into app/assets/stylesheets/application.css", :yellow)
|
29
|
-
else
|
28
|
+
else
|
30
29
|
insert_into_file application_css_location, :before => "*/" do
|
31
30
|
"\n *= require 'blacklight_advanced_search'\n\n"
|
32
31
|
end
|
33
32
|
end
|
34
|
-
|
35
33
|
end
|
36
34
|
|
37
35
|
def js_asset
|
@@ -41,7 +39,7 @@ module BlacklightAdvancedSearch
|
|
41
39
|
say_status "skipped", "Can not find an application.js, did not insert our require", :red
|
42
40
|
return
|
43
41
|
end
|
44
|
-
|
42
|
+
|
45
43
|
original_js = File.binread(application_js_location)
|
46
44
|
if original_js.include?("require 'blacklight_advanced_search'")
|
47
45
|
say_status("skipped", "insert into app/assets/javascripts/application.js", :yellow)
|
@@ -51,7 +49,5 @@ module BlacklightAdvancedSearch
|
|
51
49
|
end
|
52
50
|
end
|
53
51
|
end
|
54
|
-
|
55
52
|
end
|
56
53
|
end
|
57
|
-
|
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'rails/generators'
|
2
2
|
|
3
3
|
class BlacklightAdvancedSearchGenerator < Rails::Generators::Base
|
4
|
-
|
5
4
|
def inject_asset_requires
|
6
5
|
say "`rails g blacklight_advanced_search` is deprecated; use blacklight_advanced_search:install instead", :red
|
7
6
|
generate "blacklight_advanced_search:install"
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
@@ -47,16 +47,20 @@ module BlacklightAdvancedSearch
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def install_localized_search_form
|
50
|
-
if options[:force]
|
50
|
+
if options[:force] || yes?("Install local search form with advanced link? (y/N)", :green)
|
51
51
|
# We're going to copy the search from from actual currently loaded
|
52
|
-
# Blacklight into local app as custom local override -- but add our link at the end too.
|
52
|
+
# Blacklight into local app as custom local override -- but add our link at the end too.
|
53
53
|
source_file = File.read(File.join(Blacklight.root, "app/views/catalog/_search_form.html.erb"))
|
54
54
|
|
55
|
-
new_file_contents = source_file +
|
55
|
+
new_file_contents = source_file + <<-EOF.strip_heredoc
|
56
|
+
\n\n
|
57
|
+
<div class="navbar-form">
|
58
|
+
<%= link_to 'More options', blacklight_advanced_search_engine.advanced_search_path(search_state.to_h), class: 'advanced_search btn btn-default'%>
|
59
|
+
</div>
|
60
|
+
EOF
|
56
61
|
|
57
|
-
create_file("app/views/catalog/_search_form.html.erb", new_file_contents)
|
62
|
+
create_file("app/views/catalog/_search_form.html.erb", new_file_contents)
|
58
63
|
end
|
59
64
|
end
|
60
65
|
end
|
61
|
-
|
62
66
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
class AdvancedController < BlacklightAdvancedSearch::AdvancedController
|
2
|
-
|
3
2
|
blacklight_config.configure do |config|
|
4
3
|
# name of Solr request handler, leave unset to use the same one your Blacklight
|
5
4
|
# is ordinarily using (recommended if possible)
|
@@ -54,5 +53,4 @@ class AdvancedController < BlacklightAdvancedSearch::AdvancedController
|
|
54
53
|
}
|
55
54
|
end
|
56
55
|
end
|
57
|
-
|
58
56
|
end
|
@@ -1,78 +1,75 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'parslet'
|
3
3
|
|
4
|
-
# Parslet uses Object#tap, which is in ruby 1.8.7+, but not 1.8.6.
|
4
|
+
# Parslet uses Object#tap, which is in ruby 1.8.7+, but not 1.8.6.
|
5
5
|
# But it's easy enough to implement in pure ruby, let's monkey patch
|
6
6
|
# it in if it's not there, so we'll still work with 1.8.6
|
7
7
|
unless Object.method_defined?(:tap)
|
8
8
|
class Object
|
9
9
|
def tap
|
10
10
|
yield(self)
|
11
|
-
|
11
|
+
self
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
15
15
|
module ParsingNesting
|
16
16
|
class Grammar < Parslet::Parser
|
17
17
|
root :query
|
18
|
-
|
19
|
-
# query is actually a list of expressions.
|
18
|
+
|
19
|
+
# query is actually a list of expressions.
|
20
20
|
rule :query do
|
21
|
-
(spacing? >>
|
21
|
+
(spacing? >> (expression | paren_unit) >> spacing?).repeat
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
rule :paren_list do
|
25
25
|
(str('(') >> query >> str(')')).as(:list)
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
rule :paren_unit do
|
29
|
-
(str('(') >> spacing? >>
|
29
|
+
(str('(') >> spacing? >> expression >> spacing? >> str(')')) |
|
30
30
|
paren_list
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
# Note well: It was tricky to parse the thing we want where you can
|
34
34
|
# have a flat list with boolean operators, but where 'OR' takes precedence.
|
35
35
|
# eg "A AND B OR C AND C" or "A OR B AND C OR D". Tricky to parse at all,
|
36
36
|
# tricky to make precedence work. Important things that seem to make it work:
|
37
37
|
# and_list comes BEFORE or_list in :expression.
|
38
38
|
# and_list's operand can be an or_list, but NOT vice versa
|
39
|
-
# There are others, it was an iterative process with testing.
|
39
|
+
# There are others, it was an iterative process with testing.
|
40
40
|
rule :expression do
|
41
|
-
(and_list | or_list | unary_expression
|
41
|
+
(and_list | or_list | unary_expression)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
rule :and_list do
|
45
|
-
((or_list | unary_expression | paren_unit) >>
|
46
|
-
(spacing >> str("AND") >> spacing >> (or_list | unary_expression | paren_unit)).repeat(1)).as(:and_list)
|
45
|
+
((or_list | unary_expression | paren_unit) >>
|
46
|
+
(spacing >> str("AND") >> spacing >> (or_list | unary_expression | paren_unit)).repeat(1)).as(:and_list)
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
rule :or_list do
|
50
|
-
((unary_expression | paren_unit) >>
|
51
|
-
(spacing >> str("OR") >> spacing >> (unary_expression | paren_unit)).repeat(1)).as(:or_list)
|
50
|
+
((unary_expression | paren_unit) >>
|
51
|
+
(spacing >> str("OR") >> spacing >> (unary_expression | paren_unit)).repeat(1)).as(:or_list)
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
rule :unary_expression do
|
55
55
|
(str('+') >> (phrase | token)).as(:mandatory) |
|
56
56
|
(str('-') >> (phrase | token)).as(:excluded) |
|
57
57
|
(str('NOT') >> spacing? >> (unary_expression | paren_unit)).as(:not_expression) |
|
58
58
|
(phrase | token)
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
rule :token do
|
62
62
|
match['^ ")('].repeat(1).as(:token)
|
63
63
|
end
|
64
64
|
rule :phrase do
|
65
|
-
match('"') >> match['^"'].repeat(1).as(:phrase)
|
65
|
+
match('"') >> match['^"'].repeat(1).as(:phrase) >> match('"')
|
66
66
|
end
|
67
|
-
|
68
|
-
|
67
|
+
|
69
68
|
rule :spacing do
|
70
|
-
match[' '].repeat(1)
|
69
|
+
match[' '].repeat(1)
|
71
70
|
end
|
72
71
|
rule :spacing? do
|
73
72
|
spacing.maybe
|
74
73
|
end
|
75
74
|
end
|
76
|
-
|
77
|
-
|
78
75
|
end
|