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,158 +1,155 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Repository
5
- module InstanceMethods # rubocop:disable Metrics/ModuleLength
6
- include Grumlin::Expressions
3
+ module Grumlin::Repository::InstanceMethods # rubocop:disable Metrics/ModuleLength
4
+ include Grumlin::Expressions
7
5
 
8
- extend Forwardable
6
+ extend Forwardable
9
7
 
10
- UPSERT_RETRY_PARAMS = {
11
- on: [Grumlin::AlreadyExistsError, Grumlin::ConcurrentModificationError],
12
- sleep_method: ->(n) { Async::Task.current.sleep(n) },
13
- tries: 5,
14
- sleep: ->(n) { (n**2) + 1 + rand }
15
- }.freeze
8
+ def_delegator "self.class", :shortcuts
9
+ def_delegator :self, :__, :g
16
10
 
17
- DEFAULT_ERROR_HANDLING_STRATEGY = ErrorHandlingStrategy.new(mode: :retry, **UPSERT_RETRY_PARAMS)
11
+ UPSERT_RETRY_PARAMS = {
12
+ on: [Grumlin::AlreadyExistsError, Grumlin::ConcurrentModificationError],
13
+ sleep_method: ->(n) { Async::Task.current.sleep(n) },
14
+ tries: 5,
15
+ sleep: ->(n) { (n**2) + 1 + rand }
16
+ }.freeze
18
17
 
19
- def_delegators :shortcuts, :g, :__
18
+ DEFAULT_ERROR_HANDLING_STRATEGY = Grumlin::Repository::ErrorHandlingStrategy.new(mode: :retry, **UPSERT_RETRY_PARAMS)
20
19
 
21
- def shortcuts
22
- self.class.shortcuts
23
- end
20
+ def __
21
+ shortcuts.traversal_start_class.new(pool: Grumlin.default_pool, middlewares: self.class.middlewares)
22
+ end
24
23
 
25
- def drop_vertex(id, start: g)
26
- start.V(id).drop.iterate
27
- end
24
+ def drop_vertex(id, start: g)
25
+ start.V(id).drop.iterate
26
+ end
28
27
 
29
- def drop_in_batches(traversal, batch_size: 10_000) # rubocop:disable Metrics/AbcSize
30
- total_count = traversal.count.next
28
+ def drop_in_batches(traversal, batch_size: 10_000) # rubocop:disable Metrics/AbcSize
29
+ total_count = traversal.count.next
31
30
 
32
- batches = (total_count / batch_size) + 1
31
+ batches = (total_count / batch_size) + 1
33
32
 
34
- Console.logger.info(self) do
35
- "drop_in_batches: total_count: #{total_count}, batch_size: #{batch_size}, batches: #{batches}"
36
- end
33
+ Console.logger.info(self) do
34
+ "drop_in_batches: total_count: #{total_count}, batch_size: #{batch_size}, batches: #{batches}"
35
+ end
37
36
 
38
- batches.times do |batch|
39
- Console.logger.info(self) { "drop_in_batches: deleting batch #{batch + 1}/#{batches}..." }
40
- traversal.limit(batch_size).drop.iterate
41
- Console.logger.info(self) { "drop_in_batches: batch #{batch + 1}/#{batches} deleted" }
42
- end
37
+ batches.times do |batch|
38
+ Console.logger.info(self) { "drop_in_batches: deleting batch #{batch + 1}/#{batches}..." }
39
+ traversal.limit(batch_size).drop.iterate
40
+ Console.logger.info(self) { "drop_in_batches: batch #{batch + 1}/#{batches} deleted" }
41
+ end
43
42
 
44
- return if traversal.count.next.zero?
43
+ return if traversal.count.next.zero?
45
44
 
46
- drop_in_batches(traversal, batch_size: batch_size)
45
+ drop_in_batches(traversal, batch_size: batch_size)
47
46
 
48
- Console.logger.info(self) { "drop_in_batches: finished." }
49
- end
47
+ Console.logger.info(self) { "drop_in_batches: finished." }
48
+ end
50
49
 
