groovy 0.4.2 → 0.4.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2e735dd501cd86a3ee2ab21b111bf7aa4ab3d76d9d14acf8be86b97e34335b1
4
- data.tar.gz: 97b3b88d1f297ab6d74b9818858272c8fa711a27a3c0d64964508988a3304b85
3
+ metadata.gz: 43812d3bd8ef9579ceed22b3d19e78378ceb3a924bfc8d6c0983e49199c26c88
4
+ data.tar.gz: 5960ca73a253d62f31301995c8f643bcaa084a99140b5f59eee0161c816d20e2
5
5
  SHA512:
6
- metadata.gz: deb85e6549f328555d795e542e282c6e3a50c574fdac769530648c69292f02ed469bc293304db863b7c9ac28a5bb653bd85eb79a7652f6390b3f5309fc492552
7
- data.tar.gz: 7546011290684eca615cf4823df5886908fe823c9bee086a80504a01a7d3aa846a0fd34dc05efc023a061078ac3645d352618c64a03b614989cb51bc3ca4c5c9
6
+ metadata.gz: 22cc09a73d9804f5d5e1cb432d3948ff24ef9a28dbd84b5a403b3709477d013089617ebf19b23f4d3450c3c7dc8e15c01ab527698c5efa5821c9d351056777ad
7
+ data.tar.gz: 809b9fa8e3158df7afeb6e80967d7156772094b21b69f6fe7d9f5e6db03626fe8a9b2ee123f89c7bf791ef6e195415524b1e967291d836becdc6173fbd0bfbfc
@@ -28,4 +28,18 @@ populate if Product.count == 0
28
28
 
29
29
  # 50_000 products: 50M
30
30
  # 100_000 products: 50M
31
- # 500_000 products: 62M
31
+ # 500_000 products: 62M
32
+
33
+ module MemInfo
34
+ KERNEL_PAGE_SIZE = `getconf PAGESIZE`.chomp.to_i rescue 4096
35
+ STATM_PATH = "/proc/#{Process.pid}/statm"
36
+ STATM_FOUND = File.exist?(STATM_PATH)
37
+ def self.rss
38
+ STATM_FOUND ? (File.read(STATM_PATH).split(' ')[1].to_i * KERNEL_PAGE_SIZE) / 1024 : 0
39
+ end
40
+ end
41
+
42
+ puts MemInfo.rss
43
+ prices = Product.all.map { |x| x.price }
44
+ puts prices.inspect
45
+ puts MemInfo.rss
@@ -150,7 +150,6 @@ module Groovy
150
150
  # Groonga["#{table_name}.#{name}"] # .search, .similar_search, etc
151
151
  # end
152
152
 
153
-
154
153
  def index_search(column, query, options = {}, &block)
155
154
  results = table.select { |rec| rec[column].match(query) }
156
155
  render_results(results, &block)
@@ -187,16 +186,6 @@ module Groovy
187
186
  query
188
187
  end
189
188
 
190
- def first(num = 1)
191
- arr = limit(num)
192
- num == 1 ? arr.first : arr
193
- end
194
-
195
- def last(num = 1)
196
- arr = all.sort_by(_id: :desc).limit(num)
197
- num == 1 ? arr.first : arr
198
- end
199
-
200
189
  def query
201
190
  query_class.new(self, table)
202
191
  end
@@ -208,7 +197,7 @@ module Groovy
208
197
  end
209
198
  end
210
199
 
