grumlin 0.23.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -9
  3. data/Gemfile.lock +1 -1
  4. data/README.md +94 -141
  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 +18 -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. metadata +9 -6
  77. data/lib/grumlin/middlewares/frozen_builder.rb +0 -18
  78. 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