rasti-db 2.2.0 → 3.0.0

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/lib/rasti/db.rb +16 -5
  4. data/lib/rasti/db/collection.rb +10 -10
  5. data/lib/rasti/db/data_source.rb +1 -1
  6. data/lib/rasti/db/model.rb +3 -104
  7. data/lib/rasti/db/nql/filter_condition_strategies/base.rb +17 -0
  8. data/lib/rasti/db/nql/filter_condition_strategies/postgres.rb +21 -0
  9. data/lib/rasti/db/nql/filter_condition_strategies/sqlite.rb +21 -0
  10. data/lib/rasti/db/nql/filter_condition_strategies/types/generic.rb +49 -0
  11. data/lib/rasti/db/nql/filter_condition_strategies/types/pg_array.rb +32 -0
  12. data/lib/rasti/db/nql/filter_condition_strategies/types/sqlite_array.rb +34 -0
  13. data/lib/rasti/db/nql/filter_condition_strategies/unsupported_type_comparison.rb +22 -0
  14. data/lib/rasti/db/nql/nodes/array_content.rb +21 -0
  15. data/lib/rasti/db/nql/nodes/binary_node.rb +1 -1
  16. data/lib/rasti/db/nql/nodes/comparisons/base.rb +10 -0
  17. data/lib/rasti/db/nql/nodes/comparisons/equal.rb +0 -4
  18. data/lib/rasti/db/nql/nodes/comparisons/greater_than.rb +0 -4
  19. data/lib/rasti/db/nql/nodes/comparisons/greater_than_or_equal.rb +0 -4
  20. data/lib/rasti/db/nql/nodes/comparisons/include.rb +0 -4
  21. data/lib/rasti/db/nql/nodes/comparisons/less_than.rb +0 -4
  22. data/lib/rasti/db/nql/nodes/comparisons/less_than_or_equal.rb +0 -4
  23. data/lib/rasti/db/nql/nodes/comparisons/like.rb +0 -4
  24. data/lib/rasti/db/nql/nodes/comparisons/not_equal.rb +0 -4
  25. data/lib/rasti/db/nql/nodes/comparisons/not_include.rb +0 -4
  26. data/lib/rasti/db/nql/nodes/constants/array.rb +17 -0
  27. data/lib/rasti/db/nql/nodes/constants/base.rb +17 -0
  28. data/lib/rasti/db/nql/nodes/constants/false.rb +1 -1
  29. data/lib/rasti/db/nql/nodes/constants/float.rb +1 -1
  30. data/lib/rasti/db/nql/nodes/constants/integer.rb +1 -1
  31. data/lib/rasti/db/nql/nodes/constants/literal_string.rb +1 -1
  32. data/lib/rasti/db/nql/nodes/constants/string.rb +1 -1
  33. data/lib/rasti/db/nql/nodes/constants/time.rb +1 -1
  34. data/lib/rasti/db/nql/nodes/constants/true.rb +1 -1
  35. data/lib/rasti/db/nql/nodes/parenthesis_sentence.rb +1 -1
  36. data/lib/rasti/db/nql/syntax.rb +229 -11
  37. data/lib/rasti/db/nql/syntax.treetop +24 -11
  38. data/lib/rasti/db/relations/base.rb +3 -3
  39. data/lib/rasti/db/relations/graph.rb +15 -15
  40. data/lib/rasti/db/relations/many_to_many.rb +4 -4
  41. data/lib/rasti/db/relations/many_to_one.rb +4 -4
  42. data/lib/rasti/db/relations/one_to_many.rb +2 -2
  43. data/lib/rasti/db/type_converters/postgres.rb +32 -36
  44. data/lib/rasti/db/type_converters/postgres_types/array.rb +11 -9
  45. data/lib/rasti/db/type_converters/postgres_types/hstore.rb +10 -9
  46. data/lib/rasti/db/type_converters/postgres_types/json.rb +17 -14
  47. data/lib/rasti/db/type_converters/postgres_types/jsonb.rb +17 -14
  48. data/lib/rasti/db/type_converters/sqlite.rb +62 -0
  49. data/lib/rasti/db/type_converters/sqlite_types/array.rb +34 -0
  50. data/lib/rasti/db/type_converters/time_in_zone.rb +1 -1
  51. data/lib/rasti/db/version.rb +1 -1
  52. data/rasti-db.gemspec +2 -0
  53. data/spec/collection_spec.rb +36 -28
  54. data/spec/minitest_helper.rb +4 -2
  55. data/spec/nql/filter_condition_spec.rb +19 -2
  56. data/spec/nql/filter_condition_strategies_spec.rb +112 -0
  57. data/spec/nql/syntax_parser_spec.rb +24 -0
  58. data/spec/query_spec.rb +89 -0
  59. data/spec/relations_spec.rb +17 -17
  60. data/spec/type_converters/sqlite_spec.rb +66 -0
  61. data/spec/type_converters/time_in_zone_spec.rb +1 -1
  62. metadata +46 -4
  63. data/spec/model_spec.rb +0 -90
