rasti-db 2.2.0 → 2.3.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rasti/db.rb +11 -1
  3. data/lib/rasti/db/nql/filter_condition_strategies/base.rb +17 -0
  4. data/lib/rasti/db/nql/filter_condition_strategies/postgres.rb +21 -0
  5. data/lib/rasti/db/nql/filter_condition_strategies/sqlite.rb +21 -0
  6. data/lib/rasti/db/nql/filter_condition_strategies/types/generic.rb +49 -0
  7. data/lib/rasti/db/nql/filter_condition_strategies/types/pg_array.rb +32 -0
  8. data/lib/rasti/db/nql/filter_condition_strategies/types/sqlite_array.rb +34 -0
  9. data/lib/rasti/db/nql/filter_condition_strategies/unsupported_type_comparison.rb +22 -0
  10. data/lib/rasti/db/nql/nodes/array_content.rb +21 -0
  11. data/lib/rasti/db/nql/nodes/comparisons/base.rb +10 -0
  12. data/lib/rasti/db/nql/nodes/comparisons/equal.rb +0 -4
  13. data/lib/rasti/db/nql/nodes/comparisons/greater_than.rb +0 -4
  14. data/lib/rasti/db/nql/nodes/comparisons/greater_than_or_equal.rb +0 -4
  15. data/lib/rasti/db/nql/nodes/comparisons/include.rb +0 -4
  16. data/lib/rasti/db/nql/nodes/comparisons/less_than.rb +0 -4
  17. data/lib/rasti/db/nql/nodes/comparisons/less_than_or_equal.rb +0 -4
  18. data/lib/rasti/db/nql/nodes/comparisons/like.rb +0 -4
  19. data/lib/rasti/db/nql/nodes/comparisons/not_equal.rb +0 -4
  20. data/lib/rasti/db/nql/nodes/comparisons/not_include.rb +0 -4
  21. data/lib/rasti/db/nql/nodes/constants/array.rb +17 -0
  22. data/lib/rasti/db/nql/nodes/constants/base.rb +17 -0
  23. data/lib/rasti/db/nql/nodes/constants/false.rb +1 -1
  24. data/lib/rasti/db/nql/nodes/constants/float.rb +1 -1
  25. data/lib/rasti/db/nql/nodes/constants/integer.rb +1 -1
  26. data/lib/rasti/db/nql/nodes/constants/literal_string.rb +1 -1
  27. data/lib/rasti/db/nql/nodes/constants/string.rb +1 -1
  28. data/lib/rasti/db/nql/nodes/constants/time.rb +1 -1
  29. data/lib/rasti/db/nql/nodes/constants/true.rb +1 -1
  30. data/lib/rasti/db/nql/syntax.rb +229 -11
  31. data/lib/rasti/db/nql/syntax.treetop +24 -11
  32. data/lib/rasti/db/type_converters/sqlite.rb +62 -0
  33. data/lib/rasti/db/type_converters/sqlite_types/array.rb +34 -0
  34. data/lib/rasti/db/version.rb +1 -1
  35. data/rasti-db.gemspec +1 -0
  36. data/spec/collection_spec.rb +8 -0
  37. data/spec/minitest_helper.rb +4 -2
  38. data/spec/nql/filter_condition_spec.rb +19 -2
  39. data/spec/nql/filter_condition_strategies_spec.rb +112 -0
  40. data/spec/nql/syntax_parser_spec.rb +24 -0
  41. data/spec/query_spec.rb +89 -0
  42. data/spec/type_converters/sqlite_spec.rb +66 -0
  43. metadata +32 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9da5c8d1c2a7912585a7b1f43e9f3ded9dc859e362ce071c692659e4f98b4ba
4
- data.tar.gz: fa12eafca7db2b3b540871e8701218d3bb176a7cbd3b1c36d57b8fef4884064f
3
+ metadata.gz: 057df50ee7debeb86b2ad63d4dffc965df8941e0f19ac4d4f96fe97ca319c314
4
+ data.tar.gz: c0cda671e2ea8f748b59c34108440edc7e016702f4d5832efac995a650005e62
5
5
  SHA512:
6
- metadata.gz: cd5dbacac1bf5afe377e2c3078691e8b77afed7c1fe0fa55a25e1c446f698c91630bb07045791d1aacaad45fef2967c27b2a316368083a4e27d0fd45f36c6955
7
- data.tar.gz: 83a7a7048def925647078c13cfe241ca3f082cb0ab41a377d9d58fef69de1ffc06fcd781f62e51b7677bf3ffb1d60889a8d88941bc3e44c3127f656e1af720d9
6
+ metadata.gz: d31034d885d8791bb04a2417372d56226c51b4fe8c54279d294148b22c68a7744f12887a948692f12e0b24c1a4abf8868b3c9250948ffb516e2c2515b42ea0aa
7
+ data.tar.gz: b75bc69a65eb10032f510ff5cb99cd511b69bc354c6bc3bc836e23612a0a21f9cc0a01849b752d58b1e86c0b9387adcb665ad609198495120a1da0c6fa2418a7
@@ -6,20 +6,25 @@ require 'treetop'
6
6
  require 'hierarchical_graph'
