groovy 0.4.0 → 0.4.5
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/example/basic.rb +3 -2
- data/example/relations.rb +1 -0
- data/lib/groovy.rb +8 -0
- data/lib/groovy/model.rb +21 -10
- data/lib/groovy/query.rb +22 -15
- data/lib/groovy/schema.rb +15 -4
- data/lib/groovy/version.rb +1 -1
- data/spec/model_spec.rb +9 -1
- data/spec/query_spec.rb +27 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2e7655a8fefa2f17295105acb6ba387f275eb158c0babed04675d566f736766
|
4
|
+
data.tar.gz: 5a9b94a767f58b033a11044d3b7992911a6cde0ad068955c76d85bdd2c0464b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c8813e0920559c6422f8129ca40e51c42a6db26efc65c9f2d9a1d47d8f5f6d5638f9677f970a38d36a180c93cf610b99b7adc372579eaff92ae4d8bd9aa41ae
|
7
|
+
data.tar.gz: d12ed329507b8a5c191308e198a5434f8226fd24bf4f25c8b2b8d64a832c05df39ff9946dddd53b7da5df11c2d14bc9ab97e4dcf84d67e60c9c2811d0c0122b6
|
data/example/basic.rb
CHANGED
@@ -9,6 +9,7 @@ class Product
|
|
9
9
|
schema do |t|
|
10
10
|
t.string :name
|
11
11
|
t.integer :price
|
12
|
+
t.boolean :published
|
12
13
|
t.timestamps
|
13
14
|
end
|
14
15
|
|
@@ -19,11 +20,11 @@ end
|
|
19
20
|
def populate
|
20
21
|
5_000.times do |i|
|
21
22
|
puts "Creating product #{i}" if i % 1000 == 0
|
22
|
-
Product.create!(name: "A product with index #{i}", price: 10000 + i % 10)
|
23
|
+
Product.create!(name: "A product with index #{i}", published: true, price: 10000 + i % 10)
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
|
-
populate if Product.count == 0
|
27
|
+
populate if Product.count == 0
|
27
28
|
|
28
29
|
# 50_000 products: 50M
|
29
30
|
# 100_000 products: 50M
|
data/example/relations.rb
CHANGED
data/lib/groovy.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
require 'groonga'
|
2
2
|
require File.expand_path(File.dirname(__FILE__)) + '/groovy/model'
|
3
3
|
|
4
|
+
# overwrite Groonga::Record#inspect because the #attributes part is
|
5
|
+
# making debugging take ages
|
6
|
+
class Groonga::Record
|
7
|
+
def inspect
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
4
12
|
module Groovy
|
5
13
|
|
6
14
|
class Error < StandardError; end
|
data/lib/groovy/model.rb
CHANGED
@@ -208,7 +208,7 @@ module Groovy
|
|
208
208
|
end
|
209
209
|
end
|
210
210
|
|
211
|
-
[:find_each, :find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
|
211
|
+
[:select, :find_each, :find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
|
212
212
|
define_method scope_method do |*args, &block|
|
213
213
|
query.public_send(scope_method, *args, &block)
|
214
214
|
end
|
@@ -221,6 +221,12 @@ module Groovy
|
|
221
221
|
set_timestamp(attributes, :created_at)
|
222
222
|
set_timestamp(attributes, :updated_at)
|
223
223
|
|
224
|
+
# remove nil attributes for integer columns, otherwise
|
225
|
+
# we get a TypeError (no implicit conversion from nil to integer)
|
226
|
+
attributes.each do |k, v|
|
227
|
+
attributes.delete(k) if v.nil? # && schema.integer_columns.include?(k)
|
228
|
+
end
|
229
|
+
|
224
230
|
if table.support_key?
|
225
231
|
raise "Key required" if key.nil?
|
226
232
|
table.add(key, attributes)
|
@@ -297,7 +303,7 @@ module Groovy
|
|
297
303
|
else
|
298
304
|
attrs ||= {}
|
299
305
|
unless attrs.is_a?(Hash)
|
300
|
-
raise ArgumentError.new("Attributes should be a Hash")
|
306
|
+
raise ArgumentError.new("Attributes should be a Hash, not a #{attrs.class}")
|
301
307
|
end
|
302
308
|
|
303
309
|
# don't call set_attributes since we don't want to call
|
@@ -309,6 +315,12 @@ module Groovy
|
|
309
315
|
@changes = {}
|
310
316
|
end
|
311
317
|
|
318
|
+
# get reference to the actual record in the Groonga table,
|
319
|
+
# not the temporary one we get as part of a search result.
|
320
|
+
def load_record
|
321
|
+
self.class.table[id]
|
322
|
+
end
|
323
|
+
|
312
324
|
def inspect
|
313
325
|
"#<#{self.class.name} id:#{id.inspect} attributes:[#{self.class.attribute_names.join(', ')}]>"
|
314
326
|
end
|
@@ -394,22 +406,21 @@ module Groovy
|
|
394
406
|
end
|
395
407
|
|
396
408
|
def ==(other)
|
397
|
-
self.id == other.id
|
409
|
+
self.class == other.class && self.id == other.id
|
398
410
|
end
|
399
411
|
|
400
412
|
def <=>(other)
|
401
|
-
self.id <=> other.id
|
413
|
+
self.class == other.class && self.id <=> other.id
|
402
414
|
end
|
403
415
|
|
404
416
|
private
|
405
417
|
|
406
418
|
def get_record_attribute(key)
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
end
|
419
|
+
val = record[key]
|
420
|
+
if self.class.schema.time_columns.include?(key)
|
421
|
+
fix_time_value(val)
|
422
|
+
else
|
423
|
+
val
|
413
424
|
end
|
414
425
|
end
|
415
426
|
|
data/lib/groovy/query.rb
CHANGED
@@ -6,6 +6,7 @@ module Groovy
|
|
6
6
|
AND = '+'.freeze
|
7
7
|
NOT = '-'.freeze
|
8
8
|
PER_PAGE = 50.freeze
|
9
|
+
# ESCAPE_CHARS_REGEX = /([\(\)\/\\])/.freeze
|
9
10
|
VALID_QUERY_CHARS = 'a-zA-Z0-9_\.,&-'.freeze
|
10
11
|
REMOVE_INVALID_CHARS_REGEX = Regexp.new('[^\s' + VALID_QUERY_CHARS + ']').freeze
|
11
12
|
|
@@ -47,6 +48,11 @@ module Groovy
|
|
47
48
|
self
|
48
49
|
end
|
49
50
|
|
51
|
+
def select(&block)
|
52
|
+
@select_block = block
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
50
56
|
def find(id)
|
51
57
|
find_by(_id: id)
|
52
58
|
end
|
@@ -76,7 +82,7 @@ module Groovy
|
|
76
82
|
add_param(AND + [key, val.max].join(':<=')) if val.max # gte
|
77
83
|
|
78
84
|
elsif val.is_a?(Regexp)
|
79
|
-
str = val.source.gsub(REMOVE_INVALID_CHARS_REGEX, '')
|
85
|
+
str = val.source.gsub('/', '_slash_').gsub('(', '_openp_').gsub(')', '_closep_').gsub(REMOVE_INVALID_CHARS_REGEX, '')
|
80
86
|
param = val.source[0] == '^' ? ':^' : val.source[-1] == '$' ? ':$' : ':~' # starts with or regexp
|
81
87
|
add_param(AND + [key, str.downcase].join(param)) # regex must be downcase
|
82
88
|
|
@@ -85,7 +91,7 @@ module Groovy
|
|
85
91
|
add_param(AND + str)
|
86
92
|
|
87
93
|
else
|
88
|
-
str = val.nil? || val
|
94
|
+
str = val.nil? || val === false || val.to_s.strip == '' ? "\"\"" : escape_val(val)
|
89
95
|
add_param(AND + [key, str].join(':'))
|
90
96
|
end
|
91
97
|
end
|
@@ -110,7 +116,7 @@ module Groovy
|
|
110
116
|
add_param(AND + [key, val.max].join(':>=')) if val.max # lte, nil if range.max is -1
|
111
117
|
|
112
118
|
elsif val.is_a?(Regexp)
|
113
|
-
str = val.source.gsub(REMOVE_INVALID_CHARS_REGEX, '')
|
119
|
+
str = val.source.gsub('/', '_slash_').gsub('(', '_openp_').gsub(')', '_closep_').gsub(REMOVE_INVALID_CHARS_REGEX, '')
|
114
120
|
param = val.source[0] == '^' ? ':^' : val.source[-1] == '$' ? ':$' : ':~' # starts with or regexp
|
115
121
|
add_param(NOT + [key, str.downcase].join(param)) # regex must be downcase
|
116
122
|
|
@@ -119,7 +125,7 @@ module Groovy
|
|
119
125
|
add_param(AND + str)
|
120
126
|
|
121
127
|
else
|
122
|
-
str = val.nil? || val
|
128
|
+
str = val.nil? || val === false || val.to_s.strip == '' ? "\"\"" : escape_val(val)
|
123
129
|
add_param(AND + [key, str].join(':!')) # not
|
124
130
|
end
|
125
131
|
end
|
@@ -141,10 +147,10 @@ module Groovy
|
|
141
147
|
self
|
142
148
|
end
|
143
149
|
|
144
|
-
def paginate(page = 1)
|
150
|
+
def paginate(page = 1, per_page: PER_PAGE)
|
145
151
|
page = 1 if page.to_i < 1
|
146
|
-
offset = ((page.to_i)-1) *
|
147
|
-
offset(offset).limit(
|
152
|
+
offset = ((page.to_i)-1) * per_page
|
153
|
+
offset(offset).limit(per_page) # returns self
|
148
154
|
end
|
149
155
|
|
150
156
|
# sort_by(title: :asc)
|
@@ -229,15 +235,12 @@ module Groovy
|
|
229
235
|
end
|
230
236
|
|
231
237
|
private
|
232
|
-
attr_reader :model, :table, :options
|
238
|
+
attr_reader :model, :table, :options, :select_block
|
233
239
|
|
234
240
|
def add_param(param)
|
235
|
-
if
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
# if param matches blank/nil, put at the end of the stack
|
240
|
-
param[/:\!?\\/] ? parameters.push(param) : parameters.unshift(param)
|
241
|
+
raise "Select block already given!" if select_block
|
242
|
+
raise "Duplicate param: #{param}" if parameters.include?(param)
|
243
|
+
parameters.push(param)
|
241
244
|
end
|
242
245
|
|
243
246
|
def results
|
@@ -248,7 +251,10 @@ module Groovy
|
|
248
251
|
end
|
249
252
|
|
250
253
|
def execute
|
251
|
-
set = if
|
254
|
+
set = if select_block
|
255
|
+
debug "Finding records with select block"
|
256
|
+
table.select { |record| select_block.call(record) }
|
257
|
+
elsif parameters.any?
|
252
258
|
query = prepare_query
|
253
259
|
debug "Finding records with query: #{query}"
|
254
260
|
table.select(query, options)
|
@@ -291,6 +297,7 @@ module Groovy
|
|
291
297
|
query = parameters.join(' ').split(/ or /i).map do |part|
|
292
298
|
part.gsub(' ', ' ') # replace double with single spaces
|
293
299
|
.gsub(space_regex, '\ \1') # escape spaces before word letters
|
300
|
+
.gsub('_slash_', '\/').gsub('_openp_', '\(').gsub('_closep_', '\)')
|
294
301
|
.gsub(/(\d\d):(\d\d):(\d\d)/, '\1\:\2\:\3') # escape hh:mm:ss in timestamps
|
295
302
|
end.join(' OR ').sub(/^-/, '_id:>0 -') #.gsub(' OR -', ' -')
|
296
303
|
end
|
data/lib/groovy/schema.rb
CHANGED
@@ -56,14 +56,25 @@ module Groovy
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def time_columns
|
59
|
-
|
60
|
-
get_names(table.columns.select { |c| c.column? && c.range.name == 'Time' })
|
59
|
+
columns_by_type('Time')
|
61
60
|
end
|
62
61
|
|
63
|
-
def
|
64
|
-
|
62
|
+
def integer_columns
|
63
|
+
columns_by_type('Int32')
|
65
64
|
end
|
66
65
|
|
66
|
+
def boolean_columns
|
67
|
+
columns_by_type('Bool')
|
68
|
+
end
|
69
|
+
|
70
|
+
def columns_by_type(type)
|
71
|
+
get_names(table.columns.select { |c| c.column? && c.range.name == type })
|
72
|
+
end
|
73
|
+
|
74
|
+
# def time_column?(name)
|
75
|
+
# time_columns.include?(name)
|
76
|
+
# end
|
77
|
+
|
67
78
|
def rebuild!
|
68
79
|
log("Rebuilding!")
|
69
80
|
# remove_table! if table
|
data/lib/groovy/version.rb
CHANGED
data/spec/model_spec.rb
CHANGED
@@ -17,7 +17,7 @@ describe Groovy::Model do
|
|
17
17
|
describe '.scope' do
|
18
18
|
|
19
19
|
before :all do
|
20
|
-
TestProduct.class_eval do
|
20
|
+
TestProduct.class_eval do
|
21
21
|
scope :with_name, -> (name) { where(name: name) if name }
|
22
22
|
scope :by_price_asc, -> { sort_by(price: :asc) }
|
23
23
|
scope :cheapest, -> { by_price_asc }
|
@@ -49,6 +49,13 @@ describe Groovy::Model do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
describe '.create' do
|
52
|
+
|
53
|
+
it 'does not explode when inserting nil values for columns' do
|
54
|
+
expect do
|
55
|
+
TestProduct.create({ price: nil })
|
56
|
+
end.not_to raise_error
|
57
|
+
end
|
58
|
+
|
52
59
|
end
|
53
60
|
|
54
61
|
describe '.find' do
|
@@ -60,6 +67,7 @@ describe Groovy::Model do
|
|
60
67
|
describe '.delete_all' do
|
61
68
|
|
62
69
|
before do
|
70
|
+
TestProduct.delete_all
|
63
71
|
@first = TestProduct.create!(name: 'A product', price: 100)
|
64
72
|
@second = TestProduct.create!(name: 'Another product', price: 200)
|
65
73
|
expect(TestProduct.count).to eq(2)
|
data/spec/query_spec.rb
CHANGED
@@ -8,7 +8,7 @@ describe Groovy::Query do
|
|
8
8
|
@p1 = TestProduct.create!(name: "Product 1", visible: true, price: 10, tag_list: 'one, number two & three')
|
9
9
|
@p2 = TestProduct.create!(name: "Product 2", visible: false, price: 20, tag_list: 'number two, three')
|
10
10
|
@p3 = TestProduct.create!(name: "Product 3: The Best", visible: true, price: 30, tag_list: nil)
|
11
|
-
@p4 = TestProduct.create!(name: "Product 4", visible: false, price: 40, tag_list: 'one, number two')
|
11
|
+
@p4 = TestProduct.create!(name: "Product 4", visible: false, price: 40, tag_list: 'one, number two / something')
|
12
12
|
@p5 = TestProduct.create!(name: "Product 5", visible: true, price: 50, tag_list: '')
|
13
13
|
end
|
14
14
|
|
@@ -40,6 +40,13 @@ describe Groovy::Query do
|
|
40
40
|
res = TestProduct.where(tag_list: nil)
|
41
41
|
expect(res.map(&:id)).to eq([@p3.id, @p5.id])
|
42
42
|
end
|
43
|
+
it 'works with other nil values too' do
|
44
|
+
res = TestProduct.where(visible: nil).where(tag_list: nil)
|
45
|
+
expect(res.map(&:id)).to eq([])
|
46
|
+
|
47
|
+
res = TestProduct.where(tag_list: nil).where(name: nil)
|
48
|
+
expect(res.map(&:id)).to eq([])
|
49
|
+
end
|
43
50
|
it 'works with other args too' do
|
44
51
|
res = TestProduct.where(name: 'Product 5').where(tag_list: nil)
|
45
52
|
expect(res.map(&:id)).to eq([@p5.id])
|
@@ -117,6 +124,12 @@ describe Groovy::Query do
|
|
117
124
|
res = TestProduct.where(tag_list: /two & three/)
|
118
125
|
expect(res.map(&:id)).to eq([@p1.id])
|
119
126
|
end
|
127
|
+
|
128
|
+
it 'works with slashes' do
|
129
|
+
str = 'two / something'
|
130
|
+
res = TestProduct.where(tag_list: /#{str}/)
|
131
|
+
expect(res.map(&:id)).to eq([@p4.id])
|
132
|
+
end
|
120
133
|
end
|
121
134
|
|
122
135
|
describe 'starts with regex' do
|
@@ -187,6 +200,13 @@ describe Groovy::Query do
|
|
187
200
|
res = TestProduct.where.not(tag_list: nil)
|
188
201
|
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p4.id])
|
189
202
|
end
|
203
|
+
it 'works with other nil values too' do
|
204
|
+
res = TestProduct.where.not(visible: nil).where(tag_list: nil)
|
205
|
+
expect(res.map(&:id)).to eq([@p3.id, @p5.id])
|
206
|
+
|
207
|
+
res = TestProduct.where.not(tag_list: nil).where(name: nil)
|
208
|
+
expect(res.map(&:id)).to eq([])
|
209
|
+
end
|
190
210
|
it 'works with other args too' do
|
191
211
|
res = TestProduct.where.not(name: 'Product 2').where.not(tag_list: nil)
|
192
212
|
expect(res.map(&:id)).to eq([@p1.id, @p4.id])
|
@@ -239,6 +259,12 @@ describe Groovy::Query do
|
|
239
259
|
res = TestProduct.not(tag_list: /two & three/)
|
240
260
|
expect(res.map(&:id)).to eq([@p2.id, @p3.id, @p4.id, @p5.id])
|
241
261
|
end
|
262
|
+
|
263
|
+
it 'works with slashes' do
|
264
|
+
str = 'two / something'
|
265
|
+
res = TestProduct.not(tag_list: /#{str}/)
|
266
|
+
expect(res.map(&:id)).to eq([@p1.id, @p2.id, @p3.id, @p5.id])
|
267
|
+
end
|
242
268
|
end
|
243
269
|
|
244
270
|
describe 'starts with regex' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: groovy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
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: 2020-
|
11
|
+
date: 2020-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rroonga
|