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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/lib/rasti/db.rb +16 -5
- data/lib/rasti/db/collection.rb +10 -10
- data/lib/rasti/db/data_source.rb +1 -1
- data/lib/rasti/db/model.rb +3 -104
- data/lib/rasti/db/nql/filter_condition_strategies/base.rb +17 -0
- data/lib/rasti/db/nql/filter_condition_strategies/postgres.rb +21 -0
- data/lib/rasti/db/nql/filter_condition_strategies/sqlite.rb +21 -0
- data/lib/rasti/db/nql/filter_condition_strategies/types/generic.rb +49 -0
- data/lib/rasti/db/nql/filter_condition_strategies/types/pg_array.rb +32 -0
- data/lib/rasti/db/nql/filter_condition_strategies/types/sqlite_array.rb +34 -0
- data/lib/rasti/db/nql/filter_condition_strategies/unsupported_type_comparison.rb +22 -0
- data/lib/rasti/db/nql/nodes/array_content.rb +21 -0
- data/lib/rasti/db/nql/nodes/binary_node.rb +1 -1
- data/lib/rasti/db/nql/nodes/comparisons/base.rb +10 -0
- data/lib/rasti/db/nql/nodes/comparisons/equal.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/greater_than.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/greater_than_or_equal.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/include.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/less_than.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/less_than_or_equal.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/like.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/not_equal.rb +0 -4
- data/lib/rasti/db/nql/nodes/comparisons/not_include.rb +0 -4
- data/lib/rasti/db/nql/nodes/constants/array.rb +17 -0
- data/lib/rasti/db/nql/nodes/constants/base.rb +17 -0
- data/lib/rasti/db/nql/nodes/constants/false.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/float.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/integer.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/literal_string.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/string.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/time.rb +1 -1
- data/lib/rasti/db/nql/nodes/constants/true.rb +1 -1
- data/lib/rasti/db/nql/nodes/parenthesis_sentence.rb +1 -1
- data/lib/rasti/db/nql/syntax.rb +229 -11
- data/lib/rasti/db/nql/syntax.treetop +24 -11
- data/lib/rasti/db/relations/base.rb +3 -3
- data/lib/rasti/db/relations/graph.rb +15 -15
- data/lib/rasti/db/relations/many_to_many.rb +4 -4
- data/lib/rasti/db/relations/many_to_one.rb +4 -4
- data/lib/rasti/db/relations/one_to_many.rb +2 -2
- data/lib/rasti/db/type_converters/postgres.rb +32 -36
- data/lib/rasti/db/type_converters/postgres_types/array.rb +11 -9
- data/lib/rasti/db/type_converters/postgres_types/hstore.rb +10 -9
- data/lib/rasti/db/type_converters/postgres_types/json.rb +17 -14
- data/lib/rasti/db/type_converters/postgres_types/jsonb.rb +17 -14
- data/lib/rasti/db/type_converters/sqlite.rb +62 -0
- data/lib/rasti/db/type_converters/sqlite_types/array.rb +34 -0
- data/lib/rasti/db/type_converters/time_in_zone.rb +1 -1
- data/lib/rasti/db/version.rb +1 -1
- data/rasti-db.gemspec +2 -0
- data/spec/collection_spec.rb +36 -28
- data/spec/minitest_helper.rb +4 -2
- data/spec/nql/filter_condition_spec.rb +19 -2
- data/spec/nql/filter_condition_strategies_spec.rb +112 -0
- data/spec/nql/syntax_parser_spec.rb +24 -0
- data/spec/query_spec.rb +89 -0
- data/spec/relations_spec.rb +17 -17
- data/spec/type_converters/sqlite_spec.rb +66 -0
- data/spec/type_converters/time_in_zone_spec.rb +1 -1
- metadata +46 -4
- data/spec/model_spec.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e15c6a4b9b21397abc6135f83b6b863a8e8147e9bfea492cf25fd1d54db00cb
|
4
|
+
data.tar.gz: 0cebbbf49d1262804bfa559e6c696763761242b46a877c50c5f7dce528c9f16c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 468ca823921d6eaece74f8b0479e25fc767ed4c7e08a63fe24fd633a467ab3469cf199994b8a53e4207505e5090679011a1a5623d52f6322b910d59594a80ce0
|
7
|
+
data.tar.gz: 938c0fea726c3e70c1fe791d3034eae9f11077c2614321eb252556bf7ad5a8a3f8a470bb9dd3b833ad1e84cec57557a703f4f6591ae2e6dc064b163702264bef
|
data/Gemfile
CHANGED
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
|
18
|
-
require_relative_pattern
|
19
|
-
require_relative_pattern
|
20
|
-
require_relative_pattern
|
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
|
data/lib/rasti/db/collection.rb
CHANGED
@@ -14,7 +14,7 @@ module Rasti
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def collection_attributes
|
17
|
-
@collection_attributes ||= model.
|
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
|
data/lib/rasti/db/data_source.rb
CHANGED
data/lib/rasti/db/model.rb
CHANGED
@@ -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
|
-
|
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
|