51
- def drop_edge(id = nil, from: nil, to: nil, label: nil, start: g) # rubocop:disable Metrics/AbcSize
52
- raise ArgumentError, "either id or from:, to: and label: must be passed" if [id, from, to, label].all?(&:nil?)
53
- return start.E(id).drop.iterate unless id.nil?
50
+ def drop_edge(id = nil, from: nil, to: nil, label: nil, start: g) # rubocop:disable Metrics/AbcSize
51
+ raise ArgumentError, "either id or from:, to: and label: must be passed" if [id, from, to, label].all?(&:nil?)
52
+ return start.E(id).drop.iterate unless id.nil?
54
53
 
55
- raise ArgumentError, "from:, to: and label: must be passed" if [from, to, label].any?(&:nil?)
54
+ raise ArgumentError, "from:, to: and label: must be passed" if [from, to, label].any?(&:nil?)
56
55
 
57
- start.V(from).outE(label).where(__.inV.hasId(to)).limit(1).drop.iterate
58
- end
56
+ start.V(from).outE(label).where(__.inV.hasId(to)).limit(1).drop.iterate
57
+ end
59
58
 
60
- def add_vertex(label, id = nil, start: g, **properties)
61
- id ||= properties[T.id]
62
- properties = except(properties, T.id)
59
+ def add_vertex(label, id = nil, start: g, **properties)
60
+ id ||= properties[T.id]
61
+ properties = except(properties, T.id)
63
62
 
64
- t = start.addV(label)
65
- t = t.props(T.id => id) unless id.nil?
66
- t.props(**properties).next
67
- end
63
+ t = start.addV(label)
64
+ t = t.props(T.id => id) unless id.nil?
65
+ t.props(**properties).next
66
+ end
68
67
 
69
- def add_edge(label, id = nil, from:, to:, start: g, **properties)
70
- id ||= properties[T.id]
71
- properties = except(properties, T.label)
72
- properties[T.id] = id
68
+ def add_edge(label, id = nil, from:, to:, start: g, **properties)
69
+ id ||= properties[T.id]
70
+ properties = except(properties, T.label)
71
+ properties[T.id] = id
73
72
 
74
- start.addE(label).from(__.V(from)).to(__.V(to)).props(**properties).next
75
- end
73
+ start.addE(label).from(__.V(from)).to(__.V(to)).props(**properties).next
74
+ end
75
+
76
+ def upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params) # rubocop:disable Metrics/ParameterLists
77
+ with_upsert_error_handling(on_failure, params) do
78
+ create_properties, update_properties = cleanup_properties(create_properties, update_properties)
79
+
80
+ start.upsertV(label, id, create_properties, update_properties).id.next
81
+ end
82
+ end
76
83
 
77
- def upsert_vertex(label, id, create_properties: {}, update_properties: {}, on_failure: :retry, start: g, **params) # rubocop:disable Metrics/ParameterLists
78
- with_upsert_error_handling(on_failure, params) do
84
+ # vertices:
85
+ # [["label", "id", {create: :properties}, {update: properties}]]
86
+ # params can override Retryable config from UPSERT_RETRY_PARAMS
87
+ def upsert_vertices(vertices, batch_size: 100, on_failure: :retry, start: g, **params)
88
+ vertices.each_slice(batch_size) do |slice|
89
+ with_upsert_error_handling(on_failure, params) do
90
+ slice.reduce(start) do |t, (label, id, create_properties, update_properties)|
79
91
  create_properties, update_properties = cleanup_properties(create_properties, update_properties)
80
92
 
81
- start.upsertV(label, id, create_properties, update_properties).id.next
82
- end
93
+ t.upsertV(label, id, create_properties, update_properties)
94
+ end.id.iterate
83
95
  end
96
+ end
97
+ end
84
98
 
85
- # vertices:
86
- # [["label", "id", {create: :properties}, {update: properties}]]
87
- # params can override Retryable config from UPSERT_RETRY_PARAMS
88
- def upsert_vertices(vertices, batch_size: 100, on_failure: :retry, start: g, **params)
89
- vertices.each_slice(batch_size) do |slice|
90
- with_upsert_error_handling(on_failure, params) do
91
- slice.reduce(start) do |t, (label, id, create_properties, update_properties)|
92
- create_properties, update_properties = cleanup_properties(create_properties, update_properties)
93
-
94
- t.upsertV(label, id, create_properties, update_properties)
95
- end.id.iterate
96
- end
97
- end
98
- end
99
+ # Only from and to are used to find the existing edge, if one wants to assign an id to a created edge,
100
+ # it must be passed as T.id in create_properties.
101
+ def upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, # rubocop:disable Metrics/ParameterLists
102
+ on_failure: :retry, start: g, **params)
103
+ with_upsert_error_handling(on_failure, params) do
104
+ create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
105
+ start.upsertE(label, from, to, create_properties, update_properties).id.next
106
+ end
107
+ end
99
108
 
