grumlin 0.22.4 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +9 -9
  4. data/Gemfile.lock +10 -8
  5. data/README.md +102 -141
  6. data/Rakefile +1 -1
  7. data/bin/console +18 -3
  8. data/doc/middlewares.md +97 -0
  9. data/grumlin.gemspec +1 -0
  10. data/lib/async/channel.rb +54 -56
  11. data/lib/grumlin/benchmark/repository.rb +10 -14
  12. data/lib/grumlin/client.rb +92 -112
  13. data/lib/grumlin/config.rb +30 -15
  14. data/lib/grumlin/dummy_transaction.rb +13 -15
  15. data/lib/grumlin/edge.rb +18 -20
  16. data/lib/grumlin/expressions/cardinality.rb +5 -9
  17. data/lib/grumlin/expressions/column.rb +5 -9
  18. data/lib/grumlin/expressions/expression.rb +7 -11
  19. data/lib/grumlin/expressions/operator.rb +5 -9
  20. data/lib/grumlin/expressions/order.rb +5 -9
  21. data/lib/grumlin/expressions/p.rb +27 -31
  22. data/lib/grumlin/expressions/pop.rb +5 -9
  23. data/lib/grumlin/expressions/scope.rb +5 -9
  24. data/lib/grumlin/expressions/t.rb +5 -9
  25. data/lib/grumlin/expressions/text_p.rb +5 -9
  26. data/lib/grumlin/expressions/with_options.rb +17 -21
  27. data/lib/grumlin/features/feature_list.rb +8 -12
  28. data/lib/grumlin/features/neptune_features.rb +5 -9
  29. data/lib/grumlin/features/tinkergraph_features.rb +5 -9
  30. data/lib/grumlin/features.rb +8 -10
  31. data/lib/grumlin/middlewares/apply_shortcuts.rb +8 -0
  32. data/lib/grumlin/middlewares/build_query.rb +20 -0
  33. data/lib/grumlin/middlewares/builder.rb +15 -0
  34. data/lib/grumlin/middlewares/cast_results.rb +7 -0
  35. data/lib/grumlin/middlewares/find_blocklisted_steps.rb +14 -0
  36. data/lib/grumlin/middlewares/find_mutating_steps.rb +9 -0
  37. data/lib/grumlin/middlewares/middleware.rb +11 -0
  38. data/lib/grumlin/middlewares/run_query.rb +7 -0
  39. data/lib/grumlin/middlewares/serialize_to_bytecode.rb +9 -0
  40. data/lib/grumlin/middlewares/serialize_to_steps.rb +8 -0
  41. data/lib/grumlin/path.rb +11 -13
  42. data/lib/grumlin/property.rb +14 -16
  43. data/lib/grumlin/query_validators/blocklisted_steps_validator.rb +22 -0
  44. data/lib/grumlin/query_validators/validator.rb +36 -0
  45. data/lib/grumlin/repository/error_handling_strategy.rb +36 -40
  46. data/lib/grumlin/repository/instance_methods.rb +115 -118
  47. data/lib/grumlin/repository.rb +82 -58
  48. data/lib/grumlin/request_dispatcher.rb +55 -57
  49. data/lib/grumlin/request_error_factory.rb +53 -55
  50. data/lib/grumlin/shortcut.rb +19 -21
  51. data/lib/grumlin/shortcuts/properties.rb +12 -16
  52. data/lib/grumlin/shortcuts/storage.rb +67 -74
  53. data/lib/grumlin/shortcuts/upserts.rb +18 -22
  54. data/lib/grumlin/shortcuts.rb +23 -25
  55. data/lib/grumlin/shortcuts_applyer.rb +27 -29
  56. data/lib/grumlin/step.rb +92 -0
  57. data/lib/grumlin/step_data.rb +12 -14
  58. data/lib/grumlin/steppable.rb +24 -22
  59. data/lib/grumlin/steps.rb +51 -54
  60. data/lib/grumlin/steps_serializers/bytecode.rb +53 -56
  61. data/lib/grumlin/steps_serializers/human_readable_bytecode.rb +17 -21
  62. data/lib/grumlin/steps_serializers/serializer.rb +7 -11
  63. data/lib/grumlin/steps_serializers/string.rb +26 -30
  64. data/lib/grumlin/test/rspec/db_cleaner_context.rb +8 -12
  65. data/lib/grumlin/test/rspec/gremlin_context.rb +18 -16
  66. data/lib/grumlin/test/rspec.rb +1 -5
  67. data/lib/grumlin/transaction.rb +26 -27
  68. data/lib/grumlin/transport.rb +71 -73
  69. data/lib/grumlin/traversal_start.rb +31 -33
  70. data/lib/grumlin/traversal_strategies/options_strategy.rb +3 -7
  71. data/lib/grumlin/traverser.rb +5 -7
  72. data/lib/grumlin/typed_value.rb +11 -13
  73. data/lib/grumlin/typing.rb +70 -72
  74. data/lib/grumlin/version.rb +1 -1
  75. data/lib/grumlin/vertex.rb +14 -16
  76. data/lib/grumlin/vertex_property.rb +14 -16
  77. data/lib/grumlin/with_extension.rb +17 -19
  78. data/lib/grumlin.rb +23 -19
  79. metadata +32 -6
  80. data/lib/grumlin/action.rb +0 -92
  81. data/lib/grumlin/sugar.rb +0 -15
