query_string_search 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.rubocop.yml +18 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +97 -0
- data/Rakefile +12 -0
- data/lib/query_string_search/abstract_matcher.rb +43 -0
- data/lib/query_string_search/matcher_factory.rb +11 -0
- data/lib/query_string_search/matchers/match_all.rb +9 -0
- data/lib/query_string_search/matchers/match_attribute.rb +13 -0
- data/lib/query_string_search/matchers/match_attribute_value.rb +11 -0
- data/lib/query_string_search/matchers/match_no_attribute.rb +13 -0
- data/lib/query_string_search/search_options.rb +32 -0
- data/lib/query_string_search/search_parameters.rb +22 -0
- data/lib/query_string_search/version.rb +3 -0
- data/lib/query_string_search.rb +30 -0
- data/query_string_search.gemspec +24 -0
- data/spec/features/find_all_data_spec.rb +11 -0
- data/spec/features/find_records_that_match_multiple_parameters_spec.rb +31 -0
- data/spec/features/find_records_with_matching_attribute_values_spec.rb +49 -0
- data/spec/features/find_records_with_nil_attributes_spec.rb +29 -0
- data/spec/features/find_records_with_nonnil_attributes_spec.rb +29 -0
- data/spec/features/find_seen_and_unseen_movies_spec.rb +26 -0
- data/spec/fixtures/movie.rb +39 -0
- data/spec/lib/query_string_search/abstract_matcher_spec.rb +27 -0
- data/spec/lib/query_string_search/matcher_factory_spec.rb +31 -0
- data/spec/lib/query_string_search/matchers/match_all_spec.rb +26 -0
- data/spec/lib/query_string_search/matchers/match_attribute_spec.rb +68 -0
- data/spec/lib/query_string_search/matchers/match_attribute_value_spec.rb +61 -0
- data/spec/lib/query_string_search/matchers/match_no_attribute_spec.rb +68 -0
- data/spec/lib/query_string_search/search_options_spec.rb +53 -0
- data/spec/lib/query_string_search/search_parameters_spec.rb +27 -0
- data/spec/spec_helper.rb +85 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5750a3574b4f201d0e9274a96fa4e1a10aff5593
|
4
|
+
data.tar.gz: 6bddcc98d4a95cd33a88ecd0d7d5adb49cc787ea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 97feeae7f780d9513c10b1861cee25a72297d4e6af9fddd2e341e922349d627fa11bd25c4b785a5323cbe805846d5dbdec514474c6a30e912a9312d8479c51fd
|
7
|
+
data.tar.gz: 1f275d60e2f5b2075214b60e58546edbe261fc40db111734bb56584daac3088da502281314695fd3415a0c2ffae5c597ba1f8bbaaac30c4b4a9a19a2e4f5363e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- '**/*.gemspec'
|
4
|
+
- 'bin/**/*'
|
5
|
+
- 'spec/spec_helper.rb'
|
6
|
+
- 'vendor/bundle/**/*'
|
7
|
+
|
8
|
+
Documentation:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Style/IfUnlessModifier:
|
12
|
+
MaxLineLength: 0
|
13
|
+
|
14
|
+
Style/StringLiterals:
|
15
|
+
EnforcedStyle: double_quotes
|
16
|
+
|
17
|
+
Metrics/LineLength:
|
18
|
+
Max: 800
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.1
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Ian Whitney
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Query String Search
|
2
|
+
|
3
|
+
Provides an easy way to implement searching in your API endpoints
|
4
|
+
|
5
|
+
## Searches it supports
|
6
|
+
|
7
|
+
Say you have a `movies` endpoint and people want to be able to search your huge collection of movie data. The API Search gem will give you the following search functionality:
|
8
|
+
|
9
|
+
### Return all data
|
10
|
+
|
11
|
+
`movies` will return every movie in the data set.
|
12
|
+
|
13
|
+
### Return all data with non-null attribute value
|
14
|
+
|
15
|
+
`movies?q=rating=all`
|
16
|
+
|
17
|
+
Returns every movie with a non-nil rating.
|
18
|
+
|
19
|
+
### Return all data with null attribute value
|
20
|
+
|
21
|
+
`movies?q=rating=none`
|
22
|
+
|
23
|
+
Returns every movie without ratings.
|
24
|
+
|
25
|
+
### Return all data with an attribute value that matches
|
26
|
+
|
27
|
+
`movies?q=year=1994`
|
28
|
+
|
29
|
+
Returns every movie with a year of 1994
|
30
|
+
|
31
|
+
### Combining Searches
|
32
|
+
|
33
|
+
Search criteria can be separated with commas
|
34
|
+
|
35
|
+
`movie?q=year=1994,country=US,rated=none`
|
36
|
+
|
37
|
+
Records that match **all** the criteria will be returned. All un-rated movies made in the US in 1994.
|
38
|
+
|
39
|
+
## Installation
|
40
|
+
|
41
|
+
Add this line to your application's Gemfile:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
gem 'query_string_search', git: https://github.umn.edu/asrweb/query_string_search.git
|
45
|
+
```
|
46
|
+
|
47
|
+
And then execute:
|
48
|
+
|
49
|
+
$ bundle
|
50
|
+
|
51
|
+
## Usage
|
52
|
+
|
53
|
+
First, create a collection of data. With ActiveRecord or other ORMs this is straightforward:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
Movie.all
|
57
|
+
```
|
58
|
+
|
59
|
+
Or something similar. As long as it returns a collection of objects, you should be good.
|
60
|
+
|
61
|
+
The objects must respond to the attributes you want to search on. Say you want to allow a search string like this:
|
62
|
+
|
63
|
+
```
|
64
|
+
`movies?q=year=1994`
|
65
|
+
```
|
66
|
+
|
67
|
+
Then every object in your data collection needs to respond to `year`.
|
68
|
+
|
69
|
+
Again, with ActiveRecord this is pretty straightforward. But if you're building your data source from raw SQL then you're going to have to convert that data into objects that respond to the attributes you want to search on.
|
70
|
+
|
71
|
+
Second, search! In Rails you can do something like this in a Controller method.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
QueryStringSearch.new(data, query_string).results
|
75
|
+
```
|
76
|
+
|
77
|
+
This returns a collection of the objects that matched the search criteria.
|
78
|
+
|
79
|
+
Or you can do it not in the controller. This will work:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
test_query = "country=us"
|
83
|
+
QueryStringSearch.new(Movie.all, test_query).results
|
84
|
+
```
|
85
|
+
|
86
|
+
You get the idea. Pass in a data set and a query-stringish string and you'll get results back.
|
87
|
+
|
88
|
+
|
89
|
+
## Contributing
|
90
|
+
|
91
|
+
- Fork, branch, commit & pull.
|
92
|
+
- Tests are required.
|
93
|
+
- Don't go against our Rubocop style guidelines.
|
94
|
+
|
95
|
+
## License
|
96
|
+
|
97
|
+
© Regents of the University of Minnesota. All rights reserved.
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module QueryStringSearch
|
2
|
+
class AbstractMatcher
|
3
|
+
attr_accessor :attribute, :value
|
4
|
+
|
5
|
+
def self.matchers
|
6
|
+
descendants.push(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.descendants
|
10
|
+
ObjectSpace.each_object(Class).select { |klass| klass < AbstractMatcher }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.all_reserved_words
|
14
|
+
descendants.each_with_object([]) { |d, ret| ret << d.reserved_words }.flatten
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(attribute = nil, value = nil)
|
18
|
+
self.attribute = attribute
|
19
|
+
self.value = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def match?(_)
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.reserved_words
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.build_me?(_, _)
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def match_with_contingency
|
37
|
+
yield
|
38
|
+
rescue
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
Dir.glob(File.join(File.dirname(__FILE__), "matchers", "*.rb")) { |file| require file }
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module QueryStringSearch
|
2
|
+
class MatcherFactory
|
3
|
+
def self.build(query_option, build_options)
|
4
|
+
constructor = build_options.detect { |m| m.build_me?(query_option.search_type, query_option.search_param) }
|
5
|
+
|
6
|
+
if constructor
|
7
|
+
constructor.new(query_option.search_type, query_option.search_param)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class MatchAttribute < QueryStringSearch::AbstractMatcher
|
2
|
+
def match?(target)
|
3
|
+
match_with_contingency { target.public_send(attribute) }
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.reserved_words
|
7
|
+
%w(true all)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.build_me?(_, search_param)
|
11
|
+
reserved_words.include?(search_param)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class MatchAttributeValue < QueryStringSearch::AbstractMatcher
|
2
|
+
def match?(target)
|
3
|
+
match_with_contingency do
|
4
|
+
target.public_send(attribute).to_s.upcase == value.to_s.upcase
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.build_me?(search_type, search_param)
|
9
|
+
search_param && search_type && !all_reserved_words.include?(search_param)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class MatchNoAttribute < QueryStringSearch::AbstractMatcher
|
2
|
+
def match?(target)
|
3
|
+
match_with_contingency { !target.public_send(attribute) }
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.reserved_words
|
7
|
+
%w(false none)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.build_me?(_, search_param)
|
11
|
+
reserved_words.include?(search_param)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module QueryStringSearch
|
2
|
+
class SearchOptions
|
3
|
+
attr_reader :search_type, :search_param
|
4
|
+
|
5
|
+
def self.parse(query_string)
|
6
|
+
if query_string
|
7
|
+
search_params = query_string.split(",")
|
8
|
+
search_params.each_with_object([]) do |p, ret|
|
9
|
+
ret << new(p)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
[new(nil)]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(raw_query)
|
17
|
+
self.search_type, self.search_param = raw_query.to_s.split("=")
|
18
|
+
end
|
19
|
+
|
20
|
+
def search_type
|
21
|
+
@search_type ? @search_type.to_sym : nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def search_param
|
25
|
+
@search_param ? CGI.unescape(@search_param) : @search_param
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_writer :search_type, :search_param
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module QueryStringSearch
|
2
|
+
class SearchParameters
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegators :@collection, :each
|
6
|
+
|
7
|
+
def self.build_from_querystring(query_string, factory = QueryStringSearch::MatcherFactory, matchers = QueryStringSearch::AbstractMatcher.matchers)
|
8
|
+
parameters = QueryStringSearch::SearchOptions.parse(query_string)
|
9
|
+
new(parameters, factory, matchers)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(parameters, factory, matchers)
|
13
|
+
parameters.each do |param|
|
14
|
+
collection << factory.build(param, matchers)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def collection
|
19
|
+
@collection ||= []
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module QueryStringSearch
|
2
|
+
def self.new(data_source, query_string)
|
3
|
+
QueryStringSearch::Search.new(data_source, query_string)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Search
|
7
|
+
attr_accessor :data_source, :query_string
|
8
|
+
|
9
|
+
def initialize(data_source, query_string)
|
10
|
+
self.data_source = data_source
|
11
|
+
self.query_string = query_string
|
12
|
+
end
|
13
|
+
|
14
|
+
def results
|
15
|
+
@results = data_source
|
16
|
+
QueryStringSearch::SearchParameters.build_from_querystring(query_string).each do |param|
|
17
|
+
@results = filter_by_param(@results, param)
|
18
|
+
end
|
19
|
+
@results
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def filter_by_param(data, param)
|
25
|
+
data.select { |c| param.match?(c) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Dir.glob(File.join(File.dirname(__FILE__), "query_string_search", "*.rb")) { |file| require file }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'query_string_search/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "query_string_search"
|
8
|
+
spec.version = QueryStringSearch::VERSION
|
9
|
+
spec.authors = ["Ian Whitney"]
|
10
|
+
spec.email = ["whit0694@umn.edu"]
|
11
|
+
spec.summary = %q{Provides a standard way to do searches using query strings in an API endpoint}
|
12
|
+
spec.homepage = "https://github.com/umn-asr/query_string_search"
|
13
|
+
spec.license = ""
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3"
|
23
|
+
spec.add_development_dependency "rubocop"
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative "../../lib/query_string_search"
|
2
|
+
require_relative "../fixtures/movie"
|
3
|
+
|
4
|
+
RSpec.describe "Searching for all data" do
|
5
|
+
let(:data_set) { Movie.random_collection }
|
6
|
+
|
7
|
+
it "Uses no query string " do
|
8
|
+
results = QueryStringSearch.new(data_set, nil).results
|
9
|
+
expect(results).to eq(data_set)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative "../../lib/query_string_search"
|
2
|
+
require_relative "../fixtures/movie"
|
3
|
+
|
4
|
+
RSpec.describe "Finding data that match multiple parameters" do
|
5
|
+
let(:data_set) { Movie.random_collection }
|
6
|
+
let(:random_movie) { data_set.sample }
|
7
|
+
|
8
|
+
let(:movies_with_country) { data_set.select { |d| d.country == random_movie.country } }
|
9
|
+
let(:movies_with_year) { data_set.select { |d| d.year == random_movie.year } }
|
10
|
+
let(:movies_with_rating) { data_set.select { |d| d.rating == random_movie.rating } }
|
11
|
+
|
12
|
+
let(:expected) { movies_with_country & movies_with_year & movies_with_rating }
|
13
|
+
|
14
|
+
it "Returns records that match the requested value" do
|
15
|
+
query_string = "country=#{random_movie.country},year=#{random_movie.year},rating=#{random_movie.rating || 'none'}"
|
16
|
+
results = QueryStringSearch.new(data_set, query_string).results
|
17
|
+
|
18
|
+
expect(expected).to eq(results)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "when the objects do not respond one of the attributes" do
|
22
|
+
it "returns an empty collection" do
|
23
|
+
query_string = "country=#{random_movie.country},year=#{random_movie.year},rating=#{random_movie.rating},monkey=golden"
|
24
|
+
results = QueryStringSearch.new(data_set, query_string).results
|
25
|
+
|
26
|
+
expect { data_set.sample.monkey }.to raise_error(NoMethodError)
|
27
|
+
|
28
|
+
expect(results).to eq([])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative "../../lib/query_string_search"
|
2
|
+
require_relative "../fixtures/movie"
|
3
|
+
require "erb"
|
4
|
+
include ERB::Util
|
5
|
+
|
6
|
+
RSpec.describe "Finding data with matching attribute values" do
|
7
|
+
let(:data_set) { Movie.random_collection }
|
8
|
+
let(:movies_with_us_country) { data_set.select { |d| d.country == "US" } }
|
9
|
+
|
10
|
+
it "Returns records that match the requested value" do
|
11
|
+
results = QueryStringSearch.new(data_set, "country=US").results
|
12
|
+
|
13
|
+
expect(results).to eq(movies_with_us_country)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "is case-insensitive" do
|
17
|
+
results = QueryStringSearch.new(data_set, "country=us").results
|
18
|
+
|
19
|
+
expect(results).to eq(movies_with_us_country)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "when the values have spaces" do
|
23
|
+
let(:random_movie) { data_set.sample }
|
24
|
+
# there's a chance that more than one movie has the same title
|
25
|
+
let(:movies_with_title) { data_set.select { |d| d.title == random_movie.title } }
|
26
|
+
|
27
|
+
it "matches if the query-string is not escaped" do
|
28
|
+
results = QueryStringSearch.new(data_set, "title=#{random_movie.title}").results
|
29
|
+
|
30
|
+
expect(results).to eq(movies_with_title)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "matches if the query-string is escaped" do
|
34
|
+
results = QueryStringSearch.new(data_set, "title=#{url_encode(random_movie.title)}").results
|
35
|
+
|
36
|
+
expect(results).to eq(movies_with_title)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "when the objects do not respond to the attribute" do
|
41
|
+
it "returns an empty result set" do
|
42
|
+
results = QueryStringSearch.new(data_set, "monkey=golden").results
|
43
|
+
|
44
|
+
expect { data_set.sample.monkey }.to raise_error(NoMethodError)
|
45
|
+
|
46
|
+
expect(results).to eq([])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "../../lib/query_string_search"
|
2
|
+
require_relative "../fixtures/movie"
|
3
|
+
|
4
|
+
RSpec.describe "Finding data with nil attributes" do
|
5
|
+
let(:data_set) { Movie.random_collection }
|
6
|
+
let(:movies_with_nil_ratings) { data_set.select { |d| d.rating.nil? } }
|
7
|
+
|
8
|
+
it "Returns nil-attribute records with a query-string value of false" do
|
9
|
+
results = QueryStringSearch.new(data_set, "rating=false").results
|
10
|
+
|
11
|
+
expect(results).to eq(movies_with_nil_ratings)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "Returns nil-attribute records with a query-string value of none" do
|
15
|
+
results = QueryStringSearch.new(data_set, "rating=none").results
|
16
|
+
|
17
|
+
expect(results).to eq(movies_with_nil_ratings)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "when the objects do not respond to the attribute" do
|
21
|
+
it "returns an empty collection" do
|
22
|
+
results = QueryStringSearch.new(data_set, "monkey=false").results
|
23
|
+
|
24
|
+
expect { data_set.sample.monkey }.to raise_error(NoMethodError)
|
25
|
+
|
26
|
+
expect(results).to be_empty
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "../../lib/query_string_search"
|
2
|
+
require_relative "../fixtures/movie"
|
3
|
+
|
4
|
+
RSpec.describe "Finding data with non-nil attributes" do
|
5
|
+
let(:data_set) { Movie.random_collection }
|
6
|
+
let(:movies_with_non_nil_ratings) { data_set.select { |d| !d.rating.nil? } }
|
7
|
+
|
8
|
+
it "Returns non-nil-attribute records with a query-string value of true" do
|
9
|
+
results = QueryStringSearch.new(data_set, "rating=true").results
|
10
|
+
|
11
|
+
expect(results).to eq(movies_with_non_nil_ratings)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "Returns non-nil-attribute records with a query-string value of all" do
|
15
|
+
results = QueryStringSearch.new(data_set, "rating=all").results
|
16
|
+
|
17
|
+
expect(results).to eq(movies_with_non_nil_ratings)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "when the objects do not respond to the attribute" do
|
21
|
+
it "returns an empty collection" do
|
22
|
+
results = QueryStringSearch.new(data_set, "monkey=true").results
|
23
|
+
|
24
|
+
expect { data_set.sample.monkey }.to raise_error(NoMethodError)
|
25
|
+
|
26
|
+
expect(results).to be_empty
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative "../../lib/query_string_search"
|
2
|
+
require_relative "../fixtures/movie"
|
3
|
+
|
4
|
+
RSpec.describe "Finding movies" do
|
5
|
+
let(:data_set) { Movie.random_collection }
|
6
|
+
|
7
|
+
describe "that have been seen" do
|
8
|
+
let(:seen_movies) { data_set.select(&:seen?) }
|
9
|
+
|
10
|
+
it "uses a query-string of 'seen?=true'" do
|
11
|
+
results = QueryStringSearch.new(data_set, "seen?=true").results
|
12
|
+
|
13
|
+
expect(results).to eq(seen_movies)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "that have not been seen" do
|
18
|
+
let(:unseen_movies) { data_set.select { |d| !d.seen? } }
|
19
|
+
|
20
|
+
it "uses a query-string of 'seen?=false'" do
|
21
|
+
results = QueryStringSearch.new(data_set, "seen?=false").results
|
22
|
+
|
23
|
+
expect(results).to eq(unseen_movies)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Movie
|
2
|
+
attr_accessor :title, :rating, :year, :country, :seen
|
3
|
+
|
4
|
+
def self.random_collection(count = 1_000)
|
5
|
+
(1...count).inject([]) do |collection, _|
|
6
|
+
collection << Movie.new(random_title, random_rating, random_year, random_country)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(title, rating, year, country)
|
11
|
+
self.title = title
|
12
|
+
self.rating = rating
|
13
|
+
self.year = year
|
14
|
+
self.country = country
|
15
|
+
self.seen = [true, false].sample
|
16
|
+
end
|
17
|
+
|
18
|
+
def seen?
|
19
|
+
seen
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.random_title
|
23
|
+
"Random Movie #{rand(10_000)}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.random_rating
|
27
|
+
["X", "NC-17", "R", "PG-13", "PG", "G", nil].sample
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.random_year
|
31
|
+
(1990..2014).to_a.sample.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.random_country
|
35
|
+
%w(
|
36
|
+
US CAN UK HK BR RUS FR IN
|
37
|
+
).sample
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
require_relative "../../../lib/query_string_search/abstract_matcher"
|
3
|
+
|
4
|
+
RSpec.describe QueryStringSearch::AbstractMatcher do
|
5
|
+
describe "match?" do
|
6
|
+
it "is false" do
|
7
|
+
value = rand
|
8
|
+
target = OpenStruct.new(other: value)
|
9
|
+
it = QueryStringSearch::AbstractMatcher.new(:other, value)
|
10
|
+
expect(it.match?(target)).to be_falsey
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "build_me?" do
|
15
|
+
it "is true" do
|
16
|
+
it = QueryStringSearch::AbstractMatcher.build_me?(rand, rand)
|
17
|
+
expect(it).to be_truthy
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "matchers" do
|
22
|
+
it "is has self as last element " do
|
23
|
+
it = QueryStringSearch::AbstractMatcher.matchers
|
24
|
+
expect(it.last).to eq(QueryStringSearch::AbstractMatcher)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative "../../../lib/query_string_search/matcher_factory"
|
2
|
+
require_relative "../../../lib/query_string_search/search_options"
|
3
|
+
|
4
|
+
RSpec.describe QueryStringSearch::MatcherFactory do
|
5
|
+
let(:matcher_double) { class_double("MatchAttribute") }
|
6
|
+
let(:param_double) { double("QueryStringSearch::SearchOptions") }
|
7
|
+
let(:build_candidates) { [matcher_double] }
|
8
|
+
|
9
|
+
before do
|
10
|
+
allow(param_double).to receive(:search_param).and_return("test_search_param")
|
11
|
+
allow(param_double).to receive(:search_type).and_return("test_search_type")
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "build" do
|
15
|
+
describe "finds a matcher to build" do
|
16
|
+
it "builds that matcher and returns it" do
|
17
|
+
test_return = Object.new
|
18
|
+
expect(matcher_double).to receive(:build_me?).with(param_double.search_type, param_double.search_param).and_return(true)
|
19
|
+
expect(matcher_double).to receive(:new).with(param_double.search_type, param_double.search_param).and_return(test_return)
|
20
|
+
expect(QueryStringSearch::MatcherFactory.build(param_double, build_candidates)).to eq(test_return)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "does not find a matcher to build" do
|
25
|
+
it "returns nil" do
|
26
|
+
expect(matcher_double).to receive(:build_me?).and_return(false)
|
27
|
+
expect(QueryStringSearch::MatcherFactory.build(param_double, build_candidates)).to be_nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative "../../../../lib/query_string_search/abstract_matcher"
|
2
|
+
|
3
|
+
RSpec.describe MatchAll do
|
4
|
+
describe "match?" do
|
5
|
+
it "is true" do
|
6
|
+
it = MatchAll.new(:other, rand)
|
7
|
+
expect(it.match?(rand)).to be_truthy
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "build_me?" do
|
12
|
+
describe "given a nil search_type and search_param" do
|
13
|
+
it "is true" do
|
14
|
+
expect(MatchAll.build_me?(nil, nil)).to be_truthy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "given a non-nil search_type or search_param" do
|
19
|
+
it "is false" do
|
20
|
+
expect(MatchAll.build_me?(rand, nil)).to be_falsey
|
21
|
+
expect(MatchAll.build_me?(nil, rand)).to be_falsey
|
22
|
+
expect(MatchAll.build_me?(rand, rand)).to be_falsey
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative "../../../../lib/query_string_search/abstract_matcher"
|
2
|
+
|
3
|
+
RSpec.describe MatchAttribute do
|
4
|
+
describe "match?" do
|
5
|
+
describe "if the target's attribute is not nil" do
|
6
|
+
let(:target) { Target.new("search_value") }
|
7
|
+
let(:subject) { MatchAttribute.new(:search_attr) }
|
8
|
+
|
9
|
+
it "is true" do
|
10
|
+
expect(subject.match?(target)).to be_truthy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "if the target's attribute is true" do
|
15
|
+
let(:target) { Target.new(true) }
|
16
|
+
let(:subject) { MatchAttribute.new(:search_attr) }
|
17
|
+
|
18
|
+
it "is true" do
|
19
|
+
expect(subject.match?(target)).to be_truthy
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "if the target's attribute is nil" do
|
24
|
+
let(:target) { Target.new(nil) }
|
25
|
+
let(:subject) { MatchAttribute.new(:search_attr) }
|
26
|
+
|
27
|
+
it "is false" do
|
28
|
+
expect(subject.match?(target)).to be_falsey
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "if the target's attribute is false" do
|
33
|
+
let(:target) { Target.new(false) }
|
34
|
+
let(:subject) { MatchAttribute.new(:search_attr) }
|
35
|
+
|
36
|
+
it "is true" do
|
37
|
+
expect(subject.match?(target)).to be_falsey
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "if the target doesn't have the attribute" do
|
42
|
+
let(:target) { Target.new(rand) }
|
43
|
+
let(:subject) { MatchAttribute.new(:bad_attr) }
|
44
|
+
|
45
|
+
it "is false" do
|
46
|
+
expect(subject.match?(target)).to be_falsey
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "build_me?" do
|
52
|
+
describe "given a search param of 'all' or 'true'" do
|
53
|
+
it "is true" do
|
54
|
+
%w(all true).each do |search_param|
|
55
|
+
expect(MatchAttribute.build_me?(rand, search_param)).to be_truthy
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "given a search param that is not 'all' or 'true'" do
|
61
|
+
it "is false" do
|
62
|
+
%w(1all rue).each do |search_param|
|
63
|
+
expect(MatchAttribute.build_me?(rand, search_param)).to be_falsey
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative "../../../../lib/query_string_search/abstract_matcher"
|
2
|
+
|
3
|
+
RSpec.describe MatchAttributeValue do
|
4
|
+
describe "match?" do
|
5
|
+
describe "given a target with an attribute that matches the Parameter's attribute" do
|
6
|
+
let(:target) { Target.new("search_value") }
|
7
|
+
let(:value) { "search_value" }
|
8
|
+
let(:subject) { MatchAttributeValue.new(:search_attr, value) }
|
9
|
+
|
10
|
+
it "returns true" do
|
11
|
+
expect(subject.match?(target)).to be_truthy
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "given a value with spaces" do
|
16
|
+
let(:target) { Target.new("search value") }
|
17
|
+
let(:value) { "search value" }
|
18
|
+
let(:subject) { MatchAttributeValue.new(:search_attr, value) }
|
19
|
+
|
20
|
+
it "returns true" do
|
21
|
+
expect(subject.match?(target)).to be_truthy
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "given a target with an attribute that does not match the Parameter's attribute" do
|
26
|
+
let(:target) { Target.new("other_value") }
|
27
|
+
let(:value) { "search_value" }
|
28
|
+
let(:subject) { MatchAttributeValue.new(:search_attr, value) }
|
29
|
+
|
30
|
+
it "returns false" do
|
31
|
+
expect(subject.match?(target)).to be_falsey
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "if the target doesn't have the attribute" do
|
36
|
+
let(:value) { "search_value" }
|
37
|
+
let(:target) { Target.new(value) }
|
38
|
+
let(:subject) { MatchAttribute.new(:bad_attr, value) }
|
39
|
+
|
40
|
+
it "is false" do
|
41
|
+
expect(subject.match?(target)).to be_falsey
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "build_me?" do
|
47
|
+
describe "given a non-nil search_type and search_param" do
|
48
|
+
it "is true" do
|
49
|
+
expect(MatchAttributeValue.build_me?(rand, rand)).to be_truthy
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "given a nil search_type or search_param" do
|
54
|
+
it "is false" do
|
55
|
+
expect(MatchAttributeValue.build_me?(rand, nil)).to be_falsey
|
56
|
+
expect(MatchAttributeValue.build_me?(nil, rand)).to be_falsey
|
57
|
+
expect(MatchAttributeValue.build_me?(nil, nil)).to be_falsey
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative "../../../../lib/query_string_search/abstract_matcher"
|
2
|
+
|
3
|
+
RSpec.describe MatchNoAttribute do
|
4
|
+
describe "match?" do
|
5
|
+
describe "if the target's attribute is not nil" do
|
6
|
+
let(:target) { Target.new("search_value") }
|
7
|
+
let(:subject) { MatchNoAttribute.new(:search_attr) }
|
8
|
+
|
9
|
+
it "is false" do
|
10
|
+
expect(subject.match?(target)).to be_falsey
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "if the target's attribute is true" do
|
15
|
+
let(:target) { Target.new(true) }
|
16
|
+
let(:subject) { MatchNoAttribute.new(:search_attr) }
|
17
|
+
|
18
|
+
it "is true" do
|
19
|
+
expect(subject.match?(target)).to be_falsey
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "if the target's attribute is false" do
|
24
|
+
let(:target) { Target.new(false) }
|
25
|
+
let(:subject) { MatchNoAttribute.new(:search_attr) }
|
26
|
+
|
27
|
+
it "is true" do
|
28
|
+
expect(subject.match?(target)).to be_truthy
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "if the target's attribute is nil" do
|
33
|
+
let(:target) { Target.new(nil) }
|
34
|
+
let(:subject) { MatchNoAttribute.new(:search_attr) }
|
35
|
+
|
36
|
+
it "is true" do
|
37
|
+
expect(subject.match?(target)).to be_truthy
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "if the target doesn't have the attribute" do
|
42
|
+
let(:target) { Target.new(rand) }
|
43
|
+
let(:subject) { MatchNoAttribute.new(:bad_attr) }
|
44
|
+
|
45
|
+
it "is false" do
|
46
|
+
expect(subject.match?(target)).to be_falsey
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "build_me?" do
|
52
|
+
describe "given a search param of 'none' or 'false'" do
|
53
|
+
it "is true" do
|
54
|
+
%w(none false).each do |search_param|
|
55
|
+
expect(MatchNoAttribute.build_me?(rand, search_param)).to be_truthy
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "given a search param that is not 'none' or 'false'" do
|
61
|
+
it "is false" do
|
62
|
+
%w(one falsey).each do |search_param|
|
63
|
+
expect(MatchNoAttribute.build_me?(rand, search_param)).to be_falsey
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative "../../../lib/query_string_search/search_options"
|
2
|
+
|
3
|
+
RSpec.describe QueryStringSearch::SearchOptions do
|
4
|
+
describe "parse" do
|
5
|
+
describe "with a single-element and multi-element query_string" do
|
6
|
+
let(:single_query_string) { "test=filter" }
|
7
|
+
let(:multi_query_string) { "test=filter,test2=test%20attribute" }
|
8
|
+
let(:single_element) { QueryStringSearch::SearchOptions.parse(single_query_string) }
|
9
|
+
let(:multi_element) { QueryStringSearch::SearchOptions.parse(multi_query_string) }
|
10
|
+
|
11
|
+
describe "returns a collection" do
|
12
|
+
it "that is enumerable" do
|
13
|
+
[single_element, multi_element].each do |it|
|
14
|
+
expect(it).to respond_to(:each)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "whose contents" do
|
19
|
+
it "have search_type that are symbols" do
|
20
|
+
expect(single_element.collect(&:search_type)).to eq([:test])
|
21
|
+
expect(multi_element.collect(&:search_type)).to eq([:test, :test2])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "have search_param that are unescaped strings" do
|
25
|
+
expect(single_element.collect(&:search_param)).to eq(["filter"])
|
26
|
+
expect(multi_element.collect(&:search_param)).to eq(["filter", "test attribute"])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "with a nil" do
|
33
|
+
let(:nil_element) { QueryStringSearch::SearchOptions.parse(nil) }
|
34
|
+
describe "returns a collection" do
|
35
|
+
it "that is enumerable" do
|
36
|
+
expect(nil_element).to respond_to(:each)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "with one element" do
|
40
|
+
expect(nil_element.count).to eq(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "with nil search_type" do
|
44
|
+
expect(nil_element.first.search_type).to be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it "with nil search_param" do
|
48
|
+
expect(nil_element.first.search_param).to be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative "../../../lib/query_string_search/search_parameters"
|
2
|
+
require_relative "../../../lib/query_string_search/abstract_matcher"
|
3
|
+
require_relative "../../../lib/query_string_search/search_options"
|
4
|
+
|
5
|
+
RSpec.describe QueryStringSearch::SearchParameters do
|
6
|
+
let(:factory) { class_double("QueryStringSearch::MatcherFactory") }
|
7
|
+
let(:matchers) { QueryStringSearch::AbstractMatcher.matchers }
|
8
|
+
let(:options_double) { double("search_options") }
|
9
|
+
|
10
|
+
describe "build_from_querystring" do
|
11
|
+
it "parses the query_string and creates an instance of SearchParameters" do
|
12
|
+
expect(QueryStringSearch::SearchOptions).to receive(:parse).with("test=filter").and_return(options_double)
|
13
|
+
expect(QueryStringSearch::SearchParameters).to receive(:new).with(options_double, factory, matchers)
|
14
|
+
QueryStringSearch::SearchParameters.build_from_querystring("test=filter", factory, matchers)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "new" do
|
19
|
+
it "uses the factory to build each parameter" do
|
20
|
+
params = QueryStringSearch::SearchOptions.parse("test=filter")
|
21
|
+
params.each do |p|
|
22
|
+
expect(factory).to receive(:build).with(p, matchers)
|
23
|
+
end
|
24
|
+
QueryStringSearch::SearchParameters.new(params, factory, matchers)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
4
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
5
|
+
#
|
6
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
7
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
8
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
9
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
10
|
+
# a separate helper file that requires the additional dependencies and performs
|
11
|
+
# the additional setup, and require it from the spec files that actually need it.
|
12
|
+
#
|
13
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
14
|
+
# users commonly want.
|
15
|
+
#
|
16
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
17
|
+
RSpec.configure do |config|
|
18
|
+
# rspec-expectations config goes here. You can use an alternate
|
19
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
20
|
+
# assertions if you prefer.
|
21
|
+
config.expect_with :rspec do |expectations|
|
22
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
23
|
+
# and `failure_message` of custom matchers include text for helper methods
|
24
|
+
# defined using `chain`, e.g.:
|
25
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
26
|
+
# # => "be bigger than 2 and smaller than 4"
|
27
|
+
# ...rather than:
|
28
|
+
# # => "be bigger than 2"
|
29
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
30
|
+
end
|
31
|
+
|
32
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
33
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
34
|
+
config.mock_with :rspec do |mocks|
|
35
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
36
|
+
# a real object. This is generally recommended, and will default to
|
37
|
+
# `true` in RSpec 4.
|
38
|
+
mocks.verify_partial_doubles = true
|
39
|
+
end
|
40
|
+
|
41
|
+
# The settings below are suggested to provide a good initial experience
|
42
|
+
# with RSpec, but feel free to customize to your heart's content.
|
43
|
+
# These two settings work together to allow you to limit a spec run
|
44
|
+
# to individual examples or groups you care about by tagging them with
|
45
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
46
|
+
# get run.
|
47
|
+
config.filter_run :focus
|
48
|
+
config.run_all_when_everything_filtered = true
|
49
|
+
|
50
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
51
|
+
# For more details, see:
|
52
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
53
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
54
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
55
|
+
config.disable_monkey_patching!
|
56
|
+
|
57
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
58
|
+
# file, and it's useful to allow more verbose output when running an
|
59
|
+
# individual spec file.
|
60
|
+
if config.files_to_run.one?
|
61
|
+
# Use the documentation formatter for detailed output,
|
62
|
+
# unless a formatter has already been configured
|
63
|
+
# (e.g. via a command-line flag).
|
64
|
+
config.default_formatter = 'doc'
|
65
|
+
end
|
66
|
+
|
67
|
+
# Print the 10 slowest examples and example groups at the
|
68
|
+
# end of the spec run, to help surface which specs are running
|
69
|
+
# particularly slow.
|
70
|
+
config.profile_examples = 10
|
71
|
+
|
72
|
+
# Run specs in random order to surface order dependencies. If you find an
|
73
|
+
# order dependency and want to debug it, you can fix the order by providing
|
74
|
+
# the seed, which is printed after each run.
|
75
|
+
# --seed 1234
|
76
|
+
config.order = :random
|
77
|
+
|
78
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
79
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
80
|
+
# test failures related to randomization by passing the same `--seed` value
|
81
|
+
# as the one that triggered the failure.
|
82
|
+
Kernel.srand config.seed
|
83
|
+
end
|
84
|
+
|
85
|
+
Target = Struct.new(:search_attr)
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: query_string_search
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ian Whitney
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- whit0694@umn.edu
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".rubocop.yml"
|
79
|
+
- ".ruby-version"
|
80
|
+
- Gemfile
|
81
|
+
- LICENSE.txt
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- lib/query_string_search.rb
|
85
|
+
- lib/query_string_search/abstract_matcher.rb
|
86
|
+
- lib/query_string_search/matcher_factory.rb
|
87
|
+
- lib/query_string_search/matchers/match_all.rb
|
88
|
+
- lib/query_string_search/matchers/match_attribute.rb
|
89
|
+
- lib/query_string_search/matchers/match_attribute_value.rb
|
90
|
+
- lib/query_string_search/matchers/match_no_attribute.rb
|
91
|
+
- lib/query_string_search/search_options.rb
|
92
|
+
- lib/query_string_search/search_parameters.rb
|
93
|
+
- lib/query_string_search/version.rb
|
94
|
+
- query_string_search.gemspec
|
95
|
+
- spec/features/find_all_data_spec.rb
|
96
|
+
- spec/features/find_records_that_match_multiple_parameters_spec.rb
|
97
|
+
- spec/features/find_records_with_matching_attribute_values_spec.rb
|
98
|
+
- spec/features/find_records_with_nil_attributes_spec.rb
|
99
|
+
- spec/features/find_records_with_nonnil_attributes_spec.rb
|
100
|
+
- spec/features/find_seen_and_unseen_movies_spec.rb
|
101
|
+
- spec/fixtures/movie.rb
|
102
|
+
- spec/lib/query_string_search/abstract_matcher_spec.rb
|
103
|
+
- spec/lib/query_string_search/matcher_factory_spec.rb
|
104
|
+
- spec/lib/query_string_search/matchers/match_all_spec.rb
|
105
|
+
- spec/lib/query_string_search/matchers/match_attribute_spec.rb
|
106
|
+
- spec/lib/query_string_search/matchers/match_attribute_value_spec.rb
|
107
|
+
- spec/lib/query_string_search/matchers/match_no_attribute_spec.rb
|
108
|
+
- spec/lib/query_string_search/search_options_spec.rb
|
109
|
+
- spec/lib/query_string_search/search_parameters_spec.rb
|
110
|
+
- spec/spec_helper.rb
|
111
|
+
homepage: https://github.com/umn-asr/query_string_search
|
112
|
+
licenses:
|
113
|
+
- ''
|
114
|
+
metadata: {}
|
115
|
+
post_install_message:
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubyforge_project:
|
131
|
+
rubygems_version: 2.2.2
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: Provides a standard way to do searches using query strings in an API endpoint
|
135
|
+
test_files:
|
136
|
+
- spec/features/find_all_data_spec.rb
|
137
|
+
- spec/features/find_records_that_match_multiple_parameters_spec.rb
|
138
|
+
- spec/features/find_records_with_matching_attribute_values_spec.rb
|
139
|
+
- spec/features/find_records_with_nil_attributes_spec.rb
|
140
|
+
- spec/features/find_records_with_nonnil_attributes_spec.rb
|
141
|
+
- spec/features/find_seen_and_unseen_movies_spec.rb
|
142
|
+
- spec/fixtures/movie.rb
|
143
|
+
- spec/lib/query_string_search/abstract_matcher_spec.rb
|
144
|
+
- spec/lib/query_string_search/matcher_factory_spec.rb
|
145
|
+
- spec/lib/query_string_search/matchers/match_all_spec.rb
|
146
|
+
- spec/lib/query_string_search/matchers/match_attribute_spec.rb
|
147
|
+
- spec/lib/query_string_search/matchers/match_attribute_value_spec.rb
|
148
|
+
- spec/lib/query_string_search/matchers/match_no_attribute_spec.rb
|
149
|
+
- spec/lib/query_string_search/search_options_spec.rb
|
150
|
+
- spec/lib/query_string_search/search_parameters_spec.rb
|
151
|
+
- spec/spec_helper.rb
|