@@ -3,36 +3,39 @@ module Rasti
3
3
  module TypeConverters
4
4
  module PostgresTypes
5
5
  class JSONB
6
-
7
6
  class << self
8
7
 
9
- def column_type_regex
10
- /^jsonb$/
8
+ DB_TYPE_REGEX = /^jsonb$/
9
+
10
+ def to_db?(type)
11
+ !type.match(DB_TYPE_REGEX).nil?
11
12
  end
12
13
 
13
- def to_db(value, sub_type)
14
+ def to_db(value, type)
14
15
  Sequel.pg_jsonb value
15
16
  end
16
17
 
17
- def db_classes
18
- @db_classes ||= from_db_convertions.keys
18
+ def from_db?(klass)
19
+ to_hash?(klass) || to_array?(klass)
19
20
  end
20
21
 
21
- def from_db(object)
22
- object.public_send from_db_convertions[object.class]
22
+ def from_db(value)
23
+ to_hash?(value.class) ? value.to_h : value.to_a
23
24
  end
24
25
 
25
26
  private
26
27
 
27
- def from_db_convertions
28
- @from_db_convertions ||= {
29
- Sequel::Postgres::JSONBHash => :to_h,
30
- Sequel::Postgres::JSONBArray => :to_a
31
- }
28
+ def to_hash?(klass)
29
+ defined?(Sequel::Postgres::JSONBHash) &&
30
+ klass == Sequel::Postgres::JSONBHash
32
31
  end
33
32
 
34
- end
33
+ def to_array?(klass)
34
+ defined?(Sequel::Postgres::JSONBArray) &&
35
+ klass == Sequel::Postgres::JSONBArray
36
+ end
35
37
 
38
+ end
36
39
  end
37
40
  end
38
41
  end
@@ -0,0 +1,62 @@
1
+ module Rasti
2
+ module DB
3
+ module TypeConverters
4
+ class SQLite
5
+
6
+ CONVERTERS = [SQLiteTypes::Array]
7
+
8
+ @to_db_mapping = {}
9
+
10
+ class << self
11
+
12
+ def to_db(db, collection_name, attribute_name, value)
13
+ to_db_mapping = to_db_mapping_for db, collection_name
14
+
15
+ if to_db_mapping.key? attribute_name
16
+ to_db_mapping[attribute_name][:converter].to_db value
17
+ else
18
+ value
19
+ end
20
+ end
21
+
22
+ def from_db(object)
23
+ converter = find_converter_from_db object
24
+ if !converter.nil?
25
+ converter.from_db object
26
+ else
27
+ object
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def to_db_mapping_for(db, collection_name)
34
+ key = [db.opts[:database], collection_name]
35
+
36
+ @to_db_mapping[key] ||= begin
37
+ columns = Hash[db.schema(collection_name)]
38
+
39
+ columns.each_with_object({}) do |(name, schema), hash|
40
+ CONVERTERS.each do |converter|
41
+ unless hash.key? name
42
+ match = converter.column_type_regex.match schema[:db_type]
43
+
44
+ hash[name] = { converter: converter } if match
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def find_converter_from_db(object)
52
+ CONVERTERS.find do |converter|
53
+ converter.respond_for? object
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,34 @@
1
+ module Rasti
2
+ module DB
3
+ module TypeConverters
4
+ module SQLiteTypes
5
+ class Array
6
+
7
+ class << self
8
+
9
+ def column_type_regex
10
+ /^([a-z]+)\[\]$/
11
+ end
12
+
13
+ def to_db(values)
14
+ JSON.dump(values)
15
+ end
16
+
17
+ def respond_for?(object)
18
+ parsed = JSON.parse object
19
+ object == to_db(parsed)
20
+ rescue
21
+ false
22
+ end
23
+
24
+ def from_db(object)
25
+ JSON.parse object
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -6,7 +6,7 @@ module Rasti
6
6
  class << self