@@ -1,39 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Expressions
5
- class P
6
- class Predicate
7
- attr_reader :namespace, :name, :value, :type
3
+ class Grumlin::Expressions::P
4
+ class Predicate
5
+ attr_reader :namespace, :name, :value, :type
8
6
 
9
- def initialize(namespace, name, value:, type: nil)
10
- @namespace = namespace
11
- @name = name
12
- @value = value
13
- @type = type
14
- end
15
- end
7
+ def initialize(namespace, name, value:, type: nil)
8
+ @namespace = namespace
9
+ @name = name
10
+ @value = value
11
+ @type = type
12
+ end
13
+ end
16
14
 
17
- class << self
18
- # TODO: support more predicates
19
- %i[eq gt lt neq].each do |predicate|
20
- define_method predicate do |*args|
21
- Predicate.new("P", predicate, value: args[0])
22
- end
23
- end
15
+ class << self
16
+ # TODO: support more predicates
17
+ [:eq, :gt, :lt, :neq].each do |predicate|
18
+ define_method predicate do |*args|
19
+ Predicate.new("P", predicate, value: args[0])
20
+ end
21
+ end
24
22
 
25
- %i[within without].each do |predicate|
26
- define_method predicate do |*args|
27
- args = if args.count == 1 && args[0].is_a?(Array)
28
- args[0]
29
- elsif args.count == 1 && args[0].is_a?(Set)
30
- args[0].to_a
31
- else
32
- args.to_a
33
- end
34
- Predicate.new("P", predicate, value: args, type: "List")
35
- end
36
- end
23
+ [:within, :without].each do |predicate|
24
+ define_method predicate do |*args|
25
+ args = if args.count == 1 && args[0].is_a?(Array)
26
+ args[0]
27
+ elsif args.count == 1 && args[0].is_a?(Set)
28
+ args[0].to_a
29
+ else
30
+ args.to_a
31
+ end
32
+ Predicate.new("P", predicate, value: args, type: "List")
37
33
  end
38
34
  end
39
35
  end
@@ -1,15 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Expressions
5
- module Pop
6
- SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :pop).map(&:to_sym).freeze
3
+ module Grumlin::Expressions::Pop
4
+ SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :pop).map(&:to_sym).freeze
7
5
 
8
- class << self
9
- extend Expression
6
+ class << self
7
+ extend Grumlin::Expressions::Expression
10
8
 
11
- define_steps(SUPPORTED_STEPS, "Pop")
12
- end
13
- end
9
+ define_steps(SUPPORTED_STEPS, "Pop")
14
10
  end
15
11
  end
@@ -1,15 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Expressions
5
- module Scope
6
- SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :scope).map(&:to_sym).freeze
3
+ module Grumlin::Expressions::Scope
4
+ SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :scope).map(&:to_sym).freeze
7
5
 
8
- class << self
9
- extend Expression
6
+ class << self
7
+ extend Grumlin::Expressions::Expression
10
8
 
11
- define_steps(SUPPORTED_STEPS, "Scope")
12
- end
13
- end
9
+ define_steps(SUPPORTED_STEPS, "Scope")
14
10
  end
15
11
  end
@@ -1,15 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Expressions
5
- module T
6
- SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :t).map(&:to_sym).freeze
3
+ module Grumlin::Expressions::T
4
+ SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :t).map(&:to_sym).freeze
7
5
 
8
- class << self
9
- extend Expression
6
+ class << self
7
+ extend Grumlin::Expressions::Expression
10
8
 
11
- define_steps(SUPPORTED_STEPS, "T")
12
- end
13
- end
9
+ define_steps(SUPPORTED_STEPS, "T")
14
10
  end
15
11
  end
@@ -1,14 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Expressions
5
- class TextP < P
6
- class << self
7
- %i[containing endingWith notContaining notEndingWith notStartingWith startingWith].each do |predicate|
8
- define_method predicate do |*args|
9
- P::Predicate.new("TextP", predicate, value: args[0])
10
- end
11
- end
3
+ class Grumlin::Expressions::TextP < Grumlin::Expressions::P
4
+ class << self
5
+ [:containing, :endingWith, :notContaining, :notEndingWith, :notStartingWith, :startingWith].each do |predicate|
6
+ define_method predicate do |*args|
7
+ P::Predicate.new("TextP", predicate, value: args[0])
12
8
  end
13
9
  end
14
10
  end
@@ -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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
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)
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
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)
19
+ end
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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
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) }
6
+ end
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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Grumlin::Middlewares::Middleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ raise NotImplementedError
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Grumlin::Middlewares::RunQuery < Grumlin::Middlewares::Middleware
4
+ def call(env)
5
+ env[:results] = env[:pool].acquire { |c| c.write(env[:query]) }
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
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)
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Grumlin::Middlewares::SerializeToSteps < Grumlin::Middlewares::Middleware
4
+ def call(env)
5
+ env[:steps] = Grumlin::Steps.from(env[:traversal])
6
+ @app.call(env)
7
+ end
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