grumlin 0.23.0 → 1.0.0.rc2

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -9
  3. data/Gemfile.lock +1 -1
  4. data/README.md +100 -142
  5. data/Rakefile +1 -1
  6. data/bin/console +18 -3
  7. data/doc/middlewares.md +49 -10
  8. data/lib/async/channel.rb +54 -56
  9. data/lib/grumlin/benchmark/repository.rb +10 -14
  10. data/lib/grumlin/client.rb +93 -95
  11. data/lib/grumlin/config.rb +33 -33
  12. data/lib/grumlin/dummy_transaction.rb +13 -15
  13. data/lib/grumlin/edge.rb +18 -20
  14. data/lib/grumlin/expressions/cardinality.rb +5 -9
  15. data/lib/grumlin/expressions/column.rb +5 -9
  16. data/lib/grumlin/expressions/expression.rb +7 -11
  17. data/lib/grumlin/expressions/operator.rb +5 -9
  18. data/lib/grumlin/expressions/order.rb +5 -9
  19. data/lib/grumlin/expressions/p.rb +27 -31
  20. data/lib/grumlin/expressions/pop.rb +5 -9
  21. data/lib/grumlin/expressions/scope.rb +5 -9
  22. data/lib/grumlin/expressions/t.rb +5 -9
  23. data/lib/grumlin/expressions/text_p.rb +5 -9
  24. data/lib/grumlin/expressions/with_options.rb +17 -21
  25. data/lib/grumlin/features/feature_list.rb +8 -12
  26. data/lib/grumlin/features/neptune_features.rb +5 -9
  27. data/lib/grumlin/features/tinkergraph_features.rb +5 -9
  28. data/lib/grumlin/features.rb +8 -10
  29. data/lib/grumlin/middlewares/apply_shortcuts.rb +4 -8
  30. data/lib/grumlin/middlewares/build_query.rb +16 -20
  31. data/lib/grumlin/middlewares/builder.rb +15 -0
  32. data/lib/grumlin/middlewares/cast_results.rb +3 -7
  33. data/lib/grumlin/middlewares/find_blocklisted_steps.rb +14 -0
  34. data/lib/grumlin/middlewares/find_mutating_steps.rb +9 -0
  35. data/lib/grumlin/middlewares/middleware.rb +6 -10
  36. data/lib/grumlin/middlewares/run_query.rb +3 -7
  37. data/lib/grumlin/middlewares/serialize_to_bytecode.rb +5 -9
  38. data/lib/grumlin/middlewares/serialize_to_steps.rb +4 -8
  39. data/lib/grumlin/path.rb +11 -13
  40. data/lib/grumlin/property.rb +14 -16
  41. data/lib/grumlin/query_validators/blocklisted_steps_validator.rb +22 -0
  42. data/lib/grumlin/query_validators/validator.rb +36 -0
  43. data/lib/grumlin/repository/error_handling_strategy.rb +36 -40
  44. data/lib/grumlin/repository/instance_methods.rb +115 -118
  45. data/lib/grumlin/repository.rb +82 -58
  46. data/lib/grumlin/request_dispatcher.rb +55 -57
  47. data/lib/grumlin/request_error_factory.rb +53 -55
  48. data/lib/grumlin/shortcut.rb +19 -21
  49. data/lib/grumlin/shortcuts/properties.rb +12 -16
  50. data/lib/grumlin/shortcuts/storage.rb +67 -74
  51. data/lib/grumlin/shortcuts/upserts.rb +19 -22
  52. data/lib/grumlin/shortcuts.rb +23 -25
  53. data/lib/grumlin/shortcuts_applyer.rb +27 -29
  54. data/lib/grumlin/step.rb +88 -90
  55. data/lib/grumlin/step_data.rb +12 -14
  56. data/lib/grumlin/steppable.rb +23 -25
  57. data/lib/grumlin/steps.rb +52 -54
  58. data/lib/grumlin/steps_serializers/bytecode.rb +53 -56
  59. data/lib/grumlin/steps_serializers/human_readable_bytecode.rb +17 -21
  60. data/lib/grumlin/steps_serializers/serializer.rb +7 -11
  61. data/lib/grumlin/steps_serializers/string.rb +26 -30
  62. data/lib/grumlin/test/rspec/db_cleaner_context.rb +8 -12
  63. data/lib/grumlin/test/rspec/gremlin_context.rb +18 -16
  64. data/lib/grumlin/test/rspec.rb +1 -5
  65. data/lib/grumlin/transaction.rb +34 -36
  66. data/lib/grumlin/transport.rb +71 -73
  67. data/lib/grumlin/traversal_start.rb +31 -33
  68. data/lib/grumlin/traversal_strategies/options_strategy.rb +3 -7
  69. data/lib/grumlin/traverser.rb +5 -7
  70. data/lib/grumlin/typed_value.rb +11 -13
  71. data/lib/grumlin/typing.rb +70 -72
  72. data/lib/grumlin/version.rb +1 -1
  73. data/lib/grumlin/vertex.rb +14 -16
  74. data/lib/grumlin/vertex_property.rb +14 -16
  75. data/lib/grumlin/with_extension.rb +17 -19
  76. data/lib/grumlin.rb +13 -0
  77. metadata +9 -6
  78. data/lib/grumlin/middlewares/frozen_builder.rb +0 -18
  79. data/lib/grumlin/sugar.rb +0 -15
