scoped_search 4.1.5 → 4.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.rdoc +5 -0
- data/lib/scoped_search/auto_complete_builder.rb +7 -1
- data/lib/scoped_search/definition.rb +14 -2
- data/lib/scoped_search/query_builder.rb +12 -2
- data/lib/scoped_search/version.rb +1 -1
- data/spec/integration/auto_complete_spec.rb +15 -3
- data/spec/integration/uuid_query_spec.rb +57 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/unit/auto_complete_builder_spec.rb +15 -0
- data/spec/unit/query_builder_spec.rb +39 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c94c65fc191d2afdddd82f3177121d2ade7e6238
|
4
|
+
data.tar.gz: 36dd864d06aafd40bd3015184a17222a92d5627f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a41edacae0b5140f56dc60bc921c8afb3349132b9ad3453313fc407abbda58b251c08d1da8d56706f9307520882e9e8006f18147833c91b7e3d0995962d843e
|
7
|
+
data.tar.gz: 31932dcd317193df257a4d0fd0c46fea28866e7670a0cfbd1d840d4477ada8381906d5f83b9289b322de3bbc44048ec249d9bf6af67f605bfc76589be9ccc7e3
|
data/CHANGELOG.rdoc
CHANGED
@@ -8,6 +8,11 @@ Please add an entry to the "Unreleased changes" section in your pull requests.
|
|
8
8
|
|
9
9
|
- Nothing yet
|
10
10
|
|
11
|
+
=== Version 4.1.6
|
12
|
+
|
13
|
+
- Support for UUID columns on PostgreSQL (#184)
|
14
|
+
- Allow value mapping on runtime to allow queries like "user = current" (#183)
|
15
|
+
|
11
16
|
=== Version 4.1.5
|
12
17
|
|
13
18
|
- Bugfix related to auto-completion of virtual fields (#182)
|
@@ -201,10 +201,16 @@ module ScopedSearch
|
|
201
201
|
return complete_date_value if field.temporal?
|
202
202
|
return complete_key_value(field, token, val) if field.key_field
|
203
203
|
|
204
|
+
special_values = field.special_values.select { |v| v =~ /\A#{val}/ }
|
205
|
+
special_values + complete_value_from_db(field, special_values, val)
|
206
|
+
end
|
207
|
+
|
208
|
+
def complete_value_from_db(field, special_values, val)
|
209
|
+
count = 20 - special_values.count
|
204
210
|
completer_scope(field)
|
205
211
|
.where(value_conditions(field.quoted_field, val))
|
206
212
|
.select(field.quoted_field)
|
207
|
-
.limit(
|
213
|
+
.limit(count)
|
208
214
|
.distinct
|
209
215
|
.map(&field.field)
|
210
216
|
.compact
|
@@ -17,7 +17,7 @@ module ScopedSearch
|
|
17
17
|
|
18
18
|
attr_reader :definition, :field, :only_explicit, :relation, :key_relation, :full_text_search,
|
19
19
|
:key_field, :complete_value, :complete_enabled, :offset, :word_size, :ext_method, :operators,
|
20
|
-
:validator
|
20
|
+
:validator, :value_translation, :special_values
|
21
21
|
|
22
22
|
# Initializes a Field instance given the definition passed to the
|
23
23
|
# scoped_search call on the ActiveRecord-based model class.
|
@@ -42,7 +42,9 @@ module ScopedSearch
|
|
42
42
|
profile: nil,
|
43
43
|
relation: nil,
|
44
44
|
rename: nil,
|
45
|
+
special_values: [],
|
45
46
|
validator: nil,
|
47
|
+
value_translation: nil,
|
46
48
|
word_size: 1,
|
47
49
|
**kwargs)
|
48
50
|
|
@@ -50,6 +52,8 @@ module ScopedSearch
|
|
50
52
|
raise ArgumentError, "Missing field or 'on' keyword argument" if on.nil?
|
51
53
|
@field = on.to_sym
|
52
54
|
|
55
|
+
raise ArgumentError, "'special_values' must be an Array" unless special_values.kind_of?(Array)
|
56
|
+
|
53
57
|
# Reserved Ruby keywords so access via kwargs instead, but deprecate them for future versions
|
54
58
|
if kwargs.key?(:in)
|
55
59
|
relation = kwargs.delete(:in)
|
@@ -77,8 +81,10 @@ module ScopedSearch
|
|
77
81
|
@only_explicit = !!only_explicit
|
78
82
|
@operators = operators
|
79
83
|
@relation = relation
|
84
|
+
@special_values = special_values
|
80
85
|
@validator = validator
|
81
86
|
@word_size = word_size
|
87
|
+
@value_translation = value_translation
|
82
88
|
|
83
89
|
# Store this field in the field array
|
84
90
|
definition.define_field(rename || @field, self)
|
@@ -154,6 +160,10 @@ module ScopedSearch
|
|
154
160
|
[:string, :text].include?(type)
|
155
161
|
end
|
156
162
|
|
163
|
+
def uuid?
|
164
|
+
type == :uuid
|
165
|
+
end
|
166
|
+
|
157
167
|
# Returns true if this is a set.
|
158
168
|
def set?
|
159
169
|
complete_value.is_a?(Hash)
|
@@ -238,7 +248,7 @@ module ScopedSearch
|
|
238
248
|
return [] if field.nil?
|
239
249
|
return field.operators if field.operators
|
240
250
|
return ['=', '!=', '>', '<', '<=', '>=', '~', '!~', '^', '!^'] if field.virtual?
|
241
|
-
return ['=', '!='] if field.set?
|
251
|
+
return ['=', '!='] if field.set? || field.uuid?
|
242
252
|
return ['=', '>', '<', '<=', '>=', '!=', '^', '!^'] if field.numerical?
|
243
253
|
return ['=', '!=', '~', '!~', '^', '!^'] if field.textual?
|
244
254
|
return ['=', '>', '<'] if field.temporal?
|
@@ -247,6 +257,7 @@ module ScopedSearch
|
|
247
257
|
|
248
258
|
NUMERICAL_REGXP = /^\-?\d+(\.\d+)?$/
|
249
259
|
INTEGER_REGXP = /^\-?\d+$/
|
260
|
+
UUID_REGXP = /^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/
|
250
261
|
|
251
262
|
# Returns a list of appropriate fields to search in given a search keyword and operator.
|
252
263
|
def default_fields_for(value, operator = nil)
|
@@ -255,6 +266,7 @@ module ScopedSearch
|
|
255
266
|
column_types += [:string, :text] if [nil, :like, :unlike, :ne, :eq].include?(operator)
|
256
267
|
column_types += [:double, :float, :decimal] if value =~ NUMERICAL_REGXP
|
257
268
|
column_types += [:integer] if value =~ INTEGER_REGXP
|
269
|
+
column_types += [:uuid] if value =~ UUID_REGXP
|
258
270
|
column_types += [:datetime, :date, :timestamp] if (parse_temporal(value))
|
259
271
|
|
260
272
|
default_fields.select { |field| !field.set? && column_types.include?(field.type) }
|
@@ -181,6 +181,14 @@ module ScopedSearch
|
|
181
181
|
translated_value
|
182
182
|
end
|
183
183
|
|
184
|
+
def map_value(field, value)
|
185
|
+
old_value = value
|
186
|
+
translator = field.value_translation
|
187
|
+
value = translator.call(value) if translator
|
188
|
+
raise ScopedSearch::QueryNotSupported, "Translation from any value to nil is not allowed, translated '#{old_value}'" if value.nil?
|
189
|
+
value
|
190
|
+
end
|
191
|
+
|
184
192
|
# A 'set' is group of possible values, for example a status might be "on", "off" or "unknown" and the database representation
|
185
193
|
# could be for example a numeric value. This method will validate the input and translate it into the database representation.
|
186
194
|
def set_test(field, operator,value, &block)
|
@@ -219,7 +227,7 @@ module ScopedSearch
|
|
219
227
|
return "#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ?"
|
220
228
|
|
221
229
|
elsif [:in, :notin].include?(operator)
|
222
|
-
value.split(',').collect { |v| yield(:parameter, field.set? ? translate_value(field, v) : v.strip) }
|
230
|
+
value.split(',').collect { |v| yield(:parameter, map_value(field, field.set? ? translate_value(field, v) : v.strip)) }
|
223
231
|
value = value.split(',').collect { "?" }.join(",")
|
224
232
|
return "#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} (#{value})"
|
225
233
|
|
@@ -231,6 +239,7 @@ module ScopedSearch
|
|
231
239
|
|
232
240
|
elsif field.relation && definition.reflection_by_name(field.definition.klass, field.relation).macro == :has_many
|
233
241
|
value = value.to_i if field.offset
|
242
|
+
value = map_value(field, value)
|
234
243
|
yield(:parameter, value)
|
235
244
|
connection = field.definition.klass.connection
|
236
245
|
primary_key = "#{connection.quote_table_name(field.definition.klass.table_name)}.#{connection.quote_column_name(field.definition.klass.primary_key)}"
|
@@ -244,6 +253,7 @@ module ScopedSearch
|
|
244
253
|
|
245
254
|
else
|
246
255
|
value = value.to_i if field.offset
|
256
|
+
value = map_value(field, value)
|
247
257
|
yield(:parameter, value)
|
248
258
|
return "#{field.to_sql(operator, &block)} #{self.sql_operator(operator, field)} ?"
|
249
259
|
end
|
@@ -517,7 +527,7 @@ module ScopedSearch
|
|
517
527
|
def validate_value(field, value)
|
518
528
|
validator = field.validator
|
519
529
|
if validator
|
520
|
-
valid = validator.call(value)
|
530
|
+
valid = field.special_values.include?(value) || validator.call(value)
|
521
531
|
raise ScopedSearch::QueryNotSupported, "Value '#{value}' is not valid for field '#{field.field}'" unless valid
|
522
532
|
end
|
523
533
|
end
|
@@ -25,6 +25,12 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
25
25
|
t.integer :int
|
26
26
|
t.date :date
|
27
27
|
t.integer :unindexed
|
28
|
+
|
29
|
+
if on_postgresql?
|
30
|
+
t.uuid :uuid
|
31
|
+
else
|
32
|
+
t.string :uuid
|
33
|
+
end
|
28
34
|
end
|
29
35
|
|
30
36
|
class ::Bar < ActiveRecord::Base
|
@@ -35,7 +41,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
35
41
|
has_many :bars
|
36
42
|
default_scope { order(:string) }
|
37
43
|
|
38
|
-
scoped_search :on => [:string, :date]
|
44
|
+
scoped_search :on => [:string, :date, :uuid]
|
39
45
|
scoped_search :on => [:int], :complete_value => true
|
40
46
|
scoped_search :on => :another, :default_operator => :eq, :aliases => [:alias], :complete_value => true
|
41
47
|
scoped_search :on => :explicit, :only_explicit => true, :complete_value => true
|
@@ -90,6 +96,12 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
90
96
|
Foo.complete_for('date ').should =~ (["date = ", "date < ", "date > "])
|
91
97
|
end
|
92
98
|
|
99
|
+
it "should complete the uuid comparators" do
|
100
|
+
if on_postgresql?
|
101
|
+
Foo.complete_for('uuid ').should =~ (["uuid = ", "uuid != "])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
93
105
|
it "should complete when query is already distinct" do
|
94
106
|
Foo.distinct.complete_for('int =').length.should > 0
|
95
107
|
end
|
@@ -190,7 +202,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
190
202
|
context 'exceptional search strings' do
|
191
203
|
|
192
204
|
it "query that starts with 'or'" do
|
193
|
-
Foo.complete_for('or ').length.should ==
|
205
|
+
Foo.complete_for('or ').length.should == 10
|
194
206
|
end
|
195
207
|
|
196
208
|
it "value completion with quotes" do
|
@@ -212,7 +224,7 @@ ScopedSearch::RSpec::Database.test_databases.each do |db|
|
|
212
224
|
# the string.
|
213
225
|
context 'dotted options in the completion list' do
|
214
226
|
it "query that starts with space should not include the dotted options" do
|
215
|
-
Foo.complete_for(' ').length.should ==
|
227
|
+
Foo.complete_for(' ').length.should == 10
|
216
228
|
end
|
217
229
|
|
218
230
|
it "query that starts with the dotted string should include the dotted options" do
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
# These specs will run on all databases that are defined in the spec/database.yml file.
|
5
|
+
# Comment out any databases that you do not have available for testing purposes if needed.
|
6
|
+
ScopedSearch::RSpec::Database.test_databases.each do |db|
|
7
|
+
describe ScopedSearch, "using a #{db} database" do
|
8
|
+
before(:all) do
|
9
|
+
ScopedSearch::RSpec::Database.establish_named_connection(db)
|
10
|
+
|
11
|
+
columns = on_postgresql? ? { :uuid => :uuid } : { :uuid => :string }
|
12
|
+
|
13
|
+
@class = ScopedSearch::RSpec::Database.create_model(columns.merge(:string => :string)) do |klass|
|
14
|
+
klass.scoped_search :on => :uuid
|
15
|
+
klass.scoped_search :on => :string
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:all) do
|
20
|
+
ScopedSearch::RSpec::Database.drop_model(@class)
|
21
|
+
ScopedSearch::RSpec::Database.close_connection
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'querying boolean fields' do
|
25
|
+
|
26
|
+
before(:all) do
|
27
|
+
@record1 = @class.create!(:uuid => SecureRandom.uuid)
|
28
|
+
@record2 = @class.create!(:uuid => SecureRandom.uuid)
|
29
|
+
@record3 = @class.create!(:uuid => SecureRandom.uuid)
|
30
|
+
end
|
31
|
+
|
32
|
+
after(:all) do
|
33
|
+
@record1.destroy
|
34
|
+
@record2.destroy
|
35
|
+
@record3.destroy
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should find the first record" do
|
39
|
+
@class.search_for("uuid = #{@record1.uuid}").length.should == 1
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should find two records with negative match" do
|
43
|
+
@class.search_for("uuid != #{@record3.uuid}").length.should == 2
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should find a record by just specifying the uuid" do
|
47
|
+
@class.search_for(@record1.uuid).first.uuid.should == @record1.uuid
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should not find a record if the uuid is not a valid uuid" do
|
51
|
+
if on_postgresql?
|
52
|
+
@class.search_for(@record1.uuid[0..-2]).length.should == 0
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -10,6 +10,9 @@ require "#{File.dirname(__FILE__)}/lib/matchers"
|
|
10
10
|
require "#{File.dirname(__FILE__)}/lib/database"
|
11
11
|
require "#{File.dirname(__FILE__)}/lib/mocks"
|
12
12
|
|
13
|
+
def on_postgresql?
|
14
|
+
ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
15
|
+
end
|
13
16
|
|
14
17
|
RSpec.configure do |config|
|
15
18
|
config.include ScopedSearch::RSpec::Mocks
|
@@ -9,6 +9,8 @@ describe ScopedSearch::AutoCompleteBuilder do
|
|
9
9
|
@definition.stub(:klass).and_return(klass)
|
10
10
|
@definition.stub(:profile).and_return(:default)
|
11
11
|
@definition.stub(:profile=).and_return(true)
|
12
|
+
@definition.klass.stub(:connection).and_return(double())
|
13
|
+
@definition.stub(:default_order).and_return(nil)
|
12
14
|
end
|
13
15
|
|
14
16
|
it "should return empty suggestions if the search query is nil" do
|
@@ -19,6 +21,19 @@ describe ScopedSearch::AutoCompleteBuilder do
|
|
19
21
|
ScopedSearch::AutoCompleteBuilder.auto_complete(@definition, "").should == []
|
20
22
|
end
|
21
23
|
|
24
|
+
it 'should suggest special values' do
|
25
|
+
field = double('field')
|
26
|
+
[:temporal?, :set?, :key_field, :validator, :virtual?, :relation, :offset, :value_translation, :to_sql].each { |key| field.stub(key) }
|
27
|
+
field.stub(:special_values).and_return %w(foo bar baz)
|
28
|
+
field.stub(:complete_value).and_return(true)
|
29
|
+
@definition.stub(:default_fields_for).and_return([])
|
30
|
+
@definition.stub(:field_by_name).and_return(field)
|
31
|
+
@definition.stub(:fields).and_return [field]
|
32
|
+
ScopedSearch::AutoCompleteBuilder.any_instance.stub(:complete_value_from_db).and_return([])
|
33
|
+
ScopedSearch::AutoCompleteBuilder.auto_complete(@definition, "custom_field =").should eq(['custom_field = foo', 'custom_field = bar', 'custom_field = baz'])
|
34
|
+
ScopedSearch::AutoCompleteBuilder.auto_complete(@definition, "custom_field = f").should eq(['custom_field = foo'])
|
35
|
+
end
|
36
|
+
|
22
37
|
context "with ext_method" do
|
23
38
|
before do
|
24
39
|
@definition = ScopedSearch::Definition.new(klass)
|
@@ -42,6 +42,7 @@ describe ScopedSearch::QueryBuilder do
|
|
42
42
|
field.stub(:only_explicit).and_return(true)
|
43
43
|
field.stub(:field).and_return(:test_field)
|
44
44
|
field.stub(:validator).and_return(->(_value) { false })
|
45
|
+
field.stub(:special_values).and_return([])
|
45
46
|
|
46
47
|
@definition.stub(:field_by_name).and_return(field)
|
47
48
|
|
@@ -58,6 +59,8 @@ describe ScopedSearch::QueryBuilder do
|
|
58
59
|
field.stub(:set?).and_return(false)
|
59
60
|
field.stub(:to_sql).and_return('')
|
60
61
|
field.stub(:validator).and_return(->(value) { value =~ /^\d+$/ })
|
62
|
+
field.stub(:value_translation).and_return(nil)
|
63
|
+
field.stub(:special_values).and_return([])
|
61
64
|
|
62
65
|
@definition.stub(:field_by_name).and_return(field)
|
63
66
|
|
@@ -71,12 +74,48 @@ describe ScopedSearch::QueryBuilder do
|
|
71
74
|
field.stub(:only_explicit).and_return(true)
|
72
75
|
field.stub(:field).and_return(:test_field)
|
73
76
|
field.stub(:validator).and_return(->(_value) { raise ScopedSearch::QueryNotSupported, 'my custom message' })
|
77
|
+
field.stub(:special_values).and_return([])
|
74
78
|
|
75
79
|
@definition.stub(:field_by_name).and_return(field)
|
76
80
|
|
77
81
|
lambda { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field = test_val') }.should raise_error('my custom message')
|
78
82
|
end
|
79
83
|
|
84
|
+
context 'with value_translation' do
|
85
|
+
let(:translator) do
|
86
|
+
->(value) do
|
87
|
+
if %w(a b c).include?(value)
|
88
|
+
'good'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
let(:special_values) { %w(a b c) }
|
93
|
+
before do
|
94
|
+
field = double('field')
|
95
|
+
field.stub(:field).and_return(:test_field)
|
96
|
+
field.stub(:key_field).and_return(nil)
|
97
|
+
field.stub(:to_sql).and_return('test_field')
|
98
|
+
[:virtual?, :set?, :temporal?, :relation, :offset].each { |key| field.stub(key).and_return(false) }
|
99
|
+
field.stub(:validator).and_return(->(value) { value == 'x' }) # Nothing except for special_values and x is valid
|
100
|
+
field.stub(:special_values).and_return(special_values)
|
101
|
+
field.stub(:value_translation).and_return(translator)
|
102
|
+
@definition.stub(:field_by_name).and_return(field)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should translate the value' do
|
106
|
+
ScopedSearch::QueryBuilder.build_query(@definition, 'test_field = a').should eq(conditions: ['(test_field = ?)', 'good'])
|
107
|
+
ScopedSearch::QueryBuilder.build_query(@definition, 'test_field ^ (a, b, c)').should eq(conditions: ['(test_field IN (?,?,?))', 'good', 'good', 'good'])
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'should validate before translation' do
|
111
|
+
proc { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field = d') }.should raise_error(ScopedSearch::QueryNotSupported, /Value 'd' is not valid for field/)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should raise an error if translated value is nil' do
|
115
|
+
proc { ScopedSearch::QueryBuilder.build_query(@definition, 'test_field = x') }.should raise_error(ScopedSearch::QueryNotSupported, /Translation from any value to nil is not allowed/)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
80
119
|
context "with ext_method" do
|
81
120
|
before do
|
82
121
|
@definition = ScopedSearch::Definition.new(klass)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scoped_search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.1.
|
4
|
+
version: 4.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amos Benari
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2018-
|
13
|
+
date: 2018-11-21 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- spec/integration/set_query_spec.rb
|
121
121
|
- spec/integration/sti_querying_spec.rb
|
122
122
|
- spec/integration/string_querying_spec.rb
|
123
|
+
- spec/integration/uuid_query_spec.rb
|
123
124
|
- spec/lib/database.rb
|
124
125
|
- spec/lib/matchers.rb
|
125
126
|
- spec/lib/mocks.rb
|
@@ -157,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
158
|
version: '0'
|
158
159
|
requirements: []
|
159
160
|
rubyforge_project:
|
160
|
-
rubygems_version: 2.6.
|
161
|
+
rubygems_version: 2.6.8
|
161
162
|
signing_key:
|
162
163
|
specification_version: 4
|
163
164
|
summary: Easily search you ActiveRecord models with a simple query language using
|
@@ -177,6 +178,7 @@ test_files:
|
|
177
178
|
- spec/integration/set_query_spec.rb
|
178
179
|
- spec/integration/sti_querying_spec.rb
|
179
180
|
- spec/integration/string_querying_spec.rb
|
181
|
+
- spec/integration/uuid_query_spec.rb
|
180
182
|
- spec/lib/database.rb
|
181
183
|
- spec/lib/matchers.rb
|
182
184
|
- spec/lib/mocks.rb
|