211
- [:select, :find_each, :find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
200
+ [:first, :last, :select, :find_each, :find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
212
201
  define_method scope_method do |*args, &block|
213
202
  query.public_send(scope_method, *args, &block)
214
203
  end
@@ -221,6 +210,12 @@ module Groovy
221
210
  set_timestamp(attributes, :created_at)
222
211
  set_timestamp(attributes, :updated_at)
223
212
 
213
+ # remove nil attributes for integer columns, otherwise
214
+ # we get a TypeError (no implicit conversion from nil to integer)
215
+ attributes.each do |k, v|
216
+ attributes.delete(k) if v.nil? # && schema.integer_columns.include?(k)
217
+ end
218
+
224
219
  if table.support_key?
225
220
  raise "Key required" if key.nil?
226
221
  table.add(key, attributes)
@@ -287,17 +282,17 @@ module Groovy
287
282
  end
288
283
  end
289
284
 
290
- attr_reader :id, :attributes, :record, :changes
285
+ attr_reader :id, :record, :changes
291
286
 
292
287
  def initialize(attrs = nil, record = nil, key = nil)
293
288
  @attributes, @vectors, @_key = {}, {}, key # key is used on creation only
294
289
 
295
290
  if set_record(record)
296
- set_attributes_from_record(record)
291
+ # load_attributes_from_record(record)
297
292
  else
298
293
  attrs ||= {}
299
294
  unless attrs.is_a?(Hash)
300
- raise ArgumentError.new("Attributes should be a Hash")
295
+ raise ArgumentError.new("Attributes should be a Hash, not a #{attrs.class}")
301
296
  end
302
297
 
303
298
  # don't call set_attributes since we don't want to call
@@ -311,8 +306,13 @@ module Groovy
311
306
 
312
307
  # get reference to the actual record in the Groonga table,
313
308
  # not the temporary one we get as part of a search result.
314
- def load_record
315
- self.class.table[id]
309
+ # def load_record
310
+ # self.class.table[id]
311
+ # end
312
+
313
+ def attributes
314
+ load_attributes_from_record # populate missing
315
+ @attributes
316
316
  end
317
317
 
318
318
  def inspect
@@ -325,7 +325,9 @@ module Groovy
325
325
  end
326
326
 
327
327
  def [](key)
328
- attributes[key.to_sym]
328
+ k = key.to_sym
329
+ @attributes[k] = get_record_attribute(k) unless @attributes.key?(k)
330
+ @attributes[k]
329
331
  end
330
332
 
331
333
  def []=(key, val)
@@ -386,11 +388,15 @@ module Groovy
386
388
  end
387
389
 
388
390
  def reload
389
- raise RecordNotPersisted if id.nil?
390
- ensure_persisted!
391
- rec = self.class.table[id] # _key
392
- # set_record(rec)
393
- set_attributes_from_record(rec)
391
+ unless new_record?
392
+ # raise RecordNotPersisted if id.nil?
393
+ # ensure_persisted!
394
+ rec = self.class.table[id] # _key
395
+ set_record(rec)
396
+ # load_attributes_from_record
397
+ end
398
+
399
+ @attributes = {}
394
400
  @changes = {}
395
401
  self
396
402
  end
@@ -400,18 +406,19 @@ module Groovy
400
406
  end
401
407
 
402
408
  def ==(other)
403
- self.id == other.id
409
+ self.class == other.class && self.id == other.id
404
410
  end
405
411
 
406
412
  def <=>(other)
407
- self.id <=> other.id
413
+ self.class == other.class && self.id <=> other.id
408
414
  end
409
415
 
410
416
  private
411
417
 
412
418
  def get_record_attribute(key)
419
+ return if record.nil?
413
420
  val = record[key]
414
- if self.class.schema.time_column?(key)
421
+ if self.class.schema.time_columns.include?(key)
415
422
  fix_time_value(val)
416
423
  else
417
424
  val
@@ -427,15 +434,15 @@ module Groovy
427
434
  # record.respond_to?(:_key) ? record._key : id
428
435
  # end
429
436
 
430
- def set_attributes_from_record(rec)
437
+ def load_attributes_from_record
431
438
  self.class.attribute_names.each do |col|
432
- public_send("#{col}=", get_record_attribute(col))
439
+ public_send("#{col}=", get_record_attribute(col)) unless @attributes.key?(col)
433
440
  end
434
441
  end
435
442
 
436
443
  def set_attribute(key, val)
437
- changes[key.to_sym] = [self[key], val] if changes # nil when initializing
438
- attributes[key.to_sym] = val
444
+ changes[key.to_sym] = [self[key], val] if changes # nil before initializing
445
+ @attributes[key.to_sym] = val
439
446
  end
440
447
 
441
448
  def get_ref(name)
@@ -460,7 +467,7 @@ module Groovy
460
467
 
461
468
  def create
462
469
  fire_callbacks(:before_create)
463
- set_record(self.class.insert(attributes, @_key))
470
+ set_record(self.class.insert(@attributes, @_key))
464
471
  fire_callbacks(:after_create)
465
472
  self
466
473
  end
@@ -3,9 +3,11 @@ module Groovy
3
3
  class Query
4
4
 
5
5
  include Enumerable
6
+
6
7
  AND = '+'.freeze
7
8
  NOT = '-'.freeze
8
9
  PER_PAGE = 50.freeze
10
+ # ESCAPE_CHARS_REGEX = /([\(\)\/\\])/.freeze
9
11
  VALID_QUERY_CHARS = 'a-zA-Z0-9_\.,&-'.freeze
10
12
  REMOVE_INVALID_CHARS_REGEX = Regexp.new('[^\s' + VALID_QUERY_CHARS + ']').freeze
11
13
 
@@ -81,7 +83,7 @@ module Groovy
81
83
  add_param(AND + [key, val.max].join(':<=')) if val.max # gte
82
84
 
83
85
  elsif val.is_a?(Regexp)
84
- str = val.source.gsub(REMOVE_INVALID_CHARS_REGEX, '')
86
+ str = val.source.gsub('/', '_slash_').gsub('(', '_openp_').gsub(')', '_closep_').gsub(REMOVE_INVALID_CHARS_REGEX, '')
85
87
  param = val.source[0] == '^' ? ':^' : val.source[-1] == '$' ? ':$' : ':~' # starts with or regexp
86
88
  add_param(AND + [key, str.downcase].join(param)) # regex must be downcase
87
89
 
@@ -115,7 +117,7 @@ module Groovy
115
117
  add_param(AND + [key, val.max].join(':>=')) if val.max # lte, nil if range.max is -1
116
118
 
117
119
  elsif val.is_a?(Regexp)
118
- str = val.source.gsub(REMOVE_INVALID_CHARS_REGEX, '')
120
+ str = val.source.gsub('/', '_slash_').gsub('(', '_openp_').gsub(')', '_closep_').gsub(REMOVE_INVALID_CHARS_REGEX, '')
119
121
  param = val.source[0] == '^' ? ':^' : val.source[-1] == '$' ? ':$' : ':~' # starts with or regexp
120
122
  add_param(NOT + [key, str.downcase].join(param)) # regex must be downcase
121
123
 
@@ -137,12 +139,12 @@ module Groovy
137
139
  end
138
140
 
139
141
  def limit(num)
140
- @sorting[:limit] = num
142
+ sorting[:limit] = num
141
143
  self
142
144
  end
143
145
 
144
146
  def offset(num)
145
- @sorting[:offset] = num
147
+ sorting[:offset] = num
146
148
  self
147
149
  end
148
150
 
@@ -154,8 +156,8 @@ module Groovy
154
156
 
155
157
  # sort_by(title: :asc)
156
158
  def sort_by(hash)
157
- if hash.is_a?(String) || hash.is_a?(Symbol) # e.g. 'title.desc' or :title (asc by default)
158
- param, dir = hash.to_s.split('.')
159
+ if hash.is_a?(String) || hash.is_a?(Symbol) # e.g. 'title.desc', 'title desc' or :title (asc by default)
160
+ param, dir = hash.to_s.split(/\s|\./)
159
161
  hash = {}
160
162
  hash[param] = dir || 'asc'
161
163
  end
@@ -190,11 +192,16 @@ module Groovy
190
192
  end
191
193
 
192
194
  def [](index)
193
- records[index]
195
+ if r = results[index]
196
+ model.new_from_record(r)
197
+ end
194
198
  end
195
199
 
196
200
  def each(&block)
197
- records.each { |r| block.call(r) }
201
+ # records.each { |r| block.call(r) }
202
+ results.each_with_index do |r, index|
203
+ yield model.new_from_record(r)
204
+ end
198
205
  end
199
206
 
200
207
  def update_all(attrs)
@@ -206,35 +213,50 @@ module Groovy
206
213
  @total_entries
207
214
  end
208
215
 
209
- def last(count = 1)
210
- if count > 1
211
- records[(size-count)..-1]
212
- else
213
- records[size-1]
216
+ def first(num = 1)
217
+ limit(num)
218
+ num == 1 ? records.first : records
219
+ end
220
+
221
+ def last(num = 1)
222
+ if sorting[:by]
223
+ get_last(num)
224
+ else # no sorting, so
225
+ sort_by(_id: :desc).limit(num)
226
+ # if last(2) or more, then re-sort by ascending ID
227
+ num == 1 ? records.first : records.sort { |a,b| a.id <=> b.id }
214
228
  end
215
229
  end
216
230
 
217
231
  def in_batches(of: 1000, from: nil, &block)
218
- @sorting[:limit] = of
219
- @sorting[:offset] = from || 0
232
+ sorting[:limit] = of
233
+ sorting[:offset] = from || 0
220
234
 
221
235
  while results.any?
222
236
  yield to_a
223
237
  break if results.size < of
224
238
 
225
- @sorting[:offset] += of
239
+ sorting[:offset] += of
226
240
  @records = @results = nil # reset
227
241
  end
228
242
  end
229
243
 
244
+ private
245
+ attr_reader :model, :table, :options, :select_block
246
+
230
247
  def records
231
248
  @records ||= results.map do |r|
232
249
  model.new_from_record(r)
233
250
  end
234
251
  end
235
252
 
236
- private
237
- attr_reader :model, :table, :options, :select_block
253
+ def get_last(count = 1)
254
+ if count > 1
255
+ records[(size-count)..-1]
256
+ else
257
+ records[size-1]
258
+ end
259
+ end
238
260
 
239
261
  def add_param(param)
240
262
  raise "Select block already given!" if select_block
@@ -264,7 +286,7 @@ module Groovy
264
286
 
265
287
  @total_entries = set.size
266
288
 
267
- debug "Sorting with #{sort_key_and_order}, #{sorting.inspect}"
289
+ debug "Sorting with #{sort_key_and_order} (options: #{sorting.inspect})"
268
290
  set = set.sort(sort_key_and_order, {
269
291
  limit: sorting[:limit],
270
292
  offset: sorting[:offset], # [sorting[:offset], @total_entries].min
@@ -296,6 +318,7 @@ module Groovy
296
318
  query = parameters.join(' ').split(/ or /i).map do |part|
297
319
  part.gsub(' ', ' ') # replace double with single spaces
298
320
  .gsub(space_regex, '\ \1') # escape spaces before word letters
321
+ .gsub('_slash_', '\/').gsub('_openp_', '\(').gsub('_closep_', '\)')
299
322
  .gsub(/(\d\d):(\d\d):(\d\d)/, '\1\:\2\:\3') # escape hh:mm:ss in timestamps
300
323
  end.join(' OR ').sub(/^-/, '_id:>0 -') #.gsub(' OR -', ' -')
301
324
  end
@@ -26,6 +26,7 @@ module Groovy
26
26
  def initialize(context, table_name, opts = {})
27
27
  @context, @table_name, @opts = context, table_name, opts || {}
28
28
  @spec, @index_columns = {}, []
29
+ @cache = {}
29
30
  end
30
31
 
31
32
  def table
@@ -33,35 +34,43 @@ module Groovy
33
34
  end
34
35
 
35
36
  def search_table
36
- @search_table ||= context[SEARCH_TABLE_NAME]
37
+ @cache[:search_table] ||= context[SEARCH_TABLE_NAME]
37
38
  end
38
39
 
39
40
  def column_names
40
- get_names table.columns
41
+ @cache[:column_names] ||= get_names(table.columns)
41
42
  end
42
43
 
43
44
  def singular_references
44
- # @singular_references ||=
45
- get_names(table.columns.select(&:reference_column?).reject(&:vector?))
45
+ @cache[:singular_references] ||= get_names(table.columns.select(&:reference_column?).reject(&:vector?))
46
46
  end
47
47
 
48
48
  def plural_references
49
- # @plural_references ||=
50
- get_names(table.columns.select(&:vector?))
49
+ @cache[:plural_references] ||= get_names(table.columns.select(&:vector?))
51
50
  end
52
51
 
53
52
  def attribute_columns
54
- # @attribute_columns ||=
55
- get_names(table.columns.select { |c| c.column? && !c.reference_column? && !c.vector? })
53
+ @cache[:attribute_columns] ||= get_names(table.columns.select { |c| c.column? && !c.reference_column? && !c.vector? })
56
54
  end
57
55
 
58
56
  def time_columns
59
- # @time_columns ||=
60
- get_names(table.columns.select { |c| c.column? && c.range.name == 'Time' })
57
+ @cache[:time_columns] ||= columns_by_type('Time')
61
58
  end
62
59
 
63
- def time_column?(name)
64
- time_columns.include?(name)
60
+ def integer_columns
61
+ @cache[:integer_columns] ||= columns_by_type('Int32')
62
+ end
63
+
64
+ def boolean_columns
65
+ @cache[:boolean_columns] ||= columns_by_type('Bool')
66
+ end
67
+
68
+ def columns_by_type(type)
69
+ get_names(table.columns.select { |c| c.column? && c.range.name == type })
70
+ end
71
+
72
+ def reload
73
+ @cache = {}
65
74
  end
66
75
 
67
76
  def rebuild!
@@ -105,6 +114,8 @@ module Groovy
105
114
  @index_columns.each do |col|
106
115
  add_index_on(col)
107
116
  end
117
+
118
+ reload
108
119
  self
109
120
  end
110
121
 
@@ -1,3 +1,3 @@
1
1
  module Groovy
2
- VERSION = '0.4.2'.freeze
2
+ VERSION = '0.4.7'.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)
@@ -78,6 +86,28 @@ describe Groovy::Model do
78
86
  end
79
87
 
80
88
  describe '#[]' do
89
+ it 'reads value from record' do
90
+ prod = TestProduct.create!(name: 'A product', price: 100)
91
+ expect(prod.name).to eq('A product')
92
+ expect(prod['name']).to eq('A product')
93
+ expect(prod[:name]).to eq('A product')
94
+
95
+ prod = TestProduct.find(prod.id)
96
+ expect(prod.name).to eq('A product')
97
+ expect(prod['name']).to eq('A product')
98
+ expect(prod[:name]).to eq('A product')
99
+
100
+ prod = TestProduct.new
101
+ expect(prod.name).to eq(nil)
102
+ prod.name = 'Another product'
103
+ expect(prod.name).to eq('Another product')
104
+ prod.reload
105
+ expect(prod.name).to eq(nil)
106
+ prod.name = 'Another product'
107
+ prod.save
108
+ prod.reload
109
+ expect(prod.name).to eq('Another product')
110
+ end
81
111
  end
82
112
 
83
113
  describe '#[]=' do
@@ -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
 
@@ -124,6 +124,12 @@ describe Groovy::Query do
124
124
  res = TestProduct.where(tag_list: /two & three/)
125
125
  expect(res.map(&:id)).to eq([@p1.id])
126
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
127
133
  end
128
134
 
129
135
  describe 'starts with regex' do
@@ -253,6 +259,12 @@ describe Groovy::Query do
253
259
  res = TestProduct.not(tag_list: /two & three/)
254
260
  expect(res.map(&:id)).to eq([@p2.id, @p3.id, @p4.id, @p5.id])
255
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
256
268
  end
257
269
 
258
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.2
4
+ version: 0.4.7
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-10-13 00:00:00.000000000 Z
11
+ date: 2020-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rroonga