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,31 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Expressions
5
- class WithOptions
6
- WITH_OPTIONS = Grumlin.definitions.dig(:expressions, :with_options).freeze
3
+ class Grumlin::Expressions::WithOptions
4
+ WITH_OPTIONS = Grumlin.definitions.dig(:expressions, :with_options).freeze
7
5
 
8
- class << self
9
- WITH_OPTIONS.each do |k, v|
10
- define_method k do
11
- name = "@#{k}"
12
- return instance_variable_get(name) if instance_variable_defined?(name)
6
+ class << self
7
+ WITH_OPTIONS.each do |k, v|
8
+ define_method k do
9
+ name = "@#{k}"
10
+ return instance_variable_get(name) if instance_variable_defined?(name)
13
11
 
14
- instance_variable_set(name, WithOptions.new(k, v))
15
- end
16
- end
12
+ instance_variable_set(name, WithOptions.new(k, v))
17
13
  end
14
+ end
15
+ end
18
16
 
19
- attr_reader :name, :value
17
+ attr_reader :name, :value
20
18
 
21
- def initialize(name, value)
22
- @name = name
23
- @value = value
24
- end
19
+ def initialize(name, value)
20
+ @name = name
21
+ @value = value
22
+ end
25
23
 
26
- def to_s
27
- "WithOptions.#{@name}"
28
- end
29
- end
24
+ def to_s
25
+ "WithOptions.#{@name}"
30
26
  end
31
27
  end
@@ -1,19 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Features
5
- class FeatureList
6
- def user_supplied_ids?
7
- raise(NotImplementedError) if @user_supplied_ids.nil?
3
+ class Grumlin::Features::FeatureList
4
+ def user_supplied_ids?
5
+ raise(NotImplementedError) if @user_supplied_ids.nil?
8
6
 
9
- @user_supplied_ids
10
- end
7
+ @user_supplied_ids
8
+ end
11
9
 
12
- def supports_transactions?
13
- raise(NotImplementedError) if @supports_transactions.nil?
10
+ def supports_transactions?
11
+ raise(NotImplementedError) if @supports_transactions.nil?
14
12
 
15
- @supports_transactions
16
- end
17
- end
13
+ @supports_transactions
18
14
  end
19
15
  end
@@ -1,13 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Features
5
- class NeptuneFeatures < FeatureList
6
- def initialize
7
- super
8
- @user_supplied_ids = true
9
- @supports_transactions = true
10
- end
11
- end
3
+ class Grumlin::Features::NeptuneFeatures < Grumlin::Features::FeatureList
4
+ def initialize
5
+ super
6
+ @user_supplied_ids = true
7
+ @supports_transactions = true
12
8
  end
13
9
  end
@@ -1,13 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Features
5
- class TinkergraphFeatures < FeatureList
6
- def initialize
7
- super
8
- @user_supplied_ids = true
9
- @supports_transactions = false
10
- end
11
- end
3
+ class Grumlin::Features::TinkergraphFeatures < Grumlin::Features::FeatureList
4
+ def initialize
5
+ super
6
+ @user_supplied_ids = true
7
+ @supports_transactions = false
12
8
  end
13
9
  end
@@ -1,16 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Features
5
- class << self
6
- FEATURES = {
7
- neptune: NeptuneFeatures.new,
8
- tinkergraph: TinkergraphFeatures.new
9
- }.freeze
3
+ module Grumlin::Features
4
+ class << self
5
+ FEATURES = {
6
+ neptune: NeptuneFeatures.new,
7
+ tinkergraph: TinkergraphFeatures.new
8
+ }.freeze
10
9
 
11
- def for(provider)
12
- FEATURES[provider]
13
- end
10
+ def for(provider)
11
+ FEATURES[provider]
14
12
  end
15
13
  end
16
14
  end
@@ -1,12 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Middlewares
5
- class ApplyShortcuts < Middleware
6
- def call(env)
7
- env[:steps_without_shortcuts] = ShortcutsApplyer.call(env[:steps])
8
- @app.call(env)
9
- end
10
- end
3
+ class Grumlin::Middlewares::ApplyShortcuts < Grumlin::Middlewares::Middleware
4
+ def call(env)
5
+ env[:steps_without_shortcuts] = Grumlin::ShortcutsApplyer.call(env[:steps])
6
+ @app.call(env)
11
7
  end
12
8
  end