7
7
  require 'class_config'
8
8
  require 'hash_ext'
9
+ require 'inflecto'
9
10
  require 'multi_require'
10
11
 
11
12
  module Rasti
12
13
  module DB
13
-
14
+
14
15
  extend MultiRequire
15
16
  extend ClassConfig
16
17
 
17
18
  require_relative 'db/query'
18
19
  require_relative_pattern 'db/relations/*'
19
20
  require_relative_pattern 'db/type_converters/postgres_types/*'
21
+ require_relative_pattern 'db/type_converters/sqlite_types/*'
22
+ require_relative 'db/nql/nodes/constants/base'
23
+ require_relative_pattern 'db/nql/filter_condition_strategies/types/*'
20
24
  require_relative_pattern 'db/**/*'
21
25
 
22
26
  attr_config :type_converters, []
27
+ attr_config :nql_filter_condition_strategy, nil
23
28
 
24
29
  def self.to_db(db, collection_name, attribute_name, value)
25
30
  type_converters.inject(value) do |result, type_converter|
@@ -33,5 +38,10 @@ module Rasti
33
38
  end
34
39
  end
35
40
 
41
+ def self.nql_filter_condition_for(comparison_name, identifier, argument)
42
+ raise 'Undefined Rasti::DB.nql_filter_condition_strategy' unless nql_filter_condition_strategy
43
+ nql_filter_condition_strategy.filter_condition_for comparison_name, identifier, argument
44
+ end
45
+
36
46
  end
37
47
  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
@@ -0,0 +1,22 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module FilterConditionStrategies
5
+ class UnsupportedTypeComparison < StandardError
6
+
7
+ attr_reader :argument_type, :comparison_name
8
+
9
+ def initialize(argument_type, comparison_name)
10
+ @argument_type = argument_type
11
+ @comparison_name = comparison_name
12
+ end
13
+
14
+ def message
15
+ "Unsupported comparison #{comparison_name} for #{argument_type}"
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module Nodes
5
+ class ArrayContent < Treetop::Runtime::SyntaxNode
6
+
7
+ def values
8
+ [left.value] + right_value
9
+ end
10
+
11
+ private
12
+
13
+ def right_value
14
+ right.is_a?(self.class) ? right.values : [right.value]
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -13,6 +13,16 @@ module Rasti
13
13
  attribute.computed_attributes(collection_class)
14
14
  end
15
15
 
16
+ def filter_condition(collection_class)
17
+ DB.nql_filter_condition_for comparison_name, attribute.identifier(collection_class), argument
18
+ end
19
+
20
+ private
21
+
22
+ def comparison_name
23
+ Inflecto.underscore(Inflecto.demodulize(self.class)).to_sym
24
+ end
25
+
16
26
  end
17
27
  end
18
28
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class Equal < Base
7
7
 
8
- def filter_condition(collection_class)
9
- { attribute.identifier(collection_class) => argument.value }
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class GreaterThan < Base
7
7
 
8
- def filter_condition(collection_class)
9
- attribute.identifier(collection_class) > argument.value
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class GreaterThanOrEqual < Base
7
7
 
8
- def filter_condition(collection_class)
9
- attribute.identifier(collection_class) >= argument.value
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class Include < Base
7
7
 
8
- def filter_condition(collection_class)
9
- Sequel.ilike(attribute.identifier(collection_class), "%#{argument.value}%")
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class LessThan < Base
7
7
 
8
- def filter_condition(collection_class)
9
- attribute.identifier(collection_class) < argument.value
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class LessThanOrEqual < Base
7
7
 
8
- def filter_condition(collection_class)
9
- attribute.identifier(collection_class) <= argument.value
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class Like < Base
7
7
 
8
- def filter_condition(collection_class)
9
- Sequel.ilike(attribute.identifier(collection_class), argument.value)
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class NotEqual < Base
7
7
 
8
- def filter_condition(collection_class)
9
- Sequel.negate(attribute.identifier(collection_class) => argument.value)
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -5,10 +5,6 @@ module Rasti
5
5
  module Comparisons
6
6
  class NotInclude < Base
7
7
 
8
- def filter_condition(collection_class)
9
- ~ Sequel.ilike(attribute.identifier(collection_class), "%#{argument.value}%")
10
- end
11
-
12
8
  end
13
9
  end
14
10
  end
@@ -0,0 +1,17 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module Nodes
5
+ module Constants
6
+ class Array < Base
7
+
8
+ def value
9
+ contents.is_a?(ArrayContent) ? contents.values : [contents.value]
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Rasti
2
+ module DB
3
+ module NQL
4
+ module Nodes
5
+ module Constants
6
+ class Base < Treetop::Runtime::SyntaxNode
7
+
8
+ def type
9
+ Inflecto.underscore(Inflecto.demodulize(self.class)).to_sym
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -3,7 +3,7 @@ module Rasti
3
3
  module NQL
4
4
  module Nodes
5
5
  module Constants
6
- class False < Treetop::Runtime::SyntaxNode
6
+ class False < Base
7
7
 
8
8
  def value
9
9
  false