groovy 0.4.0 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1019bd31fda4785bec67322dfb8aeddfab4d0fa12143b23a8dd75f4a3c84515e
4
- data.tar.gz: 44ab6a5fd87d136bc3b6915ab323fac95b6a8012f5cdc4ea2fb6498d5236cb3a
3
+ metadata.gz: f2e7655a8fefa2f17295105acb6ba387f275eb158c0babed04675d566f736766
4
+ data.tar.gz: 5a9b94a767f58b033a11044d3b7992911a6cde0ad068955c76d85bdd2c0464b9
5
5
  SHA512:
6
- metadata.gz: 2ae8f4bcfb8d6166fa4ad8814521c498a44f3e512de23c4cb986d7ce7cd4ef98722a889c3a8e69e1f55bdfc9651f19a9b03e9d31bcb2f87552c73c194fc8f099
7
- data.tar.gz: 4a29d7007af10d67f4265ef9e431fa347ebc979745c87037fbf8957e2441fc012904cf5385d8aad8d1da6708e91ffae6b407acecf6e3c8e19f2f0bc787d90639
6
+ metadata.gz: 2c8813e0920559c6422f8129ca40e51c42a6db26efc65c9f2d9a1d47d8f5f6d5638f9677f970a38d36a180c93cf610b99b7adc372579eaff92ae4d8bd9aa41ae
7
+ data.tar.gz: d12ed329507b8a5c191308e198a5434f8226fd24bf4f25c8b2b8d64a832c05df39ff9946dddd53b7da5df11c2d14bc9ab97e4dcf84d67e60c9c2811d0c0122b6
@@ -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
@@ -34,6 +34,7 @@ class Place
34
34
  t.reference :categories, "Categories", type: :vector
35
35
  t.string :name
36
36
  t.string :description, index: true
37
+ t.boolean :visible
37
38
  t.timestamps
38
39
  end
39
40
 
@@ -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
@@ -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
- if val = record[key]
408
- if self.class.schema.time_column?(key)
409
- fix_time_value(val)
410
- else
411
- val
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
 
@@ -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 == false || val.to_s.strip == '' ? '\\' : escape_val(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 == false || val.to_s.strip == '' ? '\\' : escape_val(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) * PER_PAGE
147
- offset(offset).limit(PER_PAGE) # returns self
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 parameters.include?(param)
236
- raise "Duplicate param: #{param}"
237
- end
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 parameters.any?
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
@@ -56,14 +56,25 @@ module Groovy
56
56
  end
57
57
 
58
58
  def time_columns
59
- # @time_columns ||=
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 time_column?(name)
64
- time_columns.include?(name)
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
@@ -1,3 +1,3 @@
1
1
  module Groovy
2
- VERSION = '0.4.0'.freeze
2
+ VERSION = '0.4.5'.freeze
3
3
  end
@@ -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)
@@ -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.0
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-09-09 00:00:00.000000000 Z
11
+ date: 2020-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rroonga