@@ -1,43 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module StepsSerializers
5
- class String < Serializer
6
- # constructor params: apply_shortcuts: true|false, default: false
7
- # constructor params: anonymous: true|false, default: false
8
- # TODO: add pretty
3
+ class Grumlin::StepsSerializers::String < Grumlin::StepsSerializers::Serializer
4
+ # constructor params: apply_shortcuts: true|false, default: false
5
+ # constructor params: anonymous: true|false, default: false
6
+ # TODO: add pretty
9
7
 
10
- def serialize
11
- steps = @params[:apply_shortcuts] ? ShortcutsApplyer.call(@steps) : @steps
8
+ def serialize
9
+ steps = @params[:apply_shortcuts] ? Grumlin::ShortcutsApplyer.call(@steps) : @steps
12
10
 
13
- steps = [steps.configuration_steps, steps.steps].map do |stps|
14
- stps.map { |step| serialize_step(step) }
15
- end
11
+ steps = [steps.configuration_steps, steps.steps].map do |stps|
12
+ stps.map { |step| serialize_step(step) }
13
+ end
16
14
 
17
- "#{prefix}.#{(steps[0] + steps[1]).join(".")}"
18
- end
15
+ "#{prefix}.#{(steps[0] + steps[1]).join(".")}"
16
+ end
19
17
 
20
- private
18
+ private
21
19
 
22
- def serialize_step(step)
23
- "#{step.name}(#{(step.args + [step.params.any? ? step.params : nil].compact).map do |a|
24
- serialize_arg(a)
25
- end.join(", ")})"
26
- end
20
+ def serialize_step(step)
21
+ "#{step.name}(#{(step.args + [step.params.any? ? step.params : nil].compact).map do |a|
22
+ serialize_arg(a)
23
+ end.join(", ")})"
24
+ end
27
25
 
28
- def prefix
29
- @prefix ||= @params[:anonymous] ? "__" : "g"
30
- end
26
+ def prefix
27
+ @prefix ||= @params[:anonymous] ? "__" : "g"
28
+ end
31
29
 
32
- def serialize_arg(arg)
33
- return "\"#{arg}\"" if arg.is_a?(::String) || arg.is_a?(Symbol)
34
- return "#{arg.type}.#{arg.value}" if arg.is_a?(Grumlin::TypedValue)
35
- return arg.to_s if arg.is_a?(Grumlin::Expressions::WithOptions)
30
+ def serialize_arg(arg)
31
+ return "\"#{arg}\"" if arg.is_a?(::String) || arg.is_a?(Symbol)
32
+ return "#{arg.type}.#{arg.value}" if arg.is_a?(Grumlin::TypedValue)
33
+ return arg.to_s if arg.is_a?(Grumlin::Expressions::WithOptions)
36
34
 
37
- return arg unless arg.is_a?(Steps)
35
+ return arg unless arg.is_a?(Grumlin::Steps)
38
36
 
39
- StepsSerializers::String.new(arg, anonymous: true, **@params).serialize
40
- end
41
- end
37
+ Grumlin::StepsSerializers::String.new(arg, anonymous: true, **@params).serialize
42
38
  end
