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
data/lib/async/channel.rb CHANGED
@@ -1,74 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Async
4
- # Channel is a wrapper around Async::Queue that provides
5
- # a protocol and handy tools for passing data, exceptions and closing.
6
- # It is designed to be used only with one publisher and one subscriber
7
- class Channel
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
- def closed?
18
- @closed
19
- end
9
+ class ChannelClosedError < ChannelError; end
20
10
 
21
- def open?
22
- !@closed
23
- end
11
+ def initialize
12
+ @queue = Async::Queue.new
13
+ @closed = false
14
+ end
24
15
 
25
- # Methods for a publisher
26
- def <<(payload)
27
- raise(ChannelClosedError, "Cannot send to a closed channel") if @closed
16
+ def closed?
17
+ @closed
18
+ end
28
19
 
29
- @queue << [:payload, payload]
30
- end
20
+ def open?
21
+ !@closed
22
+ end
31
23
 
32
- def exception(exception)
33
- raise(ChannelClosedError, "Cannot send to a closed channel") if closed?
24
+ # Methods for a publisher
25
+ def <<(payload)
26
+ raise(ChannelClosedError, "Cannot send to a closed channel") if @closed
34
27
 
35
- @queue << [:exception, exception]
36
- end
28
+ @queue << [:payload, payload]
29
+ end
37
30
 
38
- def close
39
- return if closed?
31
+ def exception(exception)
32
+ raise(ChannelClosedError, "Cannot send to a closed channel") if closed?
40
33
 
41
- @queue << [:close]
42
- @closed = true
43
- end
34
+ @queue << [:exception, exception]
35
+ end
44
36
 
45
- def close!
46
- return if closed?
37
+ def close
38
+ return if closed?
47
39
 
48
- exception(ChannelClosedError.new("Channel was forcefully closed"))
49
- close
50
- end
40
+ @queue << [:close]
41
+ @closed = true
42
+ end
51
43
 
52
- # Methods for a subscriber
53
- def dequeue
54
- each do |payload| # rubocop:disable Lint/UnreachableLoop this is intended
55
- return payload
56
- end
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
- def each
60
- raise(ChannelClosedError, "Cannot receive from a closed channel") if closed?
61
-
62
- @queue.each do |type, payload|
63
- case type
64
- when :exception
65
- payload.set_backtrace(caller + (payload.backtrace || [])) # A hack to preserve full backtrace
66
- raise payload
67
- when :payload
68
- yield payload
69
- when :close
70
- break
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
- module Grumlin
4
- module Benchmark
5
- class Repository
6
- extend Grumlin::Repository
3
+ class Grumlin::Benchmark::Repository
4
+ extend Grumlin::Repository
7
5
 
8
- shortcut :simple_test do
9
- self.V
10
- end
6
+ shortcut :simple_test do
7
+ self.V
8
+ end
11
9
 
12
- def simple_test
13
- g.V.bytecode.serialize
14
- end
10
+ def simple_test
11
+ g.V.bytecode.serialize
12
+ end
15
13
 
16
- def simple_test_with_shortcut
17
- g.simple_test.bytecode.serialize
18
- end
19
- end
14
+ def simple_test_with_shortcut
15
+ g.simple_test.bytecode.serialize
20
16
  end
21
17
  end
@@ -1,143 +1,123 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Client
5
- class PoolResource < Async::Pool::Resource
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
- def close
24
- @client.close
25
- end
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
- def write(bytecode, session_id: nil)
28
- @client.write(bytecode, session_id: session_id)
29
- ensure
30
- @count += 1
31
- end
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
- def viable?
34
- !closed?
35
- end
18
+ def closed?
19
+ !@client.connected?
20
+ end
36
21
 
37
- def reusable?
38
- !closed?
39
- end
22
+ def close
23
+ @client.close
40
24
  end
41
25
 
42
- include Console
26
+ def write(query)
27
+ @client.write(query)
28
+ ensure
29
+ @count += 1
30
+ end
43
31
 
44
- # Client is not reusable. Once closed should be recreated.
45
- def initialize(url, parent: Async::Task.current, **client_options)
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 connect
54
- raise ClientClosedError if @closed
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
- # Before calling close the user must ensure that:
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
- @closed = true
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
- @transport&.close
78
- @transport&.wait
52
+ def connect
53
+ raise ClientClosedError if @closed
79
54
 
80
- @response_task&.stop
81
- @response_task&.wait
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
- return if @request_dispatcher&.requests&.empty?
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
- @request_dispatcher.clear unless check_requests
74
+ @closed = true
86
75
 
87
- raise ResourceLeakError, "Request list is not empty: #{@request_dispatcher.requests}" if check_requests
88
- ensure
89
- logger.debug(self, "Closed")
90
- end
76
+ @transport&.close
77
+ @transport&.wait
91
78
 
92
- def connected?
93
- @transport&.connected? || false
94
- end
79
+ @response_task&.stop
80
+ @response_task&.wait
95
81
 
