groovy 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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: {}
|