7
7
 
8
8
  def to_db(db, collection_name, attribute_name, value)
9
- value
9
+ value.is_a?(Timing::TimeInZone) ? value.to_time : value
10
10
  end
11
11
 
12
12
  def from_db(value)
@@ -1,5 +1,5 @@
1
1
  module Rasti
2
2
  module DB
3
- VERSION = '2.2.0'
3
+ VERSION = '3.0.0'
4
4
  end
5
5
  end
data/rasti-db.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_runtime_dependency 'sequel', '~> 5.0'
22
+ spec.add_runtime_dependency 'rasti-model', '~> 1.0'
22
23
  spec.add_runtime_dependency 'treetop', '~> 1.4.8'
23
24
  spec.add_runtime_dependency 'consty', '~> 1.0', '>= 1.0.3'
24
25
  spec.add_runtime_dependency 'timing', '~> 0.1', '>= 0.1.3'
@@ -26,6 +27,7 @@ Gem::Specification.new do |spec|
26
27
  spec.add_runtime_dependency 'multi_require', '~> 1.0'
27
28
  spec.add_runtime_dependency 'hierarchical_graph', '~> 1.0'
28
29
  spec.add_runtime_dependency 'hash_ext', '~> 0.5'
30
+ spec.add_runtime_dependency 'inflecto', '~> 0.0'
29
31
 
30
32
  spec.add_development_dependency 'rake', '~> 12.3'
31
33
  spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
@@ -2,6 +2,14 @@ require 'minitest_helper'
2
2
 
3
3
  describe 'Collection' do
4
4
 
5
+ before do
6
+ Rasti::DB.type_converters = [Rasti::DB::TypeConverters::TimeInZone]
7
+ end
8
+
9
+ after do
10
+ Rasti::DB.type_converters = [Rasti::DB::TypeConverters::TimeInZone, Rasti::DB::TypeConverters::SQLite]
11
+ end
12
+
5
13
  describe 'Specification' do
6
14
 
7
15
  it 'Implicit' do
@@ -44,7 +52,7 @@ describe 'Collection' do
44
52
  it 'Insert with many to many' do
45
53
  user_id = db[:users].insert name: 'User 1'
46
54
 
47
- 1.upto(2) do |i|
55
+ 1.upto(2) do |i|
48
56
  db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...', language_id: 1
49
57
  db[:categories].insert name: "Category #{i}"
50
58
  end
@@ -57,7 +65,7 @@ describe 'Collection' do
57
65
  end
58
66
 
59
67
  it 'Insert only many to many' do
60
- 1.upto(3) do |i|
68
+ 1.upto(3) do |i|
61
69
  db[:categories].insert name: "Category #{i}"
62
70
  end
63
71
 
@@ -93,7 +101,7 @@ describe 'Collection' do
93
101
  it 'Update with many to many' do
94
102
  user_id = db[:users].insert name: 'User 1'
95
103
 
