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,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::Action)
36
- raise WrongQueryResult,
37
- "queries must return #{Grumlin::Action}, 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