groovy 0.1.3 → 0.1.4
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/.gitignore +2 -0
- data/example/Gemfile.lock +1 -1
- data/example/basic.rb +3 -0
- data/example/relations.rb +4 -0
- data/groovy.gemspec +4 -1
- data/lib/groovy.rb +22 -3
- data/lib/groovy/model.rb +38 -9
- data/lib/groovy/query.rb +41 -20
- data/lib/groovy/version.rb +1 -1
- data/spec/groovy_spec.rb +66 -61
- data/spec/model_spec.rb +104 -0
- data/spec/query_spec.rb +204 -0
- data/spec/spec_helper.rb +29 -0
- metadata +43 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e12ec33808ce635a4cc1d18432daab18f075fdd6112d41be94c4b3d81039cfb9
|
4
|
+
data.tar.gz: 72fc2666d2c87469399e7c3297fa69a613e194f95ba22a51f1f4306a7123b36d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31599509b743b69e7b0da3f1348da0bbd029b81003685bddc3ce6b95100e8c11f7ad2524ba7b3600925cbd50b5468bae09eba0126d5e8b0072dc032e990be9f4
|
7
|
+
data.tar.gz: 0f73afaab9eb391731d864aaace675755c713ef32bfa2c045bc1760d5eda25cd67d3f2b395562d31a5bbf7affff0e50ec9c5d923a8f222516a21dec4dfef34f6
|
data/.gitignore
CHANGED
data/example/Gemfile.lock
CHANGED
data/example/basic.rb
CHANGED
data/example/relations.rb
CHANGED
@@ -23,6 +23,8 @@ class Category
|
|
23
23
|
t.string :name
|
24
24
|
t.timestamps
|
25
25
|
end
|
26
|
+
|
27
|
+
scope :named, -> (name) { where(name: name) if name }
|
26
28
|
end
|
27
29
|
|
28
30
|
class Place
|
@@ -34,6 +36,8 @@ class Place
|
|
34
36
|
t.string :description, index: true
|
35
37
|
t.timestamps
|
36
38
|
end
|
39
|
+
|
40
|
+
scope :recent, -> { sort_by(created_at: :desc) }
|
37
41
|
end
|
38
42
|
|
39
43
|
class Location
|
data/groovy.gemspec
CHANGED
@@ -14,9 +14,12 @@ Gem::Specification.new do |s|
|
|
14
14
|
# s.required_rubygems_version = ">= 1.3.6"
|
15
15
|
# s.rubyforge_project = "groovy"
|
16
16
|
|
17
|
-
s.add_development_dependency "bundler", ">= 1.0.0"
|
18
17
|
s.add_runtime_dependency "rroonga", "~> 7.1"
|
19
18
|
|
19
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
20
|
+
s.add_development_dependency "rspec", '~> 3.0', '>= 3.0.0'
|
21
|
+
s.add_development_dependency "fuubar", '>= 2.3.2'
|
22
|
+
|
20
23
|
s.files = `git ls-files`.split("\n")
|
21
24
|
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
22
25
|
s.require_path = 'lib'
|
data/lib/groovy.rb
CHANGED
@@ -3,14 +3,18 @@ require File.expand_path(File.dirname(__FILE__)) + '/groovy/model'
|
|
3
3
|
|
4
4
|
module Groovy
|
5
5
|
|
6
|
+
class Error < StandardError; end
|
7
|
+
class ContextNotFound < Error; end
|
8
|
+
class ContextAlreadyClosed < Error; end
|
9
|
+
|
6
10
|
class << self
|
7
11
|
|
8
12
|
def contexts
|
9
13
|
@contexts ||= {}
|
10
14
|
end
|
11
15
|
|
12
|
-
def [](
|
13
|
-
contexts[
|
16
|
+
def [](name)
|
17
|
+
contexts[name.to_sym]
|
14
18
|
end
|
15
19
|
|
16
20
|
def first_context_name
|
@@ -18,7 +22,14 @@ module Groovy
|
|
18
22
|
end
|
19
23
|
|
20
24
|
def open(db_path, name = :default, opts = {})
|
21
|
-
|
25
|
+
unless db_path.is_a?(String)
|
26
|
+
raise ArgumentError, "Invalid db_path: #{db_path}"
|
27
|
+
end
|
28
|
+
|
29
|
+
if contexts[name.to_sym]
|
30
|
+
raise ArgumentError, "Context already defined: #{name}"
|
31
|
+
end
|
32
|
+
|
22
33
|
contexts[name.to_sym] = if name == :default
|
23
34
|
Groonga::Context.default.tap { |ctx| open_or_create_db(ctx, db_path) }
|
24
35
|
else
|
@@ -26,6 +37,14 @@ module Groovy
|
|
26
37
|
end
|
27
38
|
end
|
28
39
|
|
40
|
+
def close(name = :default)
|
41
|
+
ctx = contexts[name.to_sym] or raise ContextNotFound.new(name)
|
42
|
+
contexts.delete(name.to_sym)
|
43
|
+
ctx.close
|
44
|
+
rescue Groonga::Closed => e
|
45
|
+
raise ContextAlreadyClosed
|
46
|
+
end
|
47
|
+
|
29
48
|
def logger=(obj)
|
30
49
|
@logger = obj
|
31
50
|
end
|
data/lib/groovy/model.rb
CHANGED
@@ -39,6 +39,7 @@ module Groovy
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def table
|
42
|
+
# raise "Table name not set: #{table_name}" if table_name.nil?
|
42
43
|
db_context[table_name]
|
43
44
|
end
|
44
45
|
|
@@ -47,7 +48,11 @@ module Groovy
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def schema(options = {}, &block)
|
50
|
-
@schema ||=
|
51
|
+
@schema ||= load_schema(options, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def load_schema(options = {}, &block)
|
55
|
+
@schema = begin
|
51
56
|
self.context_name = options[:context] || Groovy.first_context_name
|
52
57
|
self.table_name = options[:table_name] if options[:table_name]
|
53
58
|
|
@@ -66,7 +71,8 @@ module Groovy
|
|
66
71
|
def scope(name, obj)
|
67
72
|
define_singleton_method(name) do |*args|
|
68
73
|
res = obj.respond_to?(:call) ? obj.call(*args) : obj
|
69
|
-
self
|
74
|
+
# res = obj.respond_to?(:call) ? self.instance_exec(*args) { |a| obj.call(*a) } : obj
|
75
|
+
self # return self instead of query, so we can call other scopes
|
70
76
|
end
|
71
77
|
end
|
72
78
|
|
@@ -75,6 +81,19 @@ module Groovy
|
|
75
81
|
new(record.attributes, record)
|
76
82
|
end
|
77
83
|
|
84
|
+
def find_and_init_record(id)
|
85
|
+
if found = table[id]
|
86
|
+
new_from_record(found)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def find_records(&block)
|
91
|
+
records = table.select(&block)
|
92
|
+
records.map do|r|
|
93
|
+
find_and_init_record(r.attributes['_key']['_id'])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
78
97
|
def find(id)
|
79
98
|
if record = table[id] and record.id
|
80
99
|
new_from_record(record)
|
@@ -109,9 +128,10 @@ module Groovy
|
|
109
128
|
# table.select("#{col}:#{q}", operator: Groonga::Operation::SIMILAR)
|
110
129
|
end
|
111
130
|
|
112
|
-
def unique_values_for(column, limit: -1)
|
131
|
+
def unique_values_for(column, limit: -1, cache: false)
|
113
132
|
cols = [column]
|
114
133
|
opts = { drill_down: cols, drill_down_limit: limit, drilldown_sortby: '_key' }
|
134
|
+
opts[:cache] = cache ? 'yes' : 'no'
|
115
135
|
resp = table.context.select(table_name, opts) # drill_down_output_columns: cols
|
116
136
|
arr = resp.body[1] # or resp.drilldowns
|
117
137
|
arr.slice(2..-1) # .flatten
|
@@ -143,11 +163,11 @@ module Groovy
|
|
143
163
|
where(params).limit(1).first
|
144
164
|
end
|
145
165
|
|
146
|
-
def in_batches(of: 1000, from: nil, &block)
|
147
|
-
|
148
|
-
end
|
166
|
+
# def in_batches(of: 1000, from: nil, &block)
|
167
|
+
# all.in_batches(of: of, from: from, &block)
|
168
|
+
# end
|
149
169
|
|
150
|
-
[:search, :where, :not, :sort_by, :limit, :offset].each do |scope_method|
|
170
|
+
[:search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
|
151
171
|
define_method scope_method do |*args|
|
152
172
|
query.public_send(scope_method, *args)
|
153
173
|
end
|
@@ -179,7 +199,7 @@ module Groovy
|
|
179
199
|
private
|
180
200
|
|
181
201
|
def db_context
|
182
|
-
Groovy.contexts[context_name] or raise "Context not defined: #{context_name}"
|
202
|
+
Groovy.contexts[context_name.to_sym] or raise "Context not defined: #{context_name}"
|
183
203
|
end
|
184
204
|
|
185
205
|
def add_attr_accessors(col)
|
@@ -269,11 +289,16 @@ module Groovy
|
|
269
289
|
|
270
290
|
def update_attributes(obj)
|
271
291
|
set_attributes(obj)
|
292
|
+
return false if respond_to?(:invalid?) and invalid?
|
272
293
|
update
|
273
294
|
end
|
274
295
|
|
296
|
+
def update_attributes!(obj)
|
297
|
+
update_attributes or raise "Invalid!"
|
298
|
+
end
|
299
|
+
|
275
300
|
def save(options = {})
|
276
|
-
return false if respond_to?(:invalid) and invalid?
|
301
|
+
return false if respond_to?(:invalid?) and invalid?
|
277
302
|
new_record? ? create : update
|
278
303
|
end
|
279
304
|
|
@@ -304,6 +329,10 @@ module Groovy
|
|
304
329
|
attributes
|
305
330
|
end
|
306
331
|
|
332
|
+
def ==(other)
|
333
|
+
self.id == other.id
|
334
|
+
end
|
335
|
+
|
307
336
|
private
|
308
337
|
|
309
338
|
# def _key
|
data/lib/groovy/query.rb
CHANGED
@@ -17,9 +17,12 @@ module Groovy
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def merge_with!(another)
|
20
|
+
# puts "merging #{parameters.inspect} with #{another.parameters.inspect}"
|
20
21
|
# parameters.merge!(another.parameters)
|
21
|
-
parameters
|
22
|
-
sorting
|
22
|
+
@parameters = another.parameters
|
23
|
+
@sorting = another.sorting
|
24
|
+
# parameters.concat(another.parameters)
|
25
|
+
# sorting.merge!(another.sorting)
|
23
26
|
self
|
24
27
|
end
|
25
28
|
|
@@ -42,21 +45,25 @@ module Groovy
|
|
42
45
|
def where(conditions = nil)
|
43
46
|
case conditions
|
44
47
|
when String # "foo:bar"
|
45
|
-
|
48
|
+
add_param(AND + "(#{map_operator(conditions)})")
|
46
49
|
when Hash # { foo: 'bar' } or { views: 1..100 }
|
47
50
|
conditions.each do |key, val|
|
48
51
|
if val.is_a?(Range)
|
49
|
-
|
50
|
-
|
52
|
+
add_param(AND + [key, val.min].join(':>=')) if val.min # lte
|
53
|
+
add_param(AND + [key, val.max].join(':<=')) if val.max # gte
|
54
|
+
|
51
55
|
elsif val.is_a?(Regexp)
|
52
|
-
str = val.source.gsub(/[^a-
|
53
|
-
param = val.source[0] == '^' ? ':^' : ':~' # starts with or regexp
|
54
|
-
|
56
|
+
str = val.source.gsub(/[^a-zA-Z0-9\s_\.,-]/, '')
|
57
|
+
param = val.source[0] == '^' ? ':^' : val.source[-1] == '$' ? ':$' : ':~' # starts with or regexp
|
58
|
+
add_param(AND + [key, str.downcase].join(param)) # regex must be downcase
|
59
|
+
|
55
60
|
elsif val.is_a?(Array) # { foo: [1,2,3] }
|
56
61
|
str = "#{key}:#{val.join(" OR #{key}:")}"
|
57
|
-
|
62
|
+
add_param(AND + str)
|
63
|
+
|
58
64
|
else
|
59
|
-
|
65
|
+
str = val.nil? || val.to_s.strip == '' ? '\\' : val.to_s
|
66
|
+
add_param(AND + [key, str].join(':'))
|
60
67
|
end
|
61
68
|
end
|
62
69
|
# when Array # ["foo:?", val]
|
@@ -76,13 +83,21 @@ module Groovy
|
|
76
83
|
when Hash # { foo: 'bar' }
|
77
84
|
conditions.each do |key, val|
|
78
85
|
if val.is_a?(Range)
|
79
|
-
|
80
|
-
|
86
|
+
add_param(AND + [key, val.min].join(':<=')) if val.min > 0 # gte
|
87
|
+
add_param(AND + [key, val.max].join(':>=')) if val.max # lte, nil if range.max is -1
|
88
|
+
|
89
|
+
elsif val.is_a?(Regexp)
|
90
|
+
str = val.source.gsub(/[^a-zA-Z0-9\s_\.,-]/, '')
|
91
|
+
param = val.source[0] == '^' ? ':^' : val.source[-1] == '$' ? ':$' : ':~' # starts with or regexp
|
92
|
+
add_param(NOT + [key, str.downcase].join(param)) # regex must be downcase
|
93
|
+
|
81
94
|
elsif val.is_a?(Array) # { foo: [1,2,3] }
|
82
|
-
str = "#{key}:!#{val.join("
|
83
|
-
|
95
|
+
str = "#{key}:!#{val.join(" #{AND}#{key}:!")}"
|
96
|
+
add_param(AND + str)
|
97
|
+
|
84
98
|
else
|
85
|
-
|
99
|
+
str = val.nil? || val.to_s.strip == '' ? '\\' : val.to_s
|
100
|
+
add_param(AND + [key, str].join(':!')) # not
|
86
101
|
end
|
87
102
|
end
|
88
103
|
# when Array # ["foo:?", val]
|
@@ -171,11 +186,10 @@ module Groovy
|
|
171
186
|
end
|
172
187
|
|
173
188
|
def records
|
174
|
-
@records ||= results.map do |r|
|
189
|
+
@records ||= results.map do |r|
|
175
190
|
# FIXME: figure out the right way to do this.
|
176
|
-
# model.new(r.value.attributes['_key'], r)
|
177
191
|
id = r.attributes['_value']['_key']['_id']
|
178
|
-
model.
|
192
|
+
model.find_and_init_record(id)
|
179
193
|
end
|
180
194
|
end
|
181
195
|
|
@@ -186,6 +200,11 @@ module Groovy
|
|
186
200
|
model.clear_query # sets @query to nil
|
187
201
|
end
|
188
202
|
|
203
|
+
def add_param(param)
|
204
|
+
raise "Duplicate param: #{param}" if parameters.include?(param)
|
205
|
+
parameters.push(param)
|
206
|
+
end
|
207
|
+
|
189
208
|
def results
|
190
209
|
@results ||= execute
|
191
210
|
rescue Groonga::TooLargeOffset
|
@@ -198,12 +217,14 @@ module Groovy
|
|
198
217
|
def execute
|
199
218
|
set = if parameters.any?
|
200
219
|
query = prepare_query
|
201
|
-
puts query if ENV['DEBUG']
|
220
|
+
puts "Finding records with query: #{query}" if ENV['DEBUG']
|
202
221
|
table.select(query, options)
|
203
222
|
else
|
204
223
|
table.select(options)
|
205
224
|
end
|
206
225
|
|
226
|
+
puts set.expression.inspect
|
227
|
+
|
207
228
|
@total_entries = set.size
|
208
229
|
|
209
230
|
puts "Sorting with #{sort_key_and_order}, #{sorting.inspect}" if ENV['DEBUG']
|
@@ -232,7 +253,7 @@ module Groovy
|
|
232
253
|
def prepare_query
|
233
254
|
query = parameters.join(" ").split(/ or /i).map do |part|
|
234
255
|
part.gsub(/\s(\w)/, '\ \1')
|
235
|
-
end.join(' OR ')
|
256
|
+
end.join(' OR ').sub(/^-/, '_id:>0 -') #.gsub(' OR -', ' -')
|
236
257
|
end
|
237
258
|
|
238
259
|
def method_missing(name, *args)
|
data/lib/groovy/version.rb
CHANGED
data/spec/groovy_spec.rb
CHANGED
@@ -1,61 +1,66 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
1
|
+
require_relative './spec_helper'
|
2
|
+
|
3
|
+
describe Groovy do
|
4
|
+
before :each do
|
5
|
+
Groonga::Context.default = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.open' do
|
9
|
+
it 'explodes if called with no params' do
|
10
|
+
expect do
|
11
|
+
Groovy.open
|
12
|
+
end.to raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'explodes if called with nil db_path' do
|
16
|
+
expect do
|
17
|
+
Groovy.open(nil)
|
18
|
+
end.to raise_error(ArgumentError)
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'if no context given' do
|
22
|
+
it 'sets up database for default context' do
|
23
|
+
expect(Groonga::Context.default.database).to be_nil
|
24
|
+
Groovy.open('tmp/foo1')
|
25
|
+
expect(Groonga::Context.default.database).to be_a(Groonga::Database)
|
26
|
+
Groovy.close # default
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'if context given' do
|
31
|
+
it 'sets up database for default context' do
|
32
|
+
expect(Groonga::Context.default.database).to be_nil
|
33
|
+
Groovy.open('tmp/foo2', :test)
|
34
|
+
expect(Groonga::Context.default.database).to be_nil
|
35
|
+
|
36
|
+
expect(Groovy['test']).to be_a(Groonga::Context)
|
37
|
+
expect(Groovy[:test].database).to be_a(Groonga::Database)
|
38
|
+
|
39
|
+
Groovy.close(:test)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '.close' do
|
45
|
+
it 'thows if not opened (default)' do
|
46
|
+
expect { Groovy.close }.to raise_error(Groovy::ContextNotFound)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'thows if context does not exist' do
|
50
|
+
expect { Groovy.close(:foo) }.to raise_error(Groovy::ContextNotFound)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'thows if already closed' do
|
54
|
+
Groovy.open('tmp/foo')
|
55
|
+
Groovy.close
|
56
|
+
expect do
|
57
|
+
Groovy.close
|
58
|
+
end.to raise_error(Groovy::ContextNotFound)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'works if ctx exists and open' do
|
62
|
+
Groovy.open('tmp/foo', :test)
|
63
|
+
expect(Groovy.close(:test)).to eq(nil)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
|
3
|
+
describe Groovy::Model do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
Groovy.open('tmp/model', 'model_spec')
|
7
|
+
load_schema! 'model_spec'
|
8
|
+
end
|
9
|
+
|
10
|
+
after :all do
|
11
|
+
Groovy.close('model_spec')
|
12
|
+
end
|
13
|
+
|
14
|
+
after :each do
|
15
|
+
TestProduct.clear_query # otherwise next test will fail
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.schema' do
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.scope' do
|
22
|
+
|
23
|
+
before :all do
|
24
|
+
TestProduct.class_eval do
|
25
|
+
scope :with_name, -> (name) { where(name: name) if name }
|
26
|
+
scope :by_price_asc, -> { sort_by(price: :asc) }
|
27
|
+
scope :cheapest, -> { by_price_asc }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'does not perform query by default' do
|
32
|
+
expect(TestProduct.table).not_to receive(:select)
|
33
|
+
TestProduct.with_name('foo')
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'chaining' do
|
37
|
+
|
38
|
+
it 'allow chaining (even if methods dont return the query)' do
|
39
|
+
expect do
|
40
|
+
TestProduct.with_name('foo').by_price_asc.cheapest
|
41
|
+
end.not_to raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does not duplicate params' do
|
45
|
+
res = TestProduct.with_name('foo').by_price_asc.cheapest
|
46
|
+
res.query
|
47
|
+
expect(res.query.parameters).to eq(["+name:foo"])
|
48
|
+
expect(res.query.sorting).to eq(:by=>[{:key=>"price", :order=>:asc}], :limit=>-1, :offset=>0)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '.create' do
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '.find' do
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '.find_by' do
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '.delete_all' do
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#[]' do
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#[]=' do
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#increment' do
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#dirty' do
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#update_attributes' do
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#save' do
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#delete' do
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#reload' do
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'attributes accessors' do
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'singular refs' do
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'plural refs' do
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
data/spec/query_spec.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
|
3
|
+
describe Groovy::Query do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
Groovy.open('tmp/querying', 'query_spec')
|
7
|
+
load_schema! 'query_spec'
|
8
|
+
@p1 = TestProduct.create!(name: "Product 1", visible: true, price: 10, tag_list: 'one, number two, and three')
|
9
|
+
@p2 = TestProduct.create!(name: "Product 2", visible: false, price: 20, tag_list: 'number two, three')
|
10
|
+
@p3 = TestProduct.create!(name: "Product 3", visible: true, price: 30, tag_list: nil)
|
11
|
+
@p4 = TestProduct.create!(name: "Product 4", visible: false, price: 40, tag_list: 'one, number two')
|
12
|
+
@p5 = TestProduct.create!(name: "Product 5", visible: true, price: 50, tag_list: '')
|
13
|
+
end
|
14
|
+
|
15
|
+
after :all do
|
16
|
+
Groovy.close('query_spec')
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#where' do
|
20
|
+
describe 'nil value' do
|
21
|
+
it 'finds expected records' do
|
22
|
+
res = TestProduct.where(tag_list: nil)
|
23
|
+
expect(res.map(&:id)).to eq([@p3.id, @p5.id])
|
24
|
+
end
|
25
|
+
it 'works with other args too' do
|
26
|
+
res = TestProduct.where(name: 'Product 5').where(tag_list: nil)
|
27
|
+
expect(res.map(&:id)).to eq([@p5.id])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'empty string value' do
|
32
|
+
it 'finds expected records' do
|
33
|
+
res = TestProduct.where(tag_list: '')
|
34
|
+
expect(res.map(&:id)).to eq([@p3.id, @p5.id])
|
35
|
+
end
|
36
|
+
it 'works with other args too' do
|
37
|
+
res = TestProduct.where(name: 'Product 5').where(tag_list: '')
|
38
|
+
expect(res.map(&:id)).to eq([@p5.id])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'exact search' do
|
43
|
+
it 'finds expected records' do
|
44
|
+
res = TestProduct.where(name: 'Product 2')
|
45
|
+
expect(res.map(&:id)).to eq([@p2.id])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'basic regex' do
|
50
|
+
it 'finds expected records' do
|
51
|
+
res = TestProduct.where(tag_list: /two/)
|
52
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p4.id])
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'is not case-sensitive' do
|
56
|
+
res = TestProduct.where(tag_list: /TWO/)
|
57
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p4.id])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'starts with regex' do
|
62
|
+
it 'finds expected records' do
|
63
|
+
res = TestProduct.where(name: /^product/)
|
64
|
+
# expect(res.map(&:id)).to eq([])
|
65
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p3.id, @p4.id, @p5.id])
|
66
|
+
|
67
|
+
res = TestProduct.where(name: /^Product/)
|
68
|
+
# expect(res.map(&:id)).to eq([])
|
69
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p3.id, @p4.id, @p5.id])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'ends with regex' do
|
74
|
+
it 'finds expected records' do
|
75
|
+
res = TestProduct.where(tag_list: /three$/)
|
76
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id])
|
77
|
+
|
78
|
+
# res = TestProduct.where(name: /THREE$/)
|
79
|
+
# expect(res.map(&:id)).to eq([])
|
80
|
+
# expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p3.id])
|
81
|
+
# expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p3.id, @p4.id, @p5.id])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'regex with spaces' do
|
86
|
+
it 'finds expected records' do
|
87
|
+
res = TestProduct.where(tag_list: /number two/)
|
88
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p4.id])
|
89
|
+
end
|
90
|
+
|
91
|
+
xit 'handles escaped chars correctly' do
|
92
|
+
res = TestProduct.where(tag_list: /number\stwo/)
|
93
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p4.id])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'array value' do
|
98
|
+
it 'finds expected records' do
|
99
|
+
res = TestProduct.where(_id: [@p1.id, @p3.id, @p5.id])
|
100
|
+
expect(res.map(&:id)).to eq([@p1.id, @p3.id, @p5.id])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '#not' do
|
106
|
+
describe 'nil value' do
|
107
|
+
it 'finds expected records' do
|
108
|
+
res = TestProduct.where.not(tag_list: nil)
|
109
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p4.id])
|
110
|
+
end
|
111
|
+
it 'works with other args too' do
|
112
|
+
res = TestProduct.where.not(name: 'Product 2').where.not(tag_list: nil)
|
113
|
+
expect(res.map(&:id)).to eq([@p1.id, @p4.id])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'empty string value' do
|
118
|
+
it 'finds expected records' do
|
119
|
+
res = TestProduct.where.not(tag_list: '')
|
120
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p4.id])
|
121
|
+
end
|
122
|
+
it 'works with other args too' do
|
123
|
+
res = TestProduct.where.not(name: 'Product 2').where.not(tag_list: '')
|
124
|
+
expect(res.map(&:id)).to eq([@p1.id, @p4.id])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'exact search' do
|
129
|
+
it 'finds expected records' do
|
130
|
+
res = TestProduct.not(name: 'Product 2')
|
131
|
+
expect(res.map(&:id)).to eq([@p1.id, @p3.id, @p4.id, @p5.id])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'basic regex' do
|
136
|
+
it 'finds expected records' do
|
137
|
+
res = TestProduct.not(tag_list: /two/)
|
138
|
+
expect(res.map(&:id)).to eq([@p3.id, @p5.id])
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'is not case-sensitive' do
|
142
|
+
res = TestProduct.not(tag_list: /TWO/)
|
143
|
+
expect(res.map(&:id)).to eq([@p3.id, @p5.id])
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe 'starts with regex' do
|
148
|
+
it 'finds expected records' do
|
149
|
+
res = TestProduct.not(name: /^product/)
|
150
|
+
expect(res.map(&:id)).to eq([])
|
151
|
+
|
152
|
+
res = TestProduct.not(name: /^Product/)
|
153
|
+
expect(res.map(&:id)).to eq([])
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe 'ends with regex' do
|
158
|
+
it 'finds expected records' do
|
159
|
+
res = TestProduct.not(tag_list: /three$/)
|
160
|
+
expect(res.map(&:id)).to eq([@p3.id, @p4.id, @p5.id])
|
161
|
+
|
162
|
+
# res = TestProduct.where(name: /THREE$/)
|
163
|
+
# expect(res.map(&:id)).to eq([])
|
164
|
+
# expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p3.id])
|
165
|
+
# expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p3.id, @p4.id, @p5.id])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'regex with spaces' do
|
170
|
+
it 'finds expected records' do
|
171
|
+
res = TestProduct.not(tag_list: /number two/)
|
172
|
+
expect(res.map(&:id)).to eq([@p3.id, @p5.id])
|
173
|
+
end
|
174
|
+
|
175
|
+
xit 'handles escaped chars correctly' do
|
176
|
+
res = TestProduct.not(tag_list: /number\stwo/)
|
177
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p4.id])
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'array value' do
|
182
|
+
it 'finds expected records' do
|
183
|
+
res = TestProduct.not(_id: [@p1.id, @p3.id, @p5.id])
|
184
|
+
expect(res.map(&:id)).to eq([@p2.id, @p4.id])
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe '#limit' do
|
190
|
+
end
|
191
|
+
|
192
|
+
describe '#offset' do
|
193
|
+
end
|
194
|
+
|
195
|
+
describe '#in_batches' do
|
196
|
+
end
|
197
|
+
|
198
|
+
describe '#paginate' do
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#sort_by' do
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require(:default, :test)
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require './lib/groovy'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.before(:each) do
|
9
|
+
# Groonga::Context.default = nil
|
10
|
+
end
|
11
|
+
config.after(:suite) do
|
12
|
+
FileUtils.rm_rf('tmp')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TestProduct
|
17
|
+
include Groovy::Model
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_schema!(context_name)
|
21
|
+
TestProduct.load_schema context: context_name do |t|
|
22
|
+
t.boolean :visible
|
23
|
+
t.string :name
|
24
|
+
t.integer :price
|
25
|
+
t.string :tag_list
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# load_schema!
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: groovy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomás Pollak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rroonga
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.1'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -25,19 +39,39 @@ dependencies:
|
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: 1.0.0
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
42
|
+
name: rspec
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
|
47
|
+
version: '3.0'
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 3.0.0
|
51
|
+
type: :development
|
35
52
|
prerelease: false
|
36
53
|
version_requirements: !ruby/object:Gem::Requirement
|
37
54
|
requirements:
|
38
55
|
- - "~>"
|
39
56
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
57
|
+
version: '3.0'
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 3.0.0
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: fuubar
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 2.3.2
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 2.3.2
|
41
75
|
description: Allows using Groonga in your models a-la ActiveRecord.
|
42
76
|
email:
|
43
77
|
- tomas@forkhq.com
|
@@ -63,6 +97,9 @@ files:
|
|
63
97
|
- lib/groovy/vector.rb
|
64
98
|
- lib/groovy/version.rb
|
65
99
|
- spec/groovy_spec.rb
|
100
|
+
- spec/model_spec.rb
|
101
|
+
- spec/query_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
66
103
|
homepage: https://github.com/tomas/groovy
|
67
104
|
licenses: []
|
68
105
|
metadata: {}
|