groovy 0.1.3 → 0.1.4

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: 4a5a7078b71e609451ea64d58ee47e7573f7570563118c1b1fdf62078617ce8d
4
- data.tar.gz: 7444d38fc53884cb3540c0ab73efd91f296fcfa96828890cc29509831e6ae656
3
+ metadata.gz: e12ec33808ce635a4cc1d18432daab18f075fdd6112d41be94c4b3d81039cfb9
4
+ data.tar.gz: 72fc2666d2c87469399e7c3297fa69a613e194f95ba22a51f1f4306a7123b36d
5
5
  SHA512:
6
- metadata.gz: c20a741e4074aae0e28164ed307080229257d9c1fb6e00bd9fbe50f7b558b0189c1172cadc0ecd9b56484724b442a9bdd5bd59e67fbf748bdee9f4c39642fbd0
7
- data.tar.gz: e6481be3ce2ae9fc87071bf1865e1672e7da9b3af0eee39a69dc718be6c4ef39b5dbb942cdd466fe26ccc2035fa5aba70037c12a2f373b44de42d88e3d7cc4b6
6
+ metadata.gz: 31599509b743b69e7b0da3f1348da0bbd029b81003685bddc3ce6b95100e8c11f7ad2524ba7b3600925cbd50b5468bae09eba0126d5e8b0072dc032e990be9f4
7
+ data.tar.gz: 0f73afaab9eb391731d864aaace675755c713ef32bfa2c045bc1760d5eda25cd67d3f2b395562d31a5bbf7affff0e50ec9c5d923a8f222516a21dec4dfef34f6
data/.gitignore CHANGED
@@ -4,3 +4,5 @@ test
4
4
  log
5
5
  pkg
6
6
  example/db
7
+ tmp
8
+ Gemfile.lock
data/example/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- groovy (0.1.2)
4
+ groovy (0.1.3)
5
5
  rroonga (~> 7.1)
6
6
 
7
7
  GEM
data/example/basic.rb CHANGED
@@ -11,6 +11,9 @@ class Product
11
11
  t.integer :price
12
12
  t.timestamps
13
13
  end
14
+
15
+ scope :by_price, -> { sort_by(price: :asc) }
16
+ scope :named, -> (name) { where(name: name) if name }
14
17
  end
15
18
 
16
19
  def populate
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 [](key)
13
- contexts[key.to_sym]
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
- raise "Context already defined: #{name}" if contexts[name.to_sym]
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 ||= begin
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
- all.in_batches(of: of, from: from, &block)
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.concat(another.parameters)
22
- sorting.merge!(another.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
- parameters.push(AND + "(#{map_operator(conditions)})")
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
- parameters.push(AND + [key, val.min].join(':>=')) if val.min # lte
50
- parameters.push(AND + [key, val.max].join(':<=')) if val.max # gte
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-z]/, '')
53
- param = val.source[0] == '^' ? ':^' : ':~' # starts with or regexp
54
- parameters.push(AND + [key, str].join(param))
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
- parameters.push(AND + str)
62
+ add_param(AND + str)
63
+
58
64
  else
59
- parameters.push(AND + [key, val.to_s].join(':'))
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
- parameters.push(AND + [key, val.min].join(':<=')) if val.min > 0 # gte
80
- parameters.push(AND + [key, val.max].join(':>=')) if val.max # lte, nil if range.max is -1
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(" OR #{key}:!")}"
83
- parameters.push(AND + str)
95
+ str = "#{key}:!#{val.join(" #{AND}#{key}:!")}"
96
+ add_param(AND + str)
97
+
84
98
  else
85
- parameters.push(AND + [key, val.to_s].join(':!')) # not
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.new_from_record(table[id])
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)
@@ -1,3 +1,3 @@
1
1
  module Groovy
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '0.1.4'.freeze
3
3
  end
data/spec/groovy_spec.rb CHANGED
@@ -1,61 +1,66 @@
1
- require './lib/groovy'
2
- require 'rspec/mocks'
3
-
4
- describe Groovy::Model do
5
-
6
- describe '.schema' do
7
- end
8
-
9
- describe '.scope' do
10
- end
11
-
12
- describe '.create' do
13
- end
14
-
15
- describe '.find' do
16
- end
17
-
18
- describe '.find_by' do
19
- end
20
-
21
- describe '.delete_all' do
22
- end
23
-
24
- describe '#[]' do
25
- end
26
-
27
- describe '#[]=' do
28
- end
29
-
30
- describe '#increment' do
31
- end
32
-
33
- describe '#dirty' do
34
- end
35
-
36
- describe '#update_attributes' do
37
- end
38
-
39
- describe '#save' do
40
- end
41
-
42
- describe '#delete' do
43
- end
44
-
45
- describe '#reload' do
46
- end
47
-
48
- describe 'attributes accessors' do
49
-
50
- end
51
-
52
- describe 'singular refs' do
53
-
54
- end
55
-
56
- describe 'plural refs' do
57
-
58
- end
59
-
60
- end
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
@@ -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
+
@@ -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
@@ -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.3
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-03 00:00:00.000000000 Z
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: rroonga
42
+ name: rspec
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '7.1'
34
- type: :runtime
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: '7.1'
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: {}