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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9da5c8d1c2a7912585a7b1f43e9f3ded9dc859e362ce071c692659e4f98b4ba
4
- data.tar.gz: fa12eafca7db2b3b540871e8701218d3bb176a7cbd3b1c36d57b8fef4884064f
3
+ metadata.gz: 9e15c6a4b9b21397abc6135f83b6b863a8e8147e9bfea492cf25fd1d54db00cb
4
+ data.tar.gz: 0cebbbf49d1262804bfa559e6c696763761242b46a877c50c5f7dce528c9f16c
5
5
  SHA512:
6
- metadata.gz: cd5dbacac1bf5afe377e2c3078691e8b77afed7c1fe0fa55a25e1c446f698c91630bb07045791d1aacaad45fef2967c27b2a316368083a4e27d0fd45f36c6955
7
- data.tar.gz: 83a7a7048def925647078c13cfe241ca3f082cb0ab41a377d9d58fef69de1ffc06fcd781f62e51b7677bf3ffb1d60889a8d88941bc3e44c3127f656e1af720d9
6
+ metadata.gz: 468ca823921d6eaece74f8b0479e25fc767ed4c7e08a63fe24fd633a467ab3469cf199994b8a53e4207505e5090679011a1a5623d52f6322b910d59594a80ce0
7
+ data.tar.gz: 938c0fea726c3e70c1fe791d3034eae9f11077c2614321eb252556bf7ad5a8a3f8a470bb9dd3b833ad1e84cec57557a703f4f6591ae2e6dc064b163702264bef
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rasti-db.gemspec
4
- gemspec
4
+ gemspec
data/lib/rasti/db.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'sequel'
2
+ require 'rasti-model'
2
3
  require 'consty'
3
4
  require 'time'
4
5
  require 'timing'
@@ -6,20 +7,25 @@ require 'treetop'
6
7
  require 'hierarchical_graph'
7
8
  require 'class_config'
8
9
  require 'hash_ext'
10
+ require 'inflecto'
9
11
  require 'multi_require'
10
12
 
11
13
  module Rasti
12
14
  module DB
13
-
15
+
14
16
  extend MultiRequire
15
17
  extend ClassConfig
16
18
 
17
- require_relative 'db/query'
18
- require_relative_pattern 'db/relations/*'
19
- require_relative_pattern 'db/type_converters/postgres_types/*'
20
- require_relative_pattern 'db/**/*'
19
+ require_relative 'db/query'
20
+ require_relative_pattern 'db/relations/*'
21
+ require_relative_pattern 'db/type_converters/postgres_types/*'
22
+ require_relative_pattern 'db/type_converters/sqlite_types/*'
23
+ require_relative 'db/nql/nodes/constants/base'
24
+ require_relative_pattern 'db/nql/filter_condition_strategies/types/*'
25
+ require_relative_pattern 'db/**/*'
21
26
 
22
27
  attr_config :type_converters, []
28
+ attr_config :nql_filter_condition_strategy, nil
23
29
 
24
30
  def self.to_db(db, collection_name, attribute_name, value)
25
31
  type_converters.inject(value) do |result, type_converter|
@@ -33,5 +39,10 @@ module Rasti
33
39
  end
34
40
  end
35
41
 
42
+ def self.nql_filter_condition_for(comparison_name, identifier, argument)
43
+ raise 'Undefined Rasti::DB.nql_filter_condition_strategy' unless nql_filter_condition_strategy
44
+ nql_filter_condition_strategy.filter_condition_for comparison_name, identifier, argument
45
+ end
46
+
36
47
  end
37
48
  end
@@ -14,7 +14,7 @@ module Rasti
14
14
  end
15
15
 
16
16
  def collection_attributes
17
- @collection_attributes ||= model.attributes - relations.keys - computed_attributes.keys
17
+ @collection_attributes ||= model.attribute_names - relations.keys - computed_attributes.keys
18
18
  end
19
19
 
20
20
  def primary_key
@@ -89,7 +89,7 @@ module Rasti
89
89
  raise "Query #{name} already exists" if queries.key? name
90
90
 
91
91
  queries[name] = lambda || block
92
-
92
+
93
93
  define_method name do |*args|
94
94
  default_query.instance_exec(*args, &self.class.queries.fetch(name))
95
95
  end
@@ -210,21 +210,21 @@ module Rasti
210
210
  def qualified_collection_name
211
211
  data_source.qualify self.class.collection_name
212
212
  end
213
-
213
+
214
214
  def qualify(collection_name, data_source_name: nil)
215
215
  data_source_name ||= self.class.data_source_name
216
216
  environment.qualify data_source_name, collection_name