43
39
  end
@@ -1,19 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Test
5
- module RSpec
6
- module DBCleanerContext
7
- end
3
+ module Grumlin::Test::RSpec
4
+ module DBCleanerContext
5
+ end
8
6
 
9
- ::RSpec.shared_context DBCleanerContext do
10
- include DBCleanerContext
7
+ ::RSpec.shared_context DBCleanerContext do
8
+ include DBCleanerContext
11
9
 
12
- before do
13
- g.E.drop.iterate
14
- g.V.drop.iterate
15
- end
16
- end
10
+ before do
11
+ g.E.drop.iterate
12
+ g.V.drop.iterate
17
13
  end
18
14
  end
19
15
  end
@@ -1,25 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Test
5
- module RSpec
6
- module GremlinContext
7
- end
3
+ module Grumlin::Test::RSpec
4
+ module GremlinContext
5
+ end
8
6
 
9
- ::RSpec.shared_context GremlinContext do
10
- include GremlinContext
11
- include Grumlin::Sugar
7
+ ::RSpec.shared_context GremlinContext do
8
+ include GremlinContext
9
+ include Grumlin::Expressions
12
10
 
13
- before do
14
- Grumlin::Expressions.constants.each do |tool|
15
- stub_const(tool.to_s, Grumlin::Expressions.const_get(tool))
16
- end
17
- end
11
+ [:__, :g].each do |name|
12
+ define_method(name) do |cuts = Grumlin::Shortcuts::Storage.empty|
13
+ cuts.send(name)
14
+ end
15
+ end
18
16
 
19
- after do
20
- Grumlin.close
21
- end
17
+ before do
18
+ Grumlin::Expressions.constants.each do |tool|
19
+ stub_const(tool.to_s, Grumlin::Expressions.const_get(tool))
22
20
  end
23
21
  end
22
+
23
+ after do
24
+ Grumlin.close
25
+ end
24
26
  end
25
27
  end
@@ -3,9 +3,5 @@
3
3
  require "grumlin/test/rspec/db_cleaner_context"
4
4
  require "grumlin/test/rspec/gremlin_context"
5
5
 
6
- module Grumlin
7
- module Test
8
- module RSpec
9
- end
10
- end
6
+ module Grumlin::Test::RSpec
11
7
  end
@@ -1,40 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Transaction
5
- attr_reader :session_id
6
-
7
- include Console
8
-
9
- COMMIT = Grumlin::Repository.new.g.step(:tx, :commit)
10
- ROLLBACK = Grumlin::Repository.new.g.step(:tx, :rollback)
11
-
12
- def initialize(traversal_start_class, pool:, middlewares:)
13
- @traversal_start_class = traversal_start_class
14
- @pool = pool
15
- @session_id = SecureRandom.uuid
16
- @middlewares = middlewares
17
- end
18
-
19
- def begin
20
- @traversal_start_class.new(session_id: @session_id)
21
- end
22
-
23
- def commit
24
- finalize(COMMIT)
25
- end
26
-
27
- def rollback
28
- finalize(ROLLBACK)
29
- end
30
-
31
- private
32
-
33
- def finalize(step)
34
- @middlewares.call(traversal: step,
35
- need_results: false,
36
- session_id: @session_id,
37
- pool: @pool)
38
- end
3
+ class Grumlin::Transaction
4
+ attr_reader :session_id
5
+
6
+ include Console
7
+
8
+ COMMIT = Grumlin::Repository.new.g.step(:tx, :commit)
9
+ ROLLBACK = Grumlin::Repository.new.g.step(:tx, :rollback)
10
+
11
+ def initialize(traversal_start_class, pool:, middlewares:)
12
+ @traversal_start_class = traversal_start_class
13
+ @pool = pool
14
+ @session_id = SecureRandom.uuid
15
+ @middlewares = middlewares
16
+ end
17
+
18
+ def begin
19
+ @traversal_start_class.new(session_id: @session_id)
20
+ end
21
+
22
+ def commit
23
+ finalize(COMMIT)
24
+ end
25
+
26
+ def rollback
27
+ finalize(ROLLBACK)
28
+ end
29
+
30
+ private
31
+
32
+ def finalize(step)
33
+ @middlewares.call(traversal: step,
34
+ need_results: false,
35
+ session_id: @session_id,
36
+ pool: @pool)
39
37
  end
