grumlin 0.23.0 → 1.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -9
- data/Gemfile.lock +1 -1
- data/README.md +100 -142
- data/Rakefile +1 -1
- data/bin/console +18 -3
- data/doc/middlewares.md +49 -10
- data/lib/async/channel.rb +54 -56
- data/lib/grumlin/benchmark/repository.rb +10 -14
- data/lib/grumlin/client.rb +93 -95
- data/lib/grumlin/config.rb +33 -33
- data/lib/grumlin/dummy_transaction.rb +13 -15
- data/lib/grumlin/edge.rb +18 -20
- data/lib/grumlin/expressions/cardinality.rb +5 -9
- data/lib/grumlin/expressions/column.rb +5 -9
- data/lib/grumlin/expressions/expression.rb +7 -11
- data/lib/grumlin/expressions/operator.rb +5 -9
- data/lib/grumlin/expressions/order.rb +5 -9
- data/lib/grumlin/expressions/p.rb +27 -31
- data/lib/grumlin/expressions/pop.rb +5 -9
- data/lib/grumlin/expressions/scope.rb +5 -9
- data/lib/grumlin/expressions/t.rb +5 -9
- data/lib/grumlin/expressions/text_p.rb +5 -9
- data/lib/grumlin/expressions/with_options.rb +17 -21
- data/lib/grumlin/features/feature_list.rb +8 -12
- data/lib/grumlin/features/neptune_features.rb +5 -9
- data/lib/grumlin/features/tinkergraph_features.rb +5 -9
- data/lib/grumlin/features.rb +8 -10
- data/lib/grumlin/middlewares/apply_shortcuts.rb +4 -8
- data/lib/grumlin/middlewares/build_query.rb +16 -20
- data/lib/grumlin/middlewares/builder.rb +15 -0
- data/lib/grumlin/middlewares/cast_results.rb +3 -7
- data/lib/grumlin/middlewares/find_blocklisted_steps.rb +14 -0
- data/lib/grumlin/middlewares/find_mutating_steps.rb +9 -0
- data/lib/grumlin/middlewares/middleware.rb +6 -10
- data/lib/grumlin/middlewares/run_query.rb +3 -7
- data/lib/grumlin/middlewares/serialize_to_bytecode.rb +5 -9
- data/lib/grumlin/middlewares/serialize_to_steps.rb +4 -8
- data/lib/grumlin/path.rb +11 -13
- data/lib/grumlin/property.rb +14 -16
- data/lib/grumlin/query_validators/blocklisted_steps_validator.rb +22 -0
- data/lib/grumlin/query_validators/validator.rb +36 -0
- data/lib/grumlin/repository/error_handling_strategy.rb +36 -40
- data/lib/grumlin/repository/instance_methods.rb +115 -118
- data/lib/grumlin/repository.rb +82 -58
- data/lib/grumlin/request_dispatcher.rb +55 -57
- data/lib/grumlin/request_error_factory.rb +53 -55
- data/lib/grumlin/shortcut.rb +19 -21
- data/lib/grumlin/shortcuts/properties.rb +12 -16
- data/lib/grumlin/shortcuts/storage.rb +67 -74
- data/lib/grumlin/shortcuts/upserts.rb +19 -22
- data/lib/grumlin/shortcuts.rb +23 -25
- data/lib/grumlin/shortcuts_applyer.rb +27 -29
- data/lib/grumlin/step.rb +88 -90
- data/lib/grumlin/step_data.rb +12 -14
- data/lib/grumlin/steppable.rb +23 -25
- data/lib/grumlin/steps.rb +52 -54
- data/lib/grumlin/steps_serializers/bytecode.rb +53 -56
- data/lib/grumlin/steps_serializers/human_readable_bytecode.rb +17 -21
- data/lib/grumlin/steps_serializers/serializer.rb +7 -11
- data/lib/grumlin/steps_serializers/string.rb +26 -30
- data/lib/grumlin/test/rspec/db_cleaner_context.rb +8 -12
- data/lib/grumlin/test/rspec/gremlin_context.rb +18 -16
- data/lib/grumlin/test/rspec.rb +1 -5
- data/lib/grumlin/transaction.rb +34 -36
- data/lib/grumlin/transport.rb +71 -73
- data/lib/grumlin/traversal_start.rb +31 -33
- data/lib/grumlin/traversal_strategies/options_strategy.rb +3 -7
- data/lib/grumlin/traverser.rb +5 -7
- data/lib/grumlin/typed_value.rb +11 -13
- data/lib/grumlin/typing.rb +70 -72
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin/vertex.rb +14 -16
- data/lib/grumlin/vertex_property.rb +14 -16
- data/lib/grumlin/with_extension.rb +17 -19
- data/lib/grumlin.rb +13 -0
- metadata +9 -6
- data/lib/grumlin/middlewares/frozen_builder.rb +0 -18
- data/lib/grumlin/sugar.rb +0 -15
@@ -1,31 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
end
|
16
|
-
end
|
12
|
+
instance_variable_set(name, WithOptions.new(k, v))
|
17
13
|
end
|
14
|
+
end
|
15
|
+
end
|
18
16
|
|
19
|
-
|
17
|
+
attr_reader :name, :value
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
def initialize(name, value)
|
20
|
+
@name = name
|
21
|
+
@value = value
|
22
|
+
end
|
25
23
|
|
26
|
-
|
27
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
10
|
-
|
7
|
+
@user_supplied_ids
|
8
|
+
end
|
11
9
|
|
12
|
-
|
13
|
-
|
10
|
+
def supports_transactions?
|
11
|
+
raise(NotImplementedError) if @supports_transactions.nil?
|
14
12
|
|
15
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
data/lib/grumlin/features.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Grumlin
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
}.freeze
|
3
|
+
module Grumlin::Features
|
4
|
+
class << self
|
5
|
+
FEATURES = {
|
6
|
+
neptune: NeptuneFeatures.new,
|
7
|
+
tinkergraph: TinkergraphFeatures.new
|
8
|
+
}.freeze
|
10
9
|
|
11
|
-
|
12
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
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
|
@@ -1,15 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@app = app
|
8
|
-
end
|
3
|
+
class Grumlin::Middlewares::Middleware
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
9
7
|
|
10
|
-
|
11
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
|
4
|
-
|
5
|
-
attr_reader :objects
|
3
|
+
class Grumlin::Path
|
4
|
+
attr_reader :objects
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
def initialize(path)
|
7
|
+
@labels = Grumlin::Typing.cast(path[:labels])
|
8
|
+
@objects = Grumlin::Typing.cast(path[:objects])
|
9
|
+
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def inspect
|
12
|
+
"p[#{@objects}]"
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
15
|
+
def to_s
|
16
|
+
inspect
|
19
17
|
end
|
20
18
|
end
|
data/lib/grumlin/property.rb
CHANGED
@@ -1,24 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
attr_reader :key, :value
|
3
|
+
class Grumlin::Property
|
4
|
+
attr_reader :key, :value
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
def initialize(value)
|
7
|
+
@key = value[:key]
|
8
|
+
@value = Grumlin::Typing.cast(value[:value])
|
9
|
+
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def inspect
|
12
|
+
"p[#{key}->#{value}]"
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def to_s
|
16
|
+
inspect
|
17
|
+
end
|
19
18
|
|
20
|
-
|
21
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|