217
217
  end
218
218
 
219
219
  def default_query
220
- Query.new collection_class: self.class,
221
- dataset: dataset.select_all(self.class.collection_name),
220
+ Query.new collection_class: self.class,
221
+ dataset: dataset.select_all(self.class.collection_name),
222
222
  environment: environment
223
223
  end
224
224
 
225
225
  def build_query(filter=nil, &block)
226
226
  raise ArgumentError, 'must specify filter hash or block' if filter.nil? && block.nil?
227
-
227
+
228
228
  if filter
229
229
  default_query.where(filter)
230
230
  else
@@ -233,7 +233,7 @@ module Rasti
233
233
  end
234
234
 
235
235
  def transform_attributes_to_db(attributes)
236
- attributes.each_with_object({}) do |(attribute_name, value), result|
236
+ attributes.each_with_object({}) do |(attribute_name, value), result|
237
237
  transformed_value = Rasti::DB.to_db data_source.db, qualified_collection_name, attribute_name, value
238
238
  result[attribute_name] = transformed_value
239
239
  end
@@ -247,7 +247,7 @@ module Rasti
247
247
 
248
248
  [collection_attributes, relations_ids]
249
249
  end
250
-
250
+
251
251
  def save_relations(primary_key, relations_primary_keys)
252
252
  relations_primary_keys.each do |relation_name, relation_primary_keys|
253
253
  relation = self.class.relations[relation_name]
@@ -281,9 +281,9 @@ module Rasti
281
281
  relation_data_source = environment.data_source relation.relation_data_source_name
282
282
  relation_collection_name = relation_data_source.qualify relation.relation_collection_name
283
283
 
284
- values = relation_primary_keys.map do |relation_pk|
284
+ values = relation_primary_keys.map do |relation_pk|
285
285
  {
286
- relation.source_foreign_key => primary_key,
286
+ relation.source_foreign_key => primary_key,
287
287
  relation.target_foreign_key => relation_pk
288
288
  }
289
289
  end
@@ -12,7 +12,7 @@ module Rasti
12
12
  def qualify(collection_name)
13
13
  schema ? Sequel[schema][collection_name] : Sequel[collection_name]
14
14
  end
15
-
15
+
16
16
  end
17
17
  end
18
18
  end
@@ -1,112 +1,11 @@
1
1
  module Rasti
2
2
  module DB
3
- class Model
3
+ class Model < Rasti::Model
4
4
 
5
- class UninitializedAttributeError < StandardError
6
-
7
- attr_reader :attribute
8
-
9
- def initialize(attribute)
10
- @attribute = attribute
11
- super "Uninitialized attribute #{attribute}"
12
- end
13
-
14
- end
15
-
16
-
17
- class << self
18
-
19
- def [](*attribute_names)
20
- Class.new(self) do
21
- attribute(*attribute_names)
22
-
23
- def self.inherited(subclass)
24
- subclass.instance_variable_set :@attributes, attributes.dup
25
- end
26
- end
27
- end
28
-
29
- def attributes
30
- @attributes ||= []
31
- end
32
-
33
- def model_name
34
- name || self.superclass.name
35
- end
36
-
37
- def to_s
38
- "#{model_name}[#{attributes.join(', ')}]"
39
- end
40
- alias_method :inspect, :to_s
41
-
42
- private
43
-
44
- def attribute(*names)
45
- names.each do |name|
46
- raise ArgumentError, "Attribute #{name} already exists" if attributes.include?(name)
47
-
48
- attributes << name
49
-
50
- define_method name do
51
- fetch_attribute name
52
- end
53
- end
54
- end
55
-
56
- end
57
-
58
-
59
- def initialize(attributes)
60
- invalid_attributes = attributes.keys - self.class.attributes
61
- raise "#{self.class.model_name} invalid attributes: #{invalid_attributes.join(', ')}" unless invalid_attributes.empty?
62
- @attributes = attributes
63
- end
64
-
65
- def merge(new_attributes)
66
- self.class.new attributes.merge(new_attributes)
67
- end
68
-
69
- def eql?(other)
70
- instance_of?(other.class) && to_h.eql?(other.to_h)
71
- end
72
-
73
- def ==(other)
74
- other.kind_of?(self.class) && to_h == other.to_h
75
- end
76
-
77
- def hash
78
- attributes.map(&:hash).hash
79
- end
80
-
81
- def to_s
82
- "#<#{self.class.model_name}[#{attributes.map { |n,v| "#{n}: #{v.inspect}" }.join(', ')}]>"
83
- end
84
- alias_method :inspect, :to_s
85
-
86
- def to_h
87
- self.class.attributes.each_with_object({}) do |name, hash|
88
- if attributes.key? name
89
- value = fetch_attribute name
90
- case value
91
- when Model
92
- hash[name] = value.to_h
93
- when Array
94
- hash[name] = value.map do |e|
95
- e.is_a?(Model) ? e.to_h : e
96
- end
97
- else
98
- hash[name] = value
99
- end
100
- end
101
- end
102
- end
103
-
104
5
  private