40
38
  end
@@ -1,101 +1,99 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Transport
5
- # A transport based on https://github.com/socketry/async
6
- # and https://github.com/socketry/async-websocket
7
-
8
- include Console
9
-
10
- attr_reader :url
11
-
12
- # Transport is not reusable. Once closed should be recreated.
13
- def initialize(url, parent: Async::Task.current, **client_options)
14
- @url = url
15
- @parent = parent
16
- @client_options = client_options
17
- @request_channel = Async::Channel.new
18
- @response_channel = Async::Channel.new
19
- end
3
+ class Grumlin::Transport
4
+ # A transport based on https://github.com/socketry/async
5
+ # and https://github.com/socketry/async-websocket
20
6
 
21
- def connected?
22
- !@connection.nil?
23
- end
7
+ include Console
24
8
 
25
- def connect
26
- raise ClientClosedError if @closed
27
- raise AlreadyConnectedError if connected?
9
+ attr_reader :url
28
10
 
29
- @connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
30
- logger.debug(self) { "Connected to #{@url}." }
11
+ # Transport is not reusable. Once closed should be recreated.
12
+ def initialize(url, parent: Async::Task.current, **client_options)
13
+ @url = url
14
+ @parent = parent
15
+ @client_options = client_options
16
+ @request_channel = Async::Channel.new
17
+ @response_channel = Async::Channel.new
18
+ end
31
19
 
32
- @response_task = @parent.async { run_response_task }
20
+ def connected?
21
+ !@connection.nil?
22
+ end
33
23
 
34
- @request_task = @parent.async { run_request_task }
24
+ def connect
25
+ raise ClientClosedError if @closed
26
+ raise AlreadyConnectedError if connected?
35
27
 
36
- @response_channel
37
- end
28
+ @connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
29
+ logger.debug(self) { "Connected to #{@url}." }
38
30
 
39
- def write(message)
40
- raise NotConnectedError unless connected?
31
+ @response_task = @parent.async { run_response_task }
41
32
 
42
- @request_channel << message
43
- end
33
+ @request_task = @parent.async { run_request_task }
44
34
 
45
- def close
46
- return if @closed
35
+ @response_channel
36
+ end
47
37
 
48
- @closed = true
38
+ def write(message)
39
+ raise NotConnectedError unless connected?
49
40
 
50
- @request_channel.close
51
- @response_channel.close
41
+ @request_channel << message
42
+ end
52
43
 
53
- begin
54
- @connection.close
55
- rescue StandardError
56
- nil
57
- end
58
- @connection = nil
44
+ def close
45
+ return if @closed
59
46
 
60
- @request_task&.stop(true)
61
- @response_task&.stop(true)
62
- end
47
+ @closed = true
48
+
49
+ @request_channel.close
50
+ @response_channel.close
63
51
 
64
- def wait
65
- @request_task.wait
66
- @response_task.wait
52
+ begin
53
+ @connection.close
54
+ rescue StandardError
55
+ nil
67
56
  end
57
+ @connection = nil
68
58
 
69
- private
59
+ @request_task&.stop(true)
60
+ @response_task&.stop(true)
61
+ end
62
+
63
+ def wait
64
+ @request_task.wait
65
+ @response_task.wait
66
+ end
70
67
 
71
- def run_response_task
72
- with_guard do
73
- loop do
74
- data = @connection.read
75
- @response_channel << data
76
- end
68
+ private
69
+
70
+ def run_response_task
71
+ with_guard do
72
+ loop do
73
+ data = @connection.read
74
+ @response_channel << data
77
75
  end
78
76
  end
77
+ end
79
78
 
80
- def run_request_task
81
- with_guard do
82
- @request_channel.each do |message|
83
- @connection.write(message)
84
- @connection.flush
85
- end
79
+ def run_request_task
80
+ with_guard do
81
+ @request_channel.each do |message|
82
+ @connection.write(message)
83
+ @connection.flush
86
84
  end
87
85
  end
86
+ end
88
87
 