100
- # Only from and to are used to find the existing edge, if one wants to assign an id to a created edge,
101
- # it must be passed as T.id in create_properties.
102
- def upsert_edge(label, from:, to:, create_properties: {}, update_properties: {}, # rubocop:disable Metrics/ParameterLists
103
- on_failure: :retry, start: g, **params)
104
- with_upsert_error_handling(on_failure, params) do
109
+ # edges:
110
+ # [["label", "from", "to", {create: :properties}, {update: properties}]]
111
+ # params can override Retryable config from UPSERT_RETRY_PARAMS
112
+ def upsert_edges(edges, batch_size: 100, on_failure: :retry, start: g, **params)
113
+ edges.each_slice(batch_size) do |slice|
114
+ with_upsert_error_handling(on_failure, params) do
115
+ slice.reduce(start) do |t, (label, from, to, create_properties, update_properties)|
105
116
  create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
106
- start.upsertE(label, from, to, create_properties, update_properties).id.next
107
- end
108
- end
109
117
 
110
- # edges:
111
- # [["label", "from", "to", {create: :properties}, {update: properties}]]
112
- # params can override Retryable config from UPSERT_RETRY_PARAMS
113
- def upsert_edges(edges, batch_size: 100, on_failure: :retry, start: g, **params)
114
- edges.each_slice(batch_size) do |slice|
115
- with_upsert_error_handling(on_failure, params) do
116
- slice.reduce(start) do |t, (label, from, to, create_properties, update_properties)|
117
- create_properties, update_properties = cleanup_properties(create_properties, update_properties, T.label)
118
-
119
- t.upsertE(label, from, to, create_properties, update_properties)
120
- end.id.iterate
121
- end
122
- end
118
+ t.upsertE(label, from, to, create_properties, update_properties)
119
+ end.id.iterate
123
120
  end
121
+ end
122
+ end
124
123
 
125
- private
124
+ private
126
125
 
127
- def with_upsert_error_handling(on_failure, params, &block)
128
- if params.any?
129
- ErrorHandlingStrategy.new(mode: on_failure, **UPSERT_RETRY_PARAMS.merge(params))
130
- else
131
- DEFAULT_ERROR_HANDLING_STRATEGY
132
- end.apply!(&block)
133
- end
126
+ def with_upsert_error_handling(on_failure, params, &block)
127
+ if params.any?
128
+ ErrorHandlingStrategy.new(mode: on_failure, **UPSERT_RETRY_PARAMS.merge(params))
129
+ else
130
+ DEFAULT_ERROR_HANDLING_STRATEGY
131
+ end.apply!(&block)
132
+ end
134
133
 
135
- def with_upsert_retry(retry_params, &block)
136
- retry_params = UPSERT_RETRY_PARAMS.merge((retry_params))
137
- Retryable.retryable(**retry_params, &block)
138
- end
134
+ def with_upsert_retry(retry_params, &block)
135
+ retry_params = UPSERT_RETRY_PARAMS.merge((retry_params))
136
+ Retryable.retryable(**retry_params, &block)
137
+ end
139
138
 
140
- # A polyfill for Hash#except for ruby 2.x environments without ActiveSupport
141
- # TODO: delete and use native Hash#except after ruby 2.7 is deprecated.
142
- def except(hash, *keys)
143
- return hash.except(*keys) if hash.respond_to?(:except)
139
+ # A polyfill for Hash#except for ruby 2.x environments without ActiveSupport
140
+ # TODO: delete and use native Hash#except after ruby 2.7 is deprecated.
141
+ def except(hash, *keys)
142
+ return hash.except(*keys) if hash.respond_to?(:except)
144
143
 