105
6
 
106
- attr_reader :attributes
107
-
108
- def fetch_attribute(name)
109
- attributes.key?(name) ? Rasti::DB.from_db(attributes[name]) : raise(UninitializedAttributeError, name)
7
+ def cast_attribute(type, value)
8
+ super type, Rasti::DB.from_db(value)
110
9
  end
111
10
 
112
11
  end
@@ -0,0 +1,17 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module FilterConditionStrategies
5
+ class Base
6
+
7
+ def filter_condition_for(comparison_name, identifier, argument)
8
+ type = type_for argument
9
+ raise UnsupportedTypeComparison.new(type, comparison_name) unless type.respond_to?(comparison_name)
10
+ type.public_send comparison_name, identifier, argument.value
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module FilterConditionStrategies
5
+ class Postgres < Base
6
+
7
+ PG_TYPES = {
8
+ array: Types::PGArray
9
+ }
10
+
11
+ private
12
+
13
+ def type_for(argument)
14
+ PG_TYPES.fetch(argument.type, Types::Generic)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module FilterConditionStrategies
5
+ class SQLite < Base
6
+
7
+ SQLITE_TYPES = {
8
+ array: Types::SQLiteArray
9
+ }
10
+
11
+ private
12
+
13
+ def type_for(argument)
14
+ SQLITE_TYPES.fetch(argument.type, Types::Generic)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,49 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module FilterConditionStrategies
5
+ module Types
6
+ class Generic
7
+
8
+ def self.equal(identifier, value)
9
+ {identifier => value}
10
+ end
11
+
12
+ def self.not_equal(identifier, value)
13
+ Sequel.negate equal(identifier, value)
14
+ end
15
+
16
+ def self.greater_than(identifier, value)
17
+ identifier > value
18
+ end
19
+
20
+ def self.greater_than_or_equal(identifier, value)
21
+ identifier >= value
22
+ end
23
+
24
+ def self.less_than(identifier, value)
25
+ identifier < value
26
+ end
27
+
28
+ def self.less_than_or_equal(identifier, value)
29
+ identifier <= value
30
+ end
31
+
32
+ def self.like(identifier, value)
33
+ Sequel.ilike identifier, value
34
+ end
35
+
36
+ def self.include(identifier, value)
37
+ Sequel.ilike identifier, "%#{value}%"
38
+ end
39
+
40
+ def self.not_include(identifier, value)
41
+ ~include(identifier, value)
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,32 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module FilterConditionStrategies
5
+ module Types
6
+ class PGArray
7
+
8
+ def self.equal(identifier, values)
9
+ Sequel.&(
10
+ Sequel.pg_array(identifier).contains(Sequel.pg_array(values)),
11
+ Sequel.pg_array(identifier).contained_by(Sequel.pg_array(values))
12
+ )
13
+ end
14
+
15
+ def self.not_equal(identifier, values)
16
+ ~equal(identifier, values)
17
+ end
18
+
19
+ def self.include(identifier, values)
20
+ Sequel.pg_array(identifier).overlaps Sequel.pg_array(values)
21
+ end
22
+
23
+ def self.not_include(identifier, values)
24
+ ~include(identifier, values)
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module FilterConditionStrategies
5
+ module Types
6
+ class SQLiteArray
7
+
8
+ def self.equal(identifier, values)
9
+ array = values.map { |value| "\"#{value}\"" }.join(',')
10
+ {identifier => "[#{array}]"}
11
+ end
12
+
13
+ def self.not_equal(identifier, values)
14
+ Sequel.|(*values.map { |value| ~Sequel.like(identifier, "%\"#{value}\"%") })
15
+ end
16
+
17
+ def self.like(identifier, values)
18
+ Sequel.|(*values.map { |value| Sequel.like(identifier, "%#{value}%") })
19
+ end
20
+
21
+ def self.include(identifier, values)
22
+ Sequel.|(*values.map { |value| Sequel.like(identifier, "%\"#{value}\"%") })
23
+ end
24
+
25
+ def self.not_include(identifier, values)
26
+ Sequel.&(*values.map { |value| ~Sequel.like(identifier, "%\"#{value}\"%") })
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end