@@ -1,24 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Middlewares
5
- class BuildQuery < Middleware
6
- def call(env)
7
- env[:query] = {
8
- requestId: SecureRandom.uuid,
9
- op: :bytecode,
10
- processor: env[:session_id] ? :session : :traversal,
11
- args: {
12
- gremlin: {
13
- :@type => "g:Bytecode",
14
- :@value => env[:bytecode]
15
- },
16
- aliases: { g: :g },
17
- session: env[:session_id]
18
- }.compact
19
- }
20
- @app.call(env)
21
- end
22
- end
3
+ class Grumlin::Middlewares::BuildQuery < Grumlin::Middlewares::Middleware
4
+ def call(env)
5
+ env[:query] = {
6
+ requestId: SecureRandom.uuid,
7
+ op: :bytecode,
8
+ processor: env[:session_id] ? :session : :traversal,
9
+ args: {
10
+ gremlin: {
11
+ :@type => "g:Bytecode",
12
+ :@value => env[:bytecode]
13
+ },
14
+ aliases: { g: :g },
15
+ session: env[:session_id]
16
+ }.compact
17
+ }
18
+ @app.call(env)
23
19
  end
24
20
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Grumlin::Middlewares::Builder < ::Middleware::Builder
4
+ def similar?(other)
5
+ stack == other.stack
6
+ end
7
+
8
+ def include?(middleware)
9
+ stack.any? { |m| m.first == middleware }
10
+ end
11
+
12
+ def to_app
13
+ @to_app ||= super
14
+ end
15
+ end
@@ -1,11 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Middlewares
5
- class CastResults < Middleware
6
- def call(env)
7
- env[:parsed_results] = @app.call(env).flat_map { |item| Typing.cast(item) }
8
- end
9
- end
3
+ class Grumlin::Middlewares::CastResults < Grumlin::Middlewares::Middleware
4
+ def call(env)
5
+ env[:parsed_results] = @app.call(env).flat_map { |item| Grumlin::Typing.cast(item) }
10
6
  end
11
7
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Grumlin::Middlewares::FindBlocklistedSteps < Grumlin::Middlewares::Middleware
4
+ def initialize(app, *steps)
5
+ super(app)
6
+ @validator = Grumlin::QueryValidators::BlocklistedStepsValidator.new(*steps)
7
+ end
8
+
9
+ def call(env)
10
+ @validator.validate!(env[:steps_without_shortcuts])
11
+ @app.call(env)
12
+ env[:parsed_results] = @app.call(env).flat_map { |item| Grumlin::Typing.cast(item) }
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Grumlin::Middlewares::FindMutatingSteps < Grumlin::Middlewares::FindBlocklistedSteps
4
+ MUTATING_STEPS = [:addV, :addE, :property, :drop].freeze
5
+
6
+ def initialize(app)
7
+ super(app, *MUTATING_STEPS)
8
+ end
9
+ end
@@ -1,15 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Middlewares
5
- class Middleware
6
- def initialize(app)
7
- @app = app
8
- end
3
+ class Grumlin::Middlewares::Middleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
9
7
 
10
- def call(env)
11
- raise NotImplementedError
12
- end
13
- end
8
+ def call(env)
9
+ raise NotImplementedError
14
10
  end
15
11
  end
@@ -1,11 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Middlewares
5
- class RunQuery < Middleware
6
- def call(env)
7
- env[:results] = env[:pool].acquire { |c| c.write(env[:query]) }
8
- end
9
- end
3
+ class Grumlin::Middlewares::RunQuery < Grumlin::Middlewares::Middleware
4
+ def call(env)
5
+ env[:results] = env[:pool].acquire { |c| c.write(env[:query]) }
10
6
  end
11
7
  end
@@ -1,13 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Middlewares
5
- class SerializeToBytecode < Middleware
6
- def call(env)
7
- env[:bytecode] = StepsSerializers::Bytecode.new(env[:steps_without_shortcuts],
8
- no_return: !env[:need_results]).serialize
9
- @app.call(env)
10
- end
11
- end
3
+ class Grumlin::Middlewares::SerializeToBytecode < Grumlin::Middlewares::Middleware
4
+ def call(env)
5
+ env[:bytecode] = Grumlin::StepsSerializers::Bytecode.new(env[:steps_without_shortcuts],
6
+ no_return: !env[:need_results]).serialize
7
+ @app.call(env)
12
8
  end
13
9
  end
@@ -1,12 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Middlewares
5
- class SerializeToSteps < Middleware
6
- def call(env)
7
- env[:steps] = Steps.from(env[:traversal])
8
- @app.call(env)
9
- end
10
- end
3
+ class Grumlin::Middlewares::SerializeToSteps < Grumlin::Middlewares::Middleware
4
+ def call(env)
5
+ env[:steps] = Grumlin::Steps.from(env[:traversal])
6
+ @app.call(env)
11
7
  end
12
8
  end
data/lib/grumlin/path.rb CHANGED
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Path
5
- attr_reader :objects
3
+ class Grumlin::Path
4
+ attr_reader :objects
6
5
 
7
- def initialize(path)
8
- @labels = Typing.cast(path[:labels])
9
- @objects = Typing.cast(path[:objects])
10
- end
6
+ def initialize(path)
7
+ @labels = Grumlin::Typing.cast(path[:labels])
8
+ @objects = Grumlin::Typing.cast(path[:objects])
9
+ end
11
10
 