145
- hash.each_with_object({}) do |(k, v), res|
146
- res[k] = v unless keys.include?(k)
147
- end
148
- end
144
+ hash.each_with_object({}) do |(k, v), res|
145
+ res[k] = v unless keys.include?(k)
146
+ end
147
+ end
149
148
 
150
- def cleanup_properties(create_properties, update_properties, *props_to_cleanup)
151
- props_to_cleanup = [T.id, T.label] if props_to_cleanup.empty?
152
- [create_properties, update_properties].map do |props|
153
- except(props, props_to_cleanup)
154
- end
155
- end
149
+ def cleanup_properties(create_properties, update_properties, *props_to_cleanup)
150
+ props_to_cleanup = [T.id, T.label] if props_to_cleanup.empty?
151
+ [create_properties, update_properties].map do |props|
152
+ except(props, props_to_cleanup)
156
153
  end
157
154
  end
158
155
  end
@@ -1,83 +1,107 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- module Repository
5
- RETURN_MODES = {
6
- list: :toList,
7
- none: :iterate,
8
- single: :next,
9
- traversal: :nil
10
- }.freeze
11
-
12
- def self.extended(base)
13
- super
14
- base.extend(Grumlin::Shortcuts)
15
- base.include(Repository::InstanceMethods)
16
-
17
- base.shortcuts_from(Grumlin::Shortcuts::Properties)
18
- base.shortcuts_from(Grumlin::Shortcuts::Upserts)
3
+ module Grumlin::Repository
4
+ RETURN_MODES = {
5
+ list: :toList,
6
+ none: :iterate,
7
+ single: :next,
8
+ traversal: :nil
9
+ }.freeze
10
+
11
+ def self.extended(base)
12
+ super
13
+ base.extend(Grumlin::Shortcuts)
14
+ base.include(Grumlin::Repository::InstanceMethods)
15
+
16
+ base.shortcuts_from(Grumlin::Shortcuts::Properties)
17
+ base.shortcuts_from(Grumlin::Shortcuts::Upserts)
18
+ end
19
+
20
+ def self.new
21
+ @repository ||= Class.new do # rubocop:disable Naming/MemoizedInstanceVariableName
22
+ extend Grumlin::Repository
23
+ end.new
24
+ end
25
+
26
+ def inherited(subclass)
27
+ super
28
+ subclass.middlewares = Grumlin::Middlewares::Builder.new do |b|
29
+ b.use(middlewares)
19
30
  end
31
+ end
20
32
 
21
- def self.new
22
- @repository ||= Class.new do # rubocop:disable Naming/MemoizedInstanceVariableName
23
- extend Grumlin::Repository
24
- end.new
33
+ def read_only!
34
+ middlewares do |b|
35
+ b.insert_after Grumlin::Middlewares::ApplyShortcuts, Grumlin::Middlewares::FindMutatingSteps
25
36
  end
37
+ end
26
38
 