89
- def with_guard
90
- yield
91
- rescue Async::Stop, Async::TimeoutError, StandardError => e
92
- logger.debug(self) { "Guard error, closing." }
93
- begin
94
- @response_channel.exception(e)
95
- rescue Async::Channel::ChannelClosedError
96
- nil
97
- end
98
- close
88
+ def with_guard
89
+ yield
90
+ rescue Async::Stop, Async::TimeoutError, StandardError => e
91
+ logger.debug(self) { "Guard error, closing." }
92
+ begin
93
+ @response_channel.exception(e)
94
+ rescue Async::Channel::ChannelClosedError
95
+ nil
99
96
  end
97
+ close
100
98
  end
101
99
  end
@@ -1,42 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class TraversalStart < Steppable
5
- include WithExtension
6
-
7
- class TraversalError < Grumlin::Error; end
8
- class AlreadyBoundToTransactionError < TraversalError; end
9
-
10
- def tx
11
- raise AlreadyBoundToTransactionError if @session_id
12
-
13
- transaction = tx_class.new(self.class, pool: @pool, middlewares: @middlewares)
14
- return transaction unless block_given?
15
-
16
- begin
17
- yield transaction.begin
18
- rescue Grumlin::Rollback
19
- transaction.rollback
20
- rescue StandardError
21
- transaction.rollback
22
- raise
23
- else
24
- transaction.commit
25
- end
3
+ class Grumlin::TraversalStart < Grumlin::Steppable
4
+ include Grumlin::WithExtension
5
+
6
+ class TraversalError < Grumlin::Error; end
7
+ class AlreadyBoundToTransactionError < TraversalError; end
8
+
9
+ def tx
10
+ raise AlreadyBoundToTransactionError if @session_id
11
+
12
+ transaction = tx_class.new(self.class, pool: @pool, middlewares: @middlewares)
13
+ return transaction unless block_given?
14
+
15
+ begin
16
+ yield transaction.begin
17
+ rescue Grumlin::Rollback
18
+ transaction.rollback
19
+ rescue StandardError
20
+ transaction.rollback
21
+ raise
22
+ else
23
+ transaction.commit
26
24
  end
25
+ end
27
26
 
28
- def to_s(*)
29
- self.class.to_s
30
- end
27
+ def to_s(*)
28
+ self.class.to_s
29
+ end
31
30
 
32
- def inspect
33
- self.class.inspect
34
- end
31
+ def inspect
32
+ self.class.inspect
33
+ end
35
34
 
36
- private
35
+ private
37
36
 
38
- def tx_class
39
- Grumlin.features.supports_transactions? ? Transaction : DummyTransaction
40
- end
37
+ def tx_class
38
+ Grumlin.features.supports_transactions? ? Grumlin::Transaction : Grumlin::DummyTransaction
41
39
  end
42
40
  end
@@ -1,11 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module TraversalStrategies
5
- class OptionsStrategy < TypedValue
6
- def initialize(value)
7
- super(type: "OptionsStrategy", value: value)
8
- end
9
- end
3
+ class Grumlin::TraversalStrategies::OptionsStrategy < Grumlin::TypedValue
4
+ def initialize(value)
5
+ super(type: "OptionsStrategy", value: value)
10
6
  end
11
7
  end
@@ -1,12 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Traverser
5
- attr_reader :bulk, :value
3
+ class Grumlin::Traverser
4
+ attr_reader :bulk, :value
6
5
 
7
- def initialize(value)
8
- @bulk = value.dig(:bulk, :@value) || 1
9
- @value = Typing.cast(value[:value])
10
- end
6
+ def initialize(value)
7
+ @bulk = value.dig(:bulk, :@value) || 1
8
+ @value = Grumlin::Typing.cast(value[:value])
11
9
  end
12
10
  end
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class TypedValue
5
- attr_reader :type, :value
3
+ class Grumlin::TypedValue
4
+ attr_reader :type, :value
6
5
 
7
- def initialize(type: nil, value: nil)
8
- @type = type
9
- @value = value
10
- end
6
+ def initialize(type: nil, value: nil)
7
+ @type = type
8
+ @value = value
9
+ end
11
10
 
12
- def inspect
13
- "<#{type}.#{value}>"
14
- end
11
+ def inspect
12
+ "<#{type}.#{value}>"
13
+ end
15
14
 
16
- def to_s
17
- inspect
18
- end
15
+ def to_s
16
+ inspect
19
17
  end
20
18
  end