96
- 1.upto(3) do |i|
104
+ 1.upto(3) do |i|
97
105
  db[:posts].insert user_id: user_id, title: "Post #{i}", body: '...', language_id: 1
98
106
  db[:categories].insert name: "Category #{i}"
99
107
  end
@@ -107,11 +115,11 @@ describe 'Collection' do
107
115
  posts.update 1, categories: [2,3]
108
116
 
109
117
  db[:categories_posts].where(post_id: 1).map(:category_id).must_equal [2,3]
110
-
118
+
111
119
  db[:categories_posts].where(category_id: 2).map(:post_id).must_equal [1,2]
112
120
 
113
121
  categories.update 2, posts: [2,3]
114
-
122
+
115
123
  db[:categories_posts].where(category_id: 2).map(:post_id).must_equal [2,3]
116
124
  end
117
125
 
@@ -137,7 +145,7 @@ describe 'Collection' do
137
145
  end
138
146
 
139
147
  it 'Delete only many to many' do
140
- 1.upto(3) do |i|
148
+ 1.upto(3) do |i|
141
149
  db[:categories].insert name: "Category #{i}"
142
150
  end
143
151
 
@@ -164,14 +172,14 @@ describe 'Collection' do
164
172
  1.upto(3) do |i|
165
173
  user_id = db[:users].insert name: "User #{i}"
166
174
 
167
- db[:people].insert document_number: "document_#{i}",
175
+ db[:people].insert document_number: "document_#{i}",
168
176
  first_name: "John #{i}",
169
177
  last_name: "Doe #{i}",
170
178
  birth_date: Time.now - i,
171
179
  user_id: user_id
172
180
 
173
- category_id = db[:categories].insert name: "Category #{i}"
174
-
181
+ category_id = db[:categories].insert name: "Category #{i}"
182
+
175
183
  1.upto(3) do |n|
176
184
  post_id = db[:posts].insert user_id: user_id, title: "Post #{i}.#{n}", body: '...', language_id: 1
177
185
  db[:categories_posts].insert post_id: post_id, category_id: category_id
@@ -219,7 +227,7 @@ describe 'Collection' do
219
227
  db[:posts].where(user_id: 1).count.must_equal 0
220
228
  db[:comments].join(:posts, id: :post_id).where(Sequel[:posts][:user_id] => 1).count.must_equal 0
221
229
  db[:categories_posts].join(:posts, id: :post_id).where(Sequel[:posts][:user_id] => 1).count.must_equal 0
222
-
230
+
223
231
  db[:users].count.must_equal 2
224
232
  db[:people].count.must_equal 2
225
233
  db[:categories].count.must_equal 3
@@ -233,9 +241,9 @@ describe 'Collection' do
233
241
  describe 'Multiple data sources' do
234
242
 
235
243
  before do
236
- 1.upto(3) do |i|
244
+ 1.upto(3) do |i|
237
245
  db[:users].insert name: "User #{i}"
238
- db[:people].insert document_number: "document_#{i}",
246
+ db[:people].insert document_number: "document_#{i}",
239
247
  first_name: "John #{i}",
240
248
  last_name: "Doe #{i}",
241
249
  birth_date: Time.now - i,
@@ -295,7 +303,7 @@ describe 'Collection' do
295
303
 
296
304
  it 'Find' do
297
305
  id = db[:users].insert name: 'User 1'
298
-
306
+
299
307
  users.find(id).must_equal User.new(id: id, name: 'User 1')
300
308
  end
301
309
 
@@ -402,26 +410,26 @@ describe 'Collection' do
402
410
 
403
411
  1.upto(2) do |i|
404
412
  db[:categories].insert name: "Category #{i}"
405
-
413
+
406
414
  db[:users].insert name: "User #{i}"
407
-
408
- db[:people].insert document_number: "document_#{i}",
415
+
416
+ db[:people].insert document_number: "document_#{i}",
409
417
  first_name: "John #{i}",