12
- def inspect
13
- "p[#{@objects}]"
14
- end
11
+ def inspect
12
+ "p[#{@objects}]"
13
+ end
15
14
 
16
- def to_s
17
- inspect
18
- end
15
+ def to_s
16
+ inspect
19
17
  end
20
18
  end
@@ -1,24 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Property
5
- attr_reader :key, :value
3
+ class Grumlin::Property
4
+ attr_reader :key, :value
6
5
 
7
- def initialize(value)
8
- @key = value[:key]
9
- @value = Typing.cast(value[:value])
10
- end
6
+ def initialize(value)
7
+ @key = value[:key]
8
+ @value = Grumlin::Typing.cast(value[:value])
9
+ end
11
10
 
12
- def inspect
13
- "p[#{key}->#{value}]"
14
- end
11
+ def inspect
12
+ "p[#{key}->#{value}]"
13
+ end
15
14
 
16
- def to_s
17
- inspect
18
- end
15
+ def to_s
16
+ inspect
17
+ end
19
18
 
20
- def ==(other)
21
- self.class == other.class && @key == other.key && @value == other.value
22
- end
19
+ def ==(other)
20
+ self.class == other.class && @key == other.key && @value == other.value
23
21
  end
24
22
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Grumlin::QueryValidators::BlocklistedStepsValidator < Grumlin::QueryValidators::Validator
4
+ def initialize(*names)
5
+ super()
6
+ @names = names.to_set
7
+ end
8
+
9
+ protected
10
+
11
+ def validate(steps, errors)
12
+ (steps.configuration_steps + steps.steps).each do |step|
13
+ if @names.include?(step.name)
14
+ errors[:blocklisted_steps] ||= []
15
+ errors[:blocklisted_steps] << step.name
16
+ end
17
+ step.args.each do |arg|
18
+ validate(arg, errors) if arg.is_a?(Grumlin::Steps)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Grumlin::QueryValidators::Validator
4
+ class ValidationError < Grumlin::Error
5
+ attr_reader :errors, :steps
6
+
7
+ def initialize(steps, errors)
8
+ super("Query is invalid: #{errors}")
9
+ @steps = steps
10
+ @errors = errors
11
+ end
12
+ end
13
+
14
+ # steps is an instance of `Steps` after shortcuts applied
15
+ def validate!(steps)
16
+ return unless (err = errors(steps)).any?
17
+
18
+ raise ValidationError.new(steps, err)
19
+ end
20
+
21
+ def valid?(steps)
22
+ errors(steps).empty?
23
+ end
24
+
25
+ protected
26
+
27
+ def errors(steps)
28
+ {}.tap do |errors|
29
+ validate(steps, errors)
30
+ end
31
+ end
32
+
33
+ def validate(steps, errors)
34
+ raise NotImplementedError
35
+ end
36
+ end
@@ -1,44 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Repository
5
- class ErrorHandlingStrategy
6
- def initialize(mode: :retry, **params)
7
- @mode = mode
8
- @params = params
9
- @on_exceptions = params[:on]
10
- end
11
-
12
- def raise?
13
- @mode == :raise
14
- end
15
-
16
- def ignore?
17
- @mode == :ignore
18
- end
19
-
20
- def retry?
21
- @mode == :retry
22
- end
23
-
24
- def apply!(&block)
25
- return yield if raise?
26
- return ignore_errors!(&block) if ignore?
27
-
28
- retry_errors!(&block)
29
- end
30
-
31
- private
32
-
33
- def ignore_errors!
34
- yield
35
- rescue *@on_exceptions
36
- # ignore errors
37
- end
38
-
39
- def retry_errors!(&block)
40
- Retryable.retryable(**@params, &block)
41
- end
42
- end
3
+ class Grumlin::Repository::ErrorHandlingStrategy
4
+ def initialize(mode: :retry, **params)
5
+ @mode = mode
6
+ @params = params
7
+ @on_exceptions = params[:on]
8
+ end
9
+
10
+ def raise?
11
+ @mode == :raise
12
+ end
13
+
14
+ def ignore?
15
+ @mode == :ignore
16
+ end
17
+
18
+ def retry?
19
+ @mode == :retry
20
+ end
21
+
22
+ def apply!(&block)
23
+ return yield if raise?
24
+ return ignore_errors!(&block) if ignore?
25
+
26
+ retry_errors!(&block)
27
+ end
28
+
29
+ private
30
+
31
+ def ignore_errors!
32
+ yield
33
+ rescue *@on_exceptions
34
+ # ignore errors
35
+ end
36
+
37
+ def retry_errors!(&block)
38
+ Retryable.retryable(**@params, &block)
43
39
  end
44
40
  end