scoped_search 4.1.5 → 4.1.6
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/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
|