410
418
  last_name: "Doe #{i}",
411
419
  birth_date: Time.now - i,
412
420
  user_id: i
413
-
421
+
414
422
  end
415
423
 
416
424
  db[:languages_people].insert language_id: 1, document_number: 'document_1'
417
425
  db[:languages_people].insert language_id: 2, document_number: 'document_2'
418
426
 
419
- 1.upto(3) do |i|
427
+ 1.upto(3) do |i|
420
428
  db[:posts].insert user_id: 1, title: "Post #{i}", body: '...', language_id: 1
421
429
  db[:categories_posts].insert category_id: 1, post_id: i
422
430
  end
423
-
424
- 4.upto(5) do |i|
431
+
432
+ 4.upto(5) do |i|
425
433
  db[:posts].insert user_id: 2, title: "Post #{i}", body: '...', language_id: 2
426
434
  db[:categories_posts].insert category_id: 2, post_id: i
427
435
  end
@@ -467,7 +475,7 @@ describe 'Collection' do
467
475
  it 'Global' do
468
476
  result_1 = posts.created_by(1)
469
477
  result_1.primary_keys.must_equal [1,2,3]
470
-
478
+
471
479
  result_2 = posts.created_by(2)
472
480
  result_2.primary_keys.must_equal [4,5]
473
481
  end
@@ -482,7 +490,7 @@ describe 'Collection' do
482
490
  it 'Graph' do
483
491
  1.upto(3) do |i|
484
492
  db[:users].insert name: "User #{i}"
485
- db[:people].insert document_number: "document_#{i}",
493
+ db[:people].insert document_number: "document_#{i}",
486
494
  first_name: "John #{i}",
487
495
  last_name: "Doe #{i}",
488
496
  birth_date: Time.now - i,
@@ -513,10 +521,10 @@ describe 'Collection' do
513
521
 
514
522
  comment.post_id.must_equal 1
515
523
  comment.user_id.must_equal i
516
-
524
+
517
525
  comment.user.id.must_equal i
518
526
  comment.user.name.must_equal "User #{i}"
519
-
527
+
520
528
  comment.user.posts.count.must_equal 1
521
529
  comment.user.posts[0].id.must_equal i
522
530
  comment.user.posts[0].title.must_equal "Post #{i}"
@@ -536,7 +544,7 @@ describe 'Collection' do
536
544
  stubs = Proc.new do |sql|
537
545
  case sql
538
546
 
539
- when 'SELECT users.* FROM schema_1.users',
547
+ when 'SELECT users.* FROM schema_1.users',
540
548
  'SELECT users.* FROM schema_1.users WHERE (users.id IN (2, 1))'