96
- # TODO: support yielding
97
- def write(bytecode, session_id: nil)
98
- raise NotConnectedError unless connected?
82
+ return if @request_dispatcher&.requests&.empty?
99
83
 
100
- request = to_query(bytecode, session_id: session_id)
84
+ @request_dispatcher.clear unless check_requests
101
85
 
102
- channel = @request_dispatcher.add_request(request)
103
- begin
104
- @transport.write(request)
105
- channel.dequeue.flat_map { |item| Typing.cast(item) }
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
- def inspect
113
- "<#{self.class} url=#{@url} connected=#{connected?}>"
114
- end
91
+ def connected?
92
+ @transport&.connected? || false
93
+ end
115
94
 
116
- def to_s
117
- inspect
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
- private
109
+ def inspect
110
+ "<#{self.class} url=#{@url} connected=#{connected?}>"
111
+ end
121
112
 
122
- # This might be overridden in successors
123
- def build_transport
124
- Transport.new(@url, parent: @parent, **@client_options)
125
- end
113
+ def to_s
114
+ inspect
115
+ end
126
116
 
127
- def to_query(bytecode, session_id:)
128
- {
129
- requestId: SecureRandom.uuid,
130
- op: :bytecode,
131
- processor: session_id ? :session : :traversal,
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
@@ -1,26 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class Config
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
- SUPPORTED_PROVIDERS = %i[neptune tinkergraph].freeze
6
+ SUPPORTED_PROVIDERS = [:neptune, :tinkergraph].freeze
8
7
 
9
- class ConfigurationError < Grumlin::Error; end
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
- class UnknownProviderError < ConfigurationError; end
17
+ class ConfigurationError < Grumlin::Error; end
12
18
 
13
- def initialize
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
- def validate!
21
- return if SUPPORTED_PROVIDERS.include?(provider.to_sym)
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
- raise UnknownProviderError, "provider '#{provider}' is unknown. Supported providers: #{SUPPORTED_PROVIDERS}"
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
- module Grumlin
4
- class DummyTransaction < Transaction
5
- attr_reader :uuid
3
+ class Grumlin::DummyTransaction < Grumlin::Transaction
4
+ attr_reader :uuid
6
5
 
7
- include Console
6
+ include Console
8
7
 
9
- def initialize(traversal_start_class, pool: Grumlin.default_pool) # rubocop:disable Lint/MissingSuper, Lint/UnusedMethodArgument
10
- @traversal_start_class = traversal_start_class
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
- logger.info(self) do
13
- "#{Grumlin.config.provider} does not support transactions. commit and rollback are ignored, data will be saved"
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
- def commit
18
- nil
19
- end
16
+ def commit
17
+ nil
18
+ end
20
19
 
21
- def rollback
22
- nil
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
- module Grumlin
4
- class Edge
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
- def initialize(label:, id:, inVLabel:, outVLabel:, inV:, outV:)
8
- @label = label
9
- @id = Typing.cast(id)
10
- @inVLabel = inVLabel
11
- @outVLabel = outVLabel
12
- @inV = Typing.cast(inV)
13
- @outV = Typing.cast(outV)
14
- end
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
- def ==(other)
17
- self.class == other.class && @label == other.label && @id == other.id
18
- end
15
+ def ==(other)
16
+ self.class == other.class && @label == other.label && @id == other.id
17
+ end
19
18
 
20
- def inspect
21
- "e[#{@id}][#{@outV}-#{@label}->#{@inV}]"
22
- end
19
+ def inspect
20
+ "e[#{@id}][#{@outV}-#{@label}->#{@inV}]"
21
+ end
23
22
 
24
- def to_s
25
- inspect
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
- module Expressions
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
- class << self
9
- extend Expression
6
+ class << self
7
+ extend Grumlin::Expressions::Expression
10
8
 
11
- define_steps(SUPPORTED_STEPS, "Cardinality")
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
- module Expressions
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
- class << self
9
- extend Expression
6
+ class << self
7
+ extend Grumlin::Expressions::Expression
10
8
 
11
- define_steps(SUPPORTED_STEPS, "Column")
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
- module Expressions
5
- module Expression
6
- def define_steps(steps, tool_name)
7
- steps.each do |step|
8
- define_method step do
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
- instance_variable_set(name, TypedValue.new(type: tool_name, value: step))
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
- module Expressions
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
- class << self
9
- extend Expression
6
+ class << self
7
+ extend Grumlin::Expressions::Expression
10
8
 
11
- define_steps(SUPPORTED_STEPS, "Operator")
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
- module Expressions
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
- class << self
9
- extend Expression
6
+ class << self
7
+ extend Grumlin::Expressions::Expression
10
8
 
11
- define_steps(SUPPORTED_STEPS, "Order")
12
- end
13
- end
9
+ define_steps(SUPPORTED_STEPS, "Order")
14
10
  end
15
11
  end