groovy 0.4.1 → 0.4.6

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: b64b5151ebc9ad89b92fac064613d778e067449e11eb0c87cbf8ce40ca5e2630
4
- data.tar.gz: 3e95ca4bac3946bcf79805c174216be0cb2870c06e47369056054bfb4d61a637
3
+ metadata.gz: 2569117dfa3663d6c60ed950be2972c58116e7eaf6d21f5c6640cb91642fee4f
4
+ data.tar.gz: 33e1651825dd626dab979e9c504d5de1b7d8162363d42cbeca43873e370cad04
5
5
  SHA512:
6
- metadata.gz: 3320e1c8e3d31b9d48c2fbf409f2688b3be16734eebd631b9ee44f9509934968079b9ed1ab14a02422a559865e2619da1954640feed825bc17b8865ab96ac4d1
7
- data.tar.gz: 1d1ebb476d22fa504a2b1f7ef7585f298fb5fbc84dbdc22cb34997c1a8e57032622e0c12867aab730bf75f08293d7f093a49059efb68d11c150be5684e4c9c34
6
+ metadata.gz: 66b311fe79a3f9c8467ddbe9f863a2fd731b9c7dd9df2dbeb49fc1b8d62f4b7b506b2e0ca1f05c6ad7766ede89df0a26cb4a8eae8f96097ff613ab17cadde593
7
+ data.tar.gz: c1dbb4f84dfdb79d870a0e498acbc63d7e1268f5cfd1f0951fd16cc7d890b68a6d3679e903eb703b8de4a44b6765070594df0eb53b8cfbeb7ac9fb1499f5dd3f
@@ -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
@@ -187,16 +187,6 @@ module Groovy
187
187
  query
188
188
  end
189
189
 
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
190
  def query
201
191
  query_class.new(self, table)
202
192
  end
@@ -208,7 +198,7 @@ module Groovy
208
198
  end
209
199
  end
210
200
 
211
- [:find_each, :find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
201
+ [:first, :last, :select, :find_each, :find_by, :search, :where, :not, :sort_by, :limit, :offset, :paginate, :in_batches].each do |scope_method|
212
202
  define_method scope_method do |*args, &block|
213
203
  query.public_send(scope_method, *args, &block)
214
204
  end
@@ -221,6 +211,12 @@ module Groovy
221
211
  set_timestamp(attributes, :created_at)
222
212
  set_timestamp(attributes, :updated_at)
223
213
 
214
+ # remove nil attributes for integer columns, otherwise
215
+ # we get a TypeError (no implicit conversion from nil to integer)
216
+ attributes.each do |k, v|
217
+ attributes.delete(k) if v.nil? # && schema.integer_columns.include?(k)
218
+ end
219
+
224
220
  if table.support_key?
225
221
  raise "Key required" if key.nil?
226
222
  table.add(key, attributes)
@@ -297,7 +293,7 @@ module Groovy
297
293
  else
298
294
  attrs ||= {}
299
295
  unless attrs.is_a?(Hash)
300
- raise ArgumentError.new("Attributes should be a Hash")
296
+ raise ArgumentError.new("Attributes should be a Hash, not a #{attrs.class}")
301
297
  end
302
298
 
303
299
  # don't call set_attributes since we don't want to call
@@ -309,6 +305,12 @@ module Groovy
309
305
  @changes = {}
310
306
  end
311
307
 
308
+ # get reference to the actual record in the Groonga table,
309
+ # not the temporary one we get as part of a search result.
310
+ def load_record
311
+ self.class.table[id]
312
+ end
313
+
312
314
  def inspect
313
315
  "#<#{self.class.name} id:#{id.inspect} attributes:[#{self.class.attribute_names.join(', ')}]>"
314
316
  end
@@ -394,18 +396,18 @@ module Groovy
394
396
  end
395
397
 
396
398
  def ==(other)
397
- self.id == other.id
399
+ self.class == other.class && self.id == other.id
398
400
  end
399
401
 
400
402
  def <=>(other)
401
- self.id <=> other.id
403
+ self.class == other.class && self.id <=> other.id
402
404
  end
403
405
 
404
406
  private
405
407
 
406
408
  def get_record_attribute(key)
407
409
  val = record[key]
408
- if self.class.schema.time_column?(key)
410
+ if self.class.schema.time_columns.include?(key)
409
411
  fix_time_value(val)
410
412
  else
411
413
  val
@@ -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
 
@@ -47,6 +49,11 @@ module Groovy
47
49
  self
48
50
  end
49
51
 
52
+ def select(&block)
53
+ @select_block = block
54
+ self
55
+ end
56
+
50
57
  def find(id)
51
58
  find_by(_id: id)
52
59
  end
@@ -76,7 +83,7 @@ module Groovy
76
83
  add_param(AND + [key, val.max].join(':<=')) if val.max # gte
77
84
 
78
85
  elsif val.is_a?(Regexp)
79
- str = val.source.gsub(REMOVE_INVALID_CHARS_REGEX, '')
86
+ str = val.source.gsub('/', '_slash_').gsub('(', '_openp_').gsub(')', '_closep_').gsub(REMOVE_INVALID_CHARS_REGEX, '')
80
87
  param = val.source[0] == '^' ? ':^' : val.source[-1] == '$' ? ':$' : ':~' # starts with or regexp
81
88
  add_param(AND + [key, str.downcase].join(param)) # regex must be downcase
82
89
 
@@ -110,7 +117,7 @@ module Groovy
110
117
  add_param(AND + [key, val.max].join(':>=')) if val.max # lte, nil if range.max is -1
111
118
 
112
119
  elsif val.is_a?(Regexp)
113
- str = val.source.gsub(REMOVE_INVALID_CHARS_REGEX, '')
120
+ str = val.source.gsub('/', '_slash_').gsub('(', '_openp_').gsub(')', '_closep_').gsub(REMOVE_INVALID_CHARS_REGEX, '')
114
121
  param = val.source[0] == '^' ? ':^' : val.source[-1] == '$' ? ':$' : ':~' # starts with or regexp
115
122
  add_param(NOT + [key, str.downcase].join(param)) # regex must be downcase
116
123
 
@@ -132,25 +139,25 @@ module Groovy
132
139
  end
133
140
 
134
141
  def limit(num)
135
- @sorting[:limit] = num
142
+ sorting[:limit] = num
136
143
  self
137
144
  end
138
145
 
139
146
  def offset(num)
140
- @sorting[:offset] = num
147
+ sorting[:offset] = num
141
148
  self
142
149
  end
143
150
 
144
- def paginate(page = 1)
151
+ def paginate(page = 1, per_page: PER_PAGE)
145
152
  page = 1 if page.to_i < 1
146
- offset = ((page.to_i)-1) * PER_PAGE
147
- offset(offset).limit(PER_PAGE) # returns self
153
+ offset = ((page.to_i)-1) * per_page
154
+ offset(offset).limit(per_page) # returns self
148
155
  end
149
156
 
150
157
  # sort_by(title: :asc)
151
158
  def sort_by(hash)
152
- if hash.is_a?(String) || hash.is_a?(Symbol) # e.g. 'title.desc' or :title (asc by default)
153
- 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|\./)
154
161
  hash = {}