541
549
  [
542
550
  {id: 1},
@@ -580,7 +588,7 @@ describe 'Collection' do
580
588
  Sequel.mock fetch: stubs, autoid: autoid
581
589
  end
582
590
 
583
- let :stub_environment do
591
+ let :stub_environment do
584
592
  Rasti::DB::Environment.new default: Rasti::DB::DataSource.new(stub_db, :schema_1),
585
593
  custom: Rasti::DB::DataSource.new(stub_db, :schema_2)
586
594
  end
@@ -619,7 +627,7 @@ describe 'Collection' do
619
627
 
620
628
  stub_users.insert name: 'User 1'
621
629
 
622
- stub_people.insert document_number: 'document_1',
630
+ stub_people.insert document_number: 'document_1',
623
631
  first_name: 'John',
624
632
  last_name: 'Doe',
625
633
  birth_date: Time.parse('2020-04-24'),
@@ -672,7 +680,7 @@ describe 'Collection' do
672
680
 
673
681
  stub_db.sqls.must_equal [
674
682
  'SELECT posts.* FROM schema_1.posts',
675
- 'SELECT categories.*, categories_posts.post_id AS source_foreign_key FROM schema_1.categories INNER JOIN schema_1.categories_posts ON (schema_1.categories_posts.category_id = schema_1.categories.id) WHERE (categories_posts.post_id IN (3, 4))',
683
+ 'SELECT categories.*, categories_posts.post_id AS source_foreign_key FROM schema_1.categories INNER JOIN schema_1.categories_posts ON (schema_1.categories_posts.category_id = schema_1.categories.id) WHERE (categories_posts.post_id IN (3, 4))',
676
684
  'SELECT comments.* FROM schema_1.comments WHERE (comments.post_id IN (3, 4))',
677
685
  'SELECT users.* FROM schema_1.users WHERE (users.id IN (2, 1))',
678
686
  'SELECT posts.* FROM schema_1.posts WHERE (posts.user_id IN (1, 2))',
@@ -10,12 +10,13 @@ require 'sequel/extensions/pg_array'
10
10
  require 'sequel/extensions/pg_json'
11
11
 
12
12
  Rasti::DB.configure do |config|
13
- config.type_converters = [Rasti::DB::TypeConverters::TimeInZone]
13
+ config.type_converters = [Rasti::DB::TypeConverters::TimeInZone, Rasti::DB::TypeConverters::SQLite]
14
+ config.nql_filter_condition_strategy = Rasti::DB::NQL::FilterConditionStrategies::SQLite.new
14
15
  end
15
16
 
16
17
  User = Rasti::DB::Model[:id, :name, :posts, :comments, :person, :comments_count]
17
18
  Post = Rasti::DB::Model[:id, :title, :body, :user_id, :user, :comments, :categories, :language_id, :language, :notice, :author]
18
- Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post]
19
+ Comment = Rasti::DB::Model[:id, :text, :user_id, :user, :post_id, :post, :tags]
19
20
  Category = Rasti::DB::Model[:id, :name, :posts]
20
21
  Person = Rasti::DB::Model[:document_number, :first_name, :last_name, :birth_date, :user_id, :user, :languages, :full_name]
21
22
  Language = Rasti::DB::Model[:id, :name, :people]
@@ -148,6 +149,7 @@ class Minitest::Spec
148
149
  db.create_table :comments do
149
150
  primary_key :id
150
151
  String :text, null: false
152
+ String :tags, default: Sequel.lit("'[]'")
151
153
  foreign_key :user_id, :users, null: false, index: true
152
154
  foreign_key :post_id, :posts, null: false, index: true
153
155
  end
@@ -19,13 +19,30 @@ describe 'NQL::FilterCondition' do
19
19
  def assert_comparison(filter, expected_left, expected_comparator, expected_right)
20
20
  filter.must_be_instance_of Sequel::SQL::BooleanExpression
21
21
  filter.op.must_equal expected_comparator.to_sym
22
-
22
+
23
23
  left, right = filter.args
24
24
  assert_identifier left, expected_left
25
25
 
26
26
  right.must_equal expected_right
27
27
  end
28
28
 
29
+ describe 'None Filter Condition Strategy Validation' do
30
+
31
+ before do
32
+ Rasti::DB.nql_filter_condition_strategy = nil
33
+ end
34
+
35
+ after do
36
+ Rasti::DB.nql_filter_condition_strategy = Rasti::DB::NQL::FilterConditionStrategies::SQLite.new
37
+ end
38
+
39
+ it 'must raise error' do
40
+ error = proc { filter_condition 'column = value' }.must_raise RuntimeError
41
+ error.message.must_equal 'Undefined Rasti::DB.nql_filter_condition_strategy'
42
+ end
43
+
44
+ end
45
+
29
46
  describe 'Comparison' do
30
47
 
31
48
  it 'must create filter from expression with <' do
@@ -134,7 +151,7 @@ describe 'NQL::FilterCondition' do
134
151
 
135
152
  filter.must_be_instance_of Sequel::SQL::BooleanExpression
136
153
  filter.op.must_equal :AND
137
-
154
+
138
155
  major_expression, and_expression = filter.args
139
156
  assert_comparison major_expression, 'column_one', '>', 1
140
157