rasti-db 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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