grumlin 0.22.4 → 1.0.0.rc1
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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.rubocop.yml +9 -9
- data/Gemfile.lock +10 -8
- data/README.md +102 -141
- data/Rakefile +1 -1
- data/bin/console +18 -3
- data/doc/middlewares.md +97 -0
- data/grumlin.gemspec +1 -0
- data/lib/async/channel.rb +54 -56
- data/lib/grumlin/benchmark/repository.rb +10 -14
- data/lib/grumlin/client.rb +92 -112
- data/lib/grumlin/config.rb +30 -15
- 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 +8 -0
- data/lib/grumlin/middlewares/build_query.rb +20 -0
- data/lib/grumlin/middlewares/builder.rb +15 -0
- data/lib/grumlin/middlewares/cast_results.rb +7 -0
- 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 +11 -0
- data/lib/grumlin/middlewares/run_query.rb +7 -0
- data/lib/grumlin/middlewares/serialize_to_bytecode.rb +9 -0
- data/lib/grumlin/middlewares/serialize_to_steps.rb +8 -0
- 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 +18 -22
- data/lib/grumlin/shortcuts.rb +23 -25
- data/lib/grumlin/shortcuts_applyer.rb +27 -29
- data/lib/grumlin/step.rb +92 -0
- data/lib/grumlin/step_data.rb +12 -14
- data/lib/grumlin/steppable.rb +24 -22
- data/lib/grumlin/steps.rb +51 -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 +26 -27
- 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 +23 -19
- metadata +32 -6
- data/lib/grumlin/action.rb +0 -92
- data/lib/grumlin/sugar.rb +0 -15
data/lib/async/channel.rb
CHANGED
@@ -1,74 +1,72 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
class
|
8
|
-
class ChannelError < StandardError; end
|
9
|
-
|
10
|
-
class ChannelClosedError < ChannelError; end
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@queue = Async::Queue.new
|
14
|
-
@closed = false
|
15
|
-
end
|
3
|
+
# Channel is a wrapper around Async::Queue that provides
|
4
|
+
# a protocol and handy tools for passing data, exceptions and closing.
|
5
|
+
# It is designed to be used only with one publisher and one subscriber
|
6
|
+
class Async::Channel
|
7
|
+
class ChannelError < StandardError; end
|
16
8
|
|
17
|
-
|
18
|
-
@closed
|
19
|
-
end
|
9
|
+
class ChannelClosedError < ChannelError; end
|
20
10
|
|
21
|
-
|
22
|
-
|
23
|
-
|
11
|
+
def initialize
|
12
|
+
@queue = Async::Queue.new
|
13
|
+
@closed = false
|
14
|
+
end
|
24
15
|
|
25
|
-
|
26
|
-
|
27
|
-
|
16
|
+
def closed?
|
17
|
+
@closed
|
18
|
+
end
|
28
19
|
|
29
|
-
|
30
|
-
|
20
|
+
def open?
|
21
|
+
!@closed
|
22
|
+
end
|
31
23
|
|
32
|
-
|
33
|
-
|
24
|
+
# Methods for a publisher
|
25
|
+
def <<(payload)
|
26
|
+
raise(ChannelClosedError, "Cannot send to a closed channel") if @closed
|
34
27
|
|
35
|
-
|
36
|
-
|
28
|
+
@queue << [:payload, payload]
|
29
|
+
end
|
37
30
|
|
38
|
-
|
39
|
-
|
31
|
+
def exception(exception)
|
32
|
+
raise(ChannelClosedError, "Cannot send to a closed channel") if closed?
|
40
33
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
34
|
+
@queue << [:exception, exception]
|
35
|
+
end
|
44
36
|
|
45
|
-
|
46
|
-
|
37
|
+
def close
|
38
|
+
return if closed?
|
47
39
|
|
48
|
-
|
49
|
-
|
50
|
-
|
40
|
+
@queue << [:close]
|
41
|
+
@closed = true
|
42
|
+
end
|
51
43
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
44
|
+
def close!
|
45
|
+
return if closed?
|
46
|
+
|
47
|
+
exception(ChannelClosedError.new("Channel was forcefully closed"))
|
48
|
+
close
|
49
|
+
end
|
50
|
+
|
51
|
+
# Methods for a subscriber
|
52
|
+
def dequeue
|
53
|
+
each do |payload| # rubocop:disable Lint/UnreachableLoop this is intended
|
54
|
+
return payload
|
57
55
|
end
|
56
|
+
end
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
58
|
+
def each
|
59
|
+
raise(ChannelClosedError, "Cannot receive from a closed channel") if closed?
|
60
|
+
|
61
|
+
@queue.each do |type, payload|
|
62
|
+
case type
|
63
|
+
when :exception
|
64
|
+
payload.set_backtrace(caller + (payload.backtrace || [])) # A hack to preserve full backtrace
|
65
|
+
raise payload
|
66
|
+
when :payload
|
67
|
+
yield payload
|
68
|
+
when :close
|
69
|
+
break
|
72
70
|
end
|
73
71
|
end
|
74
72
|
end
|
@@ -1,21 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class Repository
|
6
|
-
extend Grumlin::Repository
|
3
|
+
class Grumlin::Benchmark::Repository
|
4
|
+
extend Grumlin::Repository
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
shortcut :simple_test do
|
7
|
+
self.V
|
8
|
+
end
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
def simple_test
|
11
|
+
g.V.bytecode.serialize
|
12
|
+
end
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
14
|
+
def simple_test_with_shortcut
|
15
|
+
g.simple_test.bytecode.serialize
|
20
16
|
end
|
21
17
|
end
|
data/lib/grumlin/client.rb
CHANGED
@@ -1,143 +1,123 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
class
|
5
|
-
|
6
|
-
attr_reader :client
|
7
|
-
|
8
|
-
def self.call
|
9
|
-
config = Grumlin.config
|
10
|
-
new(config.url, client_factory: config.client_factory, concurrency: config.client_concurrency)
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(url, client_factory:, concurrency: 1, parent: Async::Task.current)
|
14
|
-
super(concurrency)
|
15
|
-
@client = client_factory.call(url, parent).tap(&:connect)
|
16
|
-
@parent = parent
|
17
|
-
end
|
18
|
-
|
19
|
-
def closed?
|
20
|
-
!@client.connected?
|
21
|
-
end
|
3
|
+
class Grumlin::Client
|
4
|
+
class PoolResource < Async::Pool::Resource
|
5
|
+
attr_reader :client
|
22
6
|
|
23
|
-
|
24
|
-
|
25
|
-
|
7
|
+
def self.call
|
8
|
+
config = Grumlin.config
|
9
|
+
new(config.url, client_factory: config.client_factory, concurrency: config.client_concurrency)
|
10
|
+
end
|
26
11
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
12
|
+
def initialize(url, client_factory:, concurrency: 1, parent: Async::Task.current)
|
13
|
+
super(concurrency)
|
14
|
+
@client = client_factory.call(url, parent).tap(&:connect)
|
15
|
+
@parent = parent
|
16
|
+
end
|
32
17
|
|
33
|
-
|
34
|
-
|
35
|
-
|
18
|
+
def closed?
|
19
|
+
!@client.connected?
|
20
|
+
end
|
36
21
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
22
|
+
def close
|
23
|
+
@client.close
|
40
24
|
end
|
41
25
|
|
42
|
-
|
26
|
+
def write(query)
|
27
|
+
@client.write(query)
|
28
|
+
ensure
|
29
|
+
@count += 1
|
30
|
+
end
|
43
31
|
|
44
|
-
|
45
|
-
|
46
|
-
@url = url
|
47
|
-
@client_options = client_options
|
48
|
-
@parent = parent
|
49
|
-
@request_dispatcher = nil
|
50
|
-
@transport = nil
|
32
|
+
def viable?
|
33
|
+
!closed?
|
51
34
|
end
|
52
35
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
@transport = build_transport
|
57
|
-
response_channel = @transport.connect
|
58
|
-
@request_dispatcher = RequestDispatcher.new
|
59
|
-
@response_task = @parent.async do
|
60
|
-
response_channel.each do |response|
|
61
|
-
@request_dispatcher.add_response(response)
|
62
|
-
end
|
63
|
-
rescue Async::Stop, Async::TimeoutError, StandardError
|
64
|
-
close(check_requests: false)
|
65
|
-
end
|
66
|
-
logger.debug(self, "Connected")
|
36
|
+
def reusable?
|
37
|
+
!closed?
|
67
38
|
end
|
39
|
+
end
|
68
40
|
|
69
|
-
|
70
|
-
# 1) There are no ongoing requests
|
71
|
-
# 2) There will be no new writes after
|
72
|
-
def close(check_requests: true) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
73
|
-
return if @closed
|
41
|
+
include Console
|
74
42
|
|
75
|
-
|
43
|
+
# Client is not reusable. Once closed should be recreated.
|
44
|
+
def initialize(url, parent: Async::Task.current, **client_options)
|
45
|
+
@url = url
|
46
|
+
@client_options = client_options
|
47
|
+
@parent = parent
|
48
|
+
@request_dispatcher = nil
|
49
|
+
@transport = nil
|
50
|
+
end
|
76
51
|
|
77
|
-
|
78
|
-
|
52
|
+
def connect
|
53
|
+
raise ClientClosedError if @closed
|
79
54
|
|
80
|
-
|
81
|
-
|
55
|
+
@transport = build_transport
|
56
|
+
response_channel = @transport.connect
|
57
|
+
@request_dispatcher = Grumlin::RequestDispatcher.new
|
58
|
+
@response_task = @parent.async do
|
59
|
+
response_channel.each do |response|
|
60
|
+
@request_dispatcher.add_response(response)
|
61
|
+
end
|
62
|
+
rescue Async::Stop, Async::TimeoutError, StandardError
|
63
|
+
close(check_requests: false)
|
64
|
+
end
|
65
|
+
logger.debug(self, "Connected")
|
66
|
+
end
|
82
67
|
|
83
|
-
|
68
|
+
# Before calling close the user must ensure that:
|
69
|
+
# 1) There are no ongoing requests
|
70
|
+
# 2) There will be no new writes after
|
71
|
+
def close(check_requests: true) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
72
|
+
return if @closed
|
84
73
|
|
85
|
-
|
74
|
+
@closed = true
|
86
75
|
|
87
|
-
|
88
|
-
|
89
|
-
logger.debug(self, "Closed")
|
90
|
-
end
|
76
|
+
@transport&.close
|
77
|
+
@transport&.wait
|
91
78
|
|
92
|
-
|
93
|
-
|
94
|
-
end
|
79
|
+
@response_task&.stop
|
80
|
+
@response_task&.wait
|
95
81
|
|
96
|
-
|
97
|
-
def write(bytecode, session_id: nil)
|
98
|
-
raise NotConnectedError unless connected?
|
82
|
+
return if @request_dispatcher&.requests&.empty?
|
99
83
|
|
100
|
-
|
84
|
+
@request_dispatcher.clear unless check_requests
|
101
85
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
rescue Async::Stop, Async::TimeoutError
|
107
|
-
close(check_requests: false)
|
108
|
-
raise
|
109
|
-
end
|
110
|
-
end
|
86
|
+
raise ResourceLeakError, "Request list is not empty: #{@request_dispatcher.requests}" if check_requests
|
87
|
+
ensure
|
88
|
+
logger.debug(self, "Closed")
|
89
|
+
end
|
111
90
|
|
112
|
-
|
113
|
-
|
114
|
-
|
91
|
+
def connected?
|
92
|
+
@transport&.connected? || false
|
93
|
+
end
|
115
94
|
|
116
|
-
|
117
|
-
|
95
|
+
# TODO: support yielding
|
96
|
+
def write(query)
|
97
|
+
raise NotConnectedError unless connected?
|
98
|
+
|
99
|
+
channel = @request_dispatcher.add_request(query)
|
100
|
+
begin
|
101
|
+
@transport.write(query)
|
102
|
+
channel.dequeue
|
103
|
+
rescue Async::Stop, Async::TimeoutError
|
104
|
+
close(check_requests: false)
|
105
|
+
raise
|
118
106
|
end
|
107
|
+
end
|
119
108
|
|
120
|
-
|
109
|
+
def inspect
|
110
|
+
"<#{self.class} url=#{@url} connected=#{connected?}>"
|
111
|
+
end
|
121
112
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end
|
113
|
+
def to_s
|
114
|
+
inspect
|
115
|
+
end
|
126
116
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
args: {
|
133
|
-
gremlin: {
|
134
|
-
:@type => "g:Bytecode",
|
135
|
-
:@value => bytecode.serialize
|
136
|
-
},
|
137
|
-
aliases: { g: :g },
|
138
|
-
session: session_id
|
139
|
-
}.compact
|
140
|
-
}
|
141
|
-
end
|
117
|
+
private
|
118
|
+
|
119
|
+
# This might be overridden in successors
|
120
|
+
def build_transport
|
121
|
+
Grumlin::Transport.new(@url, parent: @parent, **@client_options)
|
142
122
|
end
|
143
123
|
end
|
data/lib/grumlin/config.rb
CHANGED
@@ -1,26 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
attr_accessor :url, :pool_size, :client_concurrency, :client_factory, :provider
|
3
|
+
class Grumlin::Config
|
4
|
+
attr_accessor :url, :pool_size, :client_concurrency, :client_factory, :provider
|
6
5
|
|
7
|
-
|
6
|
+
SUPPORTED_PROVIDERS = [:neptune, :tinkergraph].freeze
|
8
7
|
|
9
|
-
|
8
|
+
DEFAULT_MIDDLEWARES = Grumlin::Middlewares::Builder.new do |b|
|
9
|
+
b.use Grumlin::Middlewares::SerializeToSteps
|
10
|
+
b.use Grumlin::Middlewares::ApplyShortcuts
|
11
|
+
b.use Grumlin::Middlewares::SerializeToBytecode
|
12
|
+
b.use Grumlin::Middlewares::BuildQuery
|
13
|
+
b.use Grumlin::Middlewares::CastResults
|
14
|
+
b.use Grumlin::Middlewares::RunQuery
|
15
|
+
end
|
10
16
|
|
11
|
-
|
17
|
+
class ConfigurationError < Grumlin::Error; end
|
12
18
|
|
13
|
-
|
14
|
-
@pool_size = 10
|
15
|
-
@client_concurrency = 5
|
16
|
-
@provider = :tinkergraph
|
17
|
-
@client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
|
18
|
-
end
|
19
|
+
class UnknownProviderError < ConfigurationError; end
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
def initialize
|
22
|
+
@pool_size = 10
|
23
|
+
@client_concurrency = 5
|
24
|
+
@provider = :tinkergraph
|
25
|
+
@client_factory = ->(url, parent) { Grumlin::Client.new(url, parent: parent) }
|
26
|
+
end
|
22
27
|
|
23
|
-
|
28
|
+
def middlewares
|
29
|
+
@middlewares ||= Grumlin::Middlewares::Builder.new do |b|
|
30
|
+
b.use DEFAULT_MIDDLEWARES
|
24
31
|
end
|
32
|
+
yield(@middlewares) if block_given?
|
33
|
+
@middlewares
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate!
|
37
|
+
return if SUPPORTED_PROVIDERS.include?(provider.to_sym)
|
38
|
+
|
39
|
+
raise UnknownProviderError, "provider '#{provider}' is unknown. Supported providers: #{SUPPORTED_PROVIDERS}"
|
25
40
|
end
|
26
41
|
end
|
@@ -1,25 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
attr_reader :uuid
|
3
|
+
class Grumlin::DummyTransaction < Grumlin::Transaction
|
4
|
+
attr_reader :uuid
|
6
5
|
|
7
|
-
|
6
|
+
include Console
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
def initialize(traversal_start_class, middlewares:, pool: nil) # rubocop:disable Lint/MissingSuper, Lint/UnusedMethodArgument
|
9
|
+
@traversal_start_class = traversal_start_class
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
11
|
+
logger.info(self) do
|
12
|
+
"#{Grumlin.config.provider} does not support transactions. commit and rollback are ignored, data will be saved"
|
15
13
|
end
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def commit
|
17
|
+
nil
|
18
|
+
end
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
20
|
+
def rollback
|
21
|
+
nil
|
24
22
|
end
|
25
23
|
end
|
data/lib/grumlin/edge.rb
CHANGED
@@ -1,28 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
attr_reader :label, :id, :inVLabel, :outVLabel, :inV, :outV
|
3
|
+
class Grumlin::Edge
|
4
|
+
attr_reader :label, :id, :inVLabel, :outVLabel, :inV, :outV
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
def initialize(label:, id:, inVLabel:, outVLabel:, inV:, outV:)
|
7
|
+
@label = label
|
8
|
+
@id = Grumlin::Typing.cast(id)
|
9
|
+
@inVLabel = inVLabel
|
10
|
+
@outVLabel = outVLabel
|
11
|
+
@inV = Grumlin::Typing.cast(inV)
|
12
|
+
@outV = Grumlin::Typing.cast(outV)
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def ==(other)
|
16
|
+
self.class == other.class && @label == other.label && @id == other.id
|
17
|
+
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
def inspect
|
20
|
+
"e[#{@id}][#{@outV}-#{@label}->#{@inV}]"
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
end
|
23
|
+
def to_s
|
24
|
+
inspect
|
27
25
|
end
|
28
26
|
end
|
@@ -1,15 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Grumlin
|
4
|
-
|
5
|
-
module Cardinality
|
6
|
-
SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :cardinality).map(&:to_sym).freeze
|
3
|
+
module Grumlin::Expressions::Cardinality
|
4
|
+
SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :cardinality).map(&:to_sym).freeze
|
7
5
|
|
8
|
-
|
9
|
-
|
6
|
+
class << self
|
7
|
+
extend Grumlin::Expressions::Expression
|
10
8
|
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
9
|
+
define_steps(SUPPORTED_STEPS, "Cardinality")
|
14
10
|
end
|
15
11
|
end
|
@@ -1,15 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Grumlin
|
4
|
-
|
5
|
-
module Column
|
6
|
-
SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :column).map(&:to_sym).freeze
|
3
|
+
module Grumlin::Expressions::Column
|
4
|
+
SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :column).map(&:to_sym).freeze
|
7
5
|
|
8
|
-
|
9
|
-
|
6
|
+
class << self
|
7
|
+
extend Grumlin::Expressions::Expression
|
10
8
|
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
9
|
+
define_steps(SUPPORTED_STEPS, "Column")
|
14
10
|
end
|
15
11
|
end
|
@@ -1,17 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Grumlin
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
name = "@#{step}"
|
10
|
-
return instance_variable_get(name) if instance_variable_defined?(name)
|
3
|
+
module Grumlin::Expressions::Expression
|
4
|
+
def define_steps(steps, tool_name)
|
5
|
+
steps.each do |step|
|
6
|
+
define_method step do
|
7
|
+
name = "@#{step}"
|
8
|
+
return instance_variable_get(name) if instance_variable_defined?(name)
|
11
9
|
|
12
|
-
|
13
|
-
end
|
14
|
-
end
|
10
|
+
instance_variable_set(name, Grumlin::TypedValue.new(type: tool_name, value: step))
|
15
11
|
end
|
16
12
|
end
|
17
13
|
end
|
@@ -1,15 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Grumlin
|
4
|
-
|
5
|
-
module Operator
|
6
|
-
SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :operator).map(&:to_sym).freeze
|
3
|
+
module Grumlin::Expressions::Operator
|
4
|
+
SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :operator).map(&:to_sym).freeze
|
7
5
|
|
8
|
-
|
9
|
-
|
6
|
+
class << self
|
7
|
+
extend Grumlin::Expressions::Expression
|
10
8
|
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
9
|
+
define_steps(SUPPORTED_STEPS, "Operator")
|
14
10
|
end
|
15
11
|
end
|
@@ -1,15 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Grumlin
|
4
|
-
|
5
|
-
module Order
|
6
|
-
SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :order).map(&:to_sym).freeze
|
3
|
+
module Grumlin::Expressions::Order
|
4
|
+
SUPPORTED_STEPS = Grumlin.definitions.dig(:expressions, :order).map(&:to_sym).freeze
|
7
5
|
|
8
|
-
|
9
|
-
|
6
|
+
class << self
|
7
|
+
extend Grumlin::Expressions::Expression
|
10
8
|
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
9
|
+
define_steps(SUPPORTED_STEPS, "Order")
|
14
10
|
end
|
15
11
|
end
|