155
162
  hash[param] = dir || 'asc'
156
163
  end
@@ -201,23 +208,30 @@ module Groovy
201
208
  @total_entries
202
209
  end
203
210
 
204
- def last(count = 1)
205
- if count > 1
206
- records[(size-count)..-1]
207
- else
208
- records[size-1]
211
+ def first(num = 1)
212
+ limit(num)
213
+ num == 1 ? records.first : records
214
+ end
215
+
216
+ def last(num = 1)
217
+ if sorting[:by]
218
+ get_last(num)
219
+ else # no sorting, so
220
+ sort_by(_id: :desc).limit(num)
221
+ # if last(2) or more, then re-sort by ascending ID
222
+ num == 1 ? records.first : records.sort { |a,b| a.id <=> b.id }
209
223
  end
210
224
  end
211
225
 
212
226
  def in_batches(of: 1000, from: nil, &block)
213
- @sorting[:limit] = of
214
- @sorting[:offset] = from || 0
227
+ sorting[:limit] = of
228
+ sorting[:offset] = from || 0
215
229
 
216
230
  while results.any?
217
231
  yield to_a
218
232
  break if results.size < of
219
233
 
220
- @sorting[:offset] += of
234
+ sorting[:offset] += of
221
235
  @records = @results = nil # reset
222
236
  end
223
237
  end
@@ -229,9 +243,18 @@ module Groovy
229
243
  end
230
244
 
231
245
  private
232
- attr_reader :model, :table, :options
246
+ attr_reader :model, :table, :options, :select_block
247
+
248
+ def get_last(count = 1)
249
+ if count > 1
250
+ records[(size-count)..-1]
251
+ else
252
+ records[size-1]
253
+ end
254
+ end
233
255
 
234
256
  def add_param(param)
257
+ raise "Select block already given!" if select_block
235
258
  raise "Duplicate param: #{param}" if parameters.include?(param)
236
259
  parameters.push(param)
237
260
  end
@@ -244,7 +267,10 @@ module Groovy
244
267
  end
245
268
 
246
269
  def execute
247
- set = if parameters.any?
270
+ set = if select_block
271
+ debug "Finding records with select block"
272
+ table.select { |record| select_block.call(record) }
273
+ elsif parameters.any?
248
274
  query = prepare_query
249
275
  debug "Finding records with query: #{query}"
250
276
  table.select(query, options)
@@ -255,7 +281,7 @@ module Groovy
255
281
 
256
282
  @total_entries = set.size
257
283
 
258
- debug "Sorting with #{sort_key_and_order}, #{sorting.inspect}"
284
+ debug "Sorting with #{sort_key_and_order} (options: #{sorting.inspect})"
259
285
  set = set.sort(sort_key_and_order, {
260
286
  limit: sorting[:limit],
261
287
  offset: sorting[:offset], # [sorting[:offset], @total_entries].min
@@ -287,6 +313,7 @@ module Groovy
287
313
  query = parameters.join(' ').split(/ or /i).map do |part|
288
314
  part.gsub(' ', ' ') # replace double with single spaces
289
315
  .gsub(space_regex, '\ \1') # escape spaces before word letters
316
+ .gsub('_slash_', '\/').gsub('_openp_', '\(').gsub('_closep_', '\)')
290
317
  .gsub(/(\d\d):(\d\d):(\d\d)/, '\1\:\2\:\3') # escape hh:mm:ss in timestamps
291
318
  end.join(' OR ').sub(/^-/, '_id:>0 -') #.gsub(' OR -', ' -')
292
319
  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.1'.freeze
2
+ VERSION = '0.4.6'.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
 
@@ -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.1
4
+ version: 0.4.6
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-07 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