27
- def query(name, return_mode: :list, postprocess_with: nil, &query_block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
28
- return_mode = validate_return_mode!(return_mode)
29
- postprocess_with = validate_postprocess_with!(postprocess_with)
39
+ def middlewares
40
+ @middlewares ||= Grumlin::Middlewares::Builder.new do |b|
41
+ b.use(Grumlin.default_middlewares)
42
+ end
30
43
 
31
- define_method name do |*args, query_params: {}, **params, &block|
32
- t = instance_exec(*args, **params, &query_block)
33
- return t if t.nil? || (t.respond_to?(:empty?) && t.empty?)
44
+ yield(@middlewares) if block_given?
45
+ @middlewares
46
+ end
34
47
 
35
- unless t.is_a?(Grumlin::Step)
36
- raise WrongQueryResult,
37
- "queries must return #{Grumlin::Step}, nil or an empty collection. Given: #{t.class}"
38
- end
48
+ def query(name, return_mode: :list, postprocess_with: nil, &query_block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
49
+ return_mode = validate_return_mode!(return_mode)
50
+ postprocess_with = validate_postprocess_with!(postprocess_with)
39
51
 
40
- return block.call(t) unless block.nil?
52
+ define_method name do |*args, query_params: {}, **params, &block|
53
+ t = instance_exec(*args, **params, &query_block)
54
+ return t if t.nil? || (t.respond_to?(:empty?) && t.empty?)
41
55
 
42
- return t.profile.next if query_params[:profile] == true
56
+ unless t.is_a?(Grumlin::Step)
57
+ raise Grumlin::WrongQueryResult,
58
+ "queries must return #{Grumlin::Step}, nil or an empty collection. Given: #{t.class}"
59
+ end
43
60
 
44
- return_mode = self.class.validate_return_mode!(query_params[:return_mode] || return_mode)
61
+ return block.call(t) unless block.nil?
45
62
 
46
- return t if return_mode == :traversal
63
+ return t.profile.next if query_params[:profile] == true
47
64
 
48
- t.public_send(RETURN_MODES[return_mode]).tap do |result|
49
- return postprocess_with.call(result) if postprocess_with.respond_to?(:call)
50
- return send(postprocess_with, result) unless postprocess_with.nil?
51
- end
52
- end
53
- end
65
+ return_mode = self.class.validate_return_mode!(query_params[:return_mode] || return_mode)
54
66
 
55
- def default_vertex_properties(&block)
56
- shortcut :addV, override: true do |*args|
57
- super(*args).props(**block.call(*args)) # rubocop:disable Performance/RedundantBlockCall
58
- end
59
- end
67
+ return t if return_mode == :traversal
60
68
 
61
- def default_edge_properties(&block)
62
- shortcut :addE, override: true do |*args|
63
- super(*args).props(**block.call(*args)) # rubocop:disable Performance/RedundantBlockCall
69
+ t.public_send(RETURN_MODES[return_mode]).tap do |result|
70
+ return postprocess_with.call(result) if postprocess_with.respond_to?(:call)
71
+ return send(postprocess_with, result) unless postprocess_with.nil?
64
72
  end
65
73
  end
74
+ end
66
75
 
67
- def validate_return_mode!(return_mode)
68
- return return_mode if RETURN_MODES.include?(return_mode)
76
+ def default_vertex_properties(&block)
77
+ shortcut :addV, override: true do |*args|
78
+ super(*args).props(**block.call(*args)) # rubocop:disable Performance/RedundantBlockCall
79
+ end
80
+ end
69
81
 
70
- raise ArgumentError, "unsupported return mode #{return_mode}. Supported modes: #{RETURN_MODES.keys}"
82
+ def default_edge_properties(&block)
83
+ shortcut :addE, override: true do |*args|
84
+ super(*args).props(**block.call(*args)) # rubocop:disable Performance/RedundantBlockCall
71
85
  end
86
+ end
72
87
 
73
- def validate_postprocess_with!(postprocess_with)
74
- if postprocess_with.nil? || postprocess_with.is_a?(Symbol) ||
75
- postprocess_with.is_a?(String) || postprocess_with.respond_to?(:call)
76
- return postprocess_with
77
- end
88
+ def validate_return_mode!(return_mode)
89
+ return return_mode if RETURN_MODES.include?(return_mode)
78
90
 
79
- raise ArgumentError,
80
- "postprocess_with must be a String, Symbol or a callable object, given: #{postprocess_with.class}"
91
+ raise ArgumentError, "unsupported return mode #{return_mode}. Supported modes: #{RETURN_MODES.keys}"
92
+ end
93
+
94
+ def validate_postprocess_with!(postprocess_with)
95
+ if postprocess_with.nil? || postprocess_with.is_a?(Symbol) ||
96
+ postprocess_with.is_a?(String) || postprocess_with.respond_to?(:call)
97
+ return postprocess_with
81
98
  end
99
+
100
+ raise ArgumentError,
101
+ "postprocess_with must be a String, Symbol or a callable object, given: #{postprocess_with.class}"
82
102
  end
103
+
104
+ protected
105
+
106
+ attr_writer :middlewares
83
107
  end
@@ -1,80 +1,78 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grumlin
4
- class RequestDispatcher
5
- attr_reader :requests
3
+ class Grumlin::RequestDispatcher
4
+ attr_reader :requests
6
5
 
7
- SUCCESS = {
8
- 200 => :success,
9
- 204 => :no_content,
10
- 206 => :partial_content
11
- }.freeze
6
+ SUCCESS = {
7
+ 200 => :success,
8
+ 204 => :no_content,
9
+ 206 => :partial_content
10
+ }.freeze
12
11
 
13
- class DispatcherError < Grumlin::Error; end
12
+ class DispatcherError < Grumlin::Error; end
14
13
 
15
- class RequestAlreadyAddedError < DispatcherError; end
14
+ class RequestAlreadyAddedError < DispatcherError; end
16
15
 
17
- class UnknownRequestError < DispatcherError; end
16
+ class UnknownRequestError < DispatcherError; end
18
17
 
19
- def initialize
20
- @requests = {}
18
+ def initialize
19
+ @requests = {}
20
+ end
21
+
22
+ def add_request(request)
23
+ raise RequestAlreadyAddedError if @requests.include?(request[:requestId])
24
+
25
+ Async::Channel.new.tap do |channel|
26
+ @requests[request[:requestId]] = { request: request, result: [], channel: channel }
21
27
  end
28
+ end
22
29
 
23
- def add_request(request)
24
- raise RequestAlreadyAddedError if @requests.include?(request[:requestId])
30
+ # builds a response object, when it's ready sends it to the client via a channel
31
+ # TODO: sometimes response does not include requestID, no idea how to handle it so far.
32
+ def add_response(response) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
33
+ request_id = response[:requestId]
34
+ raise UnknownRequestError unless ongoing_request?(request_id)
25
35
 
26
- Async::Channel.new.tap do |channel|
27
- @requests[request[:requestId]] = { request: request, result: [], channel: channel }
36
+ begin
37
+ request = @requests[request_id]
38
+
39
+ Grumlin::RequestErrorFactory.build(request, response).tap do |err|
40
+ raise err unless err.nil?
28
41
  end
29
- end
30
42
 
31
- # builds a response object, when it's ready sends it to the client via a channel
32
- # TODO: sometimes response does not include requestID, no idea how to handle it so far.
33
- def add_response(response) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
34
- request_id = response[:requestId]
35
- raise UnknownRequestError unless ongoing_request?(request_id)
36
-
37
- begin
38
- request = @requests[request_id]
39
-
40
- RequestErrorFactory.build(request, response).tap do |err|
41
- raise err unless err.nil?
42
- end
43
-
44
- case SUCCESS[response.dig(:status, :code)]
45
- when :success
46
- request[:result] << response.dig(:result, :data)
47
- request[:channel] << request[:result]
48
- close_request(request_id)
49
- when :partial_content then request[:result] << response.dig(:result, :data)
50
- when :no_content
51
- request[:channel] << []
52
- close_request(request_id)
53
- end
54
- rescue StandardError => e
55
- request[:channel].exception(e)
43
+ case SUCCESS[response.dig(:status, :code)]
44
+ when :success
45
+ request[:result] << response.dig(:result, :data)
46
+ request[:channel] << request[:result]
47
+ close_request(request_id)
48
+ when :partial_content then request[:result] << response.dig(:result, :data)
49
+ when :no_content
50
+ request[:channel] << []
56
51
  close_request(request_id)
57
52
  end
53
+ rescue StandardError => e
54
+ request[:channel].exception(e)
55
+ close_request(request_id)
58
56
  end
57
+ end
59
58
 
60
- def ongoing_request?(request_id)
61
- @requests.include?(request_id)
62
- end
59
+ def ongoing_request?(request_id)
60
+ @requests.include?(request_id)
61
+ end
63
62
 
64
- def clear
65
- @requests.each do |_id, request|
66
- request[:channel].close!
67
- end
68
- @requests.clear
63
+ def clear
64
+ @requests.each do |_id, request|
65
+ request[:channel].close!
69
66
  end
67
+ @requests.clear
68
+ end
70
69
 
71
- private
70
+ private
72
71
 
73
- def close_request(request_id)
74
- raise UnknownRequestError unless ongoing_request?(request_id)
72
+ def close_request(request_id)
73
+ raise UnknownRequestError unless ongoing_request?(request_id)
75
74
 
76
- request = @requests.delete(request_id)
77
- request[:channel].close
78
- end
75
+ request = @requests.delete(request_id)
76
+ request[:channel].close
79
77
  end
80
78
  end