brute 5.0.5 → 6.0.0

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/lib/brute/compaction/middleware/sliding_window.rb +170 -0
  3. data/lib/brute/compaction/middleware/strategy.rb +137 -0
  4. data/lib/brute/compaction/middleware/tool_results.rb +146 -0
  5. data/lib/brute/compaction/summarize.rb +349 -0
  6. data/lib/brute/compaction/transcript.rb +166 -0
  7. data/lib/brute/compaction.rb +57 -0
  8. data/lib/brute/completion/lang_chain.rb +34 -31
  9. data/lib/brute/completion/llmrb.rb +31 -29
  10. data/lib/brute/completion/open_router.rb +43 -40
  11. data/lib/brute/completion/ruby_llm.rb +28 -27
  12. data/lib/brute/contrib/otel.rb +83 -51
  13. data/lib/brute/env.rb +54 -0
  14. data/lib/brute/eval/case.rb +254 -0
  15. data/lib/brute/eval/suite.rb +181 -0
  16. data/lib/brute/eval/transcript.rb +147 -0
  17. data/lib/brute/eval/world.rb +106 -0
  18. data/lib/brute/eval.rb +48 -0
  19. data/lib/brute/events/handler.rb +3 -1
  20. data/lib/brute/events/prefixed_terminal_output.rb +3 -1
  21. data/lib/brute/events/terminal_output_handler.rb +1 -1
  22. data/lib/brute/hooks.rb +89 -102
  23. data/lib/brute/message_transport/anthropic.rb +13 -3
  24. data/lib/brute/message_transport/llm.rb +8 -4
  25. data/lib/brute/message_transport/open_router.rb +30 -16
  26. data/lib/brute/message_transport/openai.rb +14 -7
  27. data/lib/brute/message_transport/ruby_llm.rb +21 -17
  28. data/lib/brute/message_transport/ruby_open_ai.rb +59 -57
  29. data/lib/brute/messages.rb +15 -8
  30. data/lib/brute/middleware/000_base.rb +6 -6
  31. data/lib/brute/middleware/002_session_log.rb +12 -4
  32. data/lib/brute/middleware/008_checkpoint.rb +27 -19
  33. data/lib/brute/middleware/010_max_iterations.rb +1 -1
  34. data/lib/brute/middleware/020_system_prompt.rb +1 -1
  35. data/lib/brute/middleware/040_default_compaction_pipeline.rb +356 -0
  36. data/lib/brute/middleware/{070_tool_pipeline.rb → 070_default_tool_pipeline.rb} +49 -58
  37. data/lib/brute/prompt_template.rb +19 -15
  38. data/lib/brute/prompts/base.rb +19 -10
  39. data/lib/brute/prompts/environment.rb +3 -1
  40. data/lib/brute/prompts/instructions.rb +9 -7
  41. data/lib/brute/prompts/skills.rb +5 -3
  42. data/lib/brute/rack/adapter.rb +40 -23
  43. data/lib/brute/skill.rb +136 -89
  44. data/lib/brute/system_prompt.rb +9 -9
  45. data/lib/brute/token_counter/approximate.rb +54 -0
  46. data/lib/brute/token_counter/tiktoken.rb +80 -0
  47. data/lib/brute/token_counter.rb +150 -0
  48. data/lib/brute/tool.rb +10 -6
  49. data/lib/brute/tools/adapter.rb +57 -47
  50. data/lib/brute/tools/fs/snapshot_store.rb +5 -1
  51. data/lib/brute/tools/fs_patch.rb +16 -8
  52. data/lib/brute/tools/fs_read.rb +107 -80
  53. data/lib/brute/tools/fs_remove.rb +6 -2
  54. data/lib/brute/tools/fs_search.rb +14 -4
  55. data/lib/brute/tools/fs_undo.rb +6 -2
  56. data/lib/brute/tools/fs_write.rb +5 -1
  57. data/lib/brute/tools/net_fetch.rb +6 -2
  58. data/lib/brute/tools/question.rb +42 -39
  59. data/lib/brute/tools/shell.rb +20 -5
  60. data/lib/brute/tools/skill_load.rb +46 -41
  61. data/lib/brute/tools/sub_agent.rb +2 -2
  62. data/lib/brute/tools/todo_write.rb +19 -15
  63. data/lib/brute/truncation.rb +68 -43
  64. data/lib/brute/turn/agent_pipeline.rb +23 -13
  65. data/lib/brute/turn/compaction_pipeline.rb +123 -0
  66. data/lib/brute/turn/pipeline.rb +87 -96
  67. data/lib/brute/turn/tool_pipeline.rb +4 -3
  68. data/lib/brute/usage_detection/llmrb.rb +18 -14
  69. data/lib/brute/usage_detection/open_router.rb +20 -17
  70. data/lib/brute/usage_detection/ruby_llm.rb +18 -14
  71. data/lib/brute/usage_detection/usage.rb +10 -1
  72. data/lib/brute/utils/diff.rb +18 -10
  73. data/lib/brute/version.rb +1 -1
  74. data/lib/brute.rb +25 -12
  75. data/lib/brute_cli/providers/shell.rb +32 -29
  76. data/lib/brute_cli/providers/shell_response.rb +20 -18
  77. metadata +48 -5
  78. data/lib/brute/middleware/040_compaction_check.rb +0 -157
  79. data/lib/brute/middleware/event_handler.rb +0 -27
data/lib/brute.rb CHANGED
@@ -12,13 +12,13 @@ require_relative 'brute/version'
12
12
 
13
13
  module Brute
14
14
  LOGO = <<-LOGO
15
- .o8 .
16
- "888 .o8
17
- 888oooo. oooo d8b oooo oooo .o888oo .ooooo.
18
- d88' `88b `888""8P `888 `888 888 d88' `88b
19
- 888 888 888 888 888 888 888ooo888
20
- 888 888 888 888 888 888 . 888 .o
21
- `Y8bod8P' d888b `V88V"V8P' "888" `Y8bod8P'
15
+ .o8 .
16
+ "888 .o8
17
+ 888oooo. oooo d8b oooo oooo .o888oo .ooooo.
18
+ d88' `88b `888""8P `888 `888 888 d88' `88b
19
+ 888 888 888 888 888 888 888ooo888
20
+ 888 888 888 888 888 888 . 888 .o
21
+ `Y8bod8P' d888b `V88V"V8P' "888" `Y8bod8P'
22
22
  LOGO
23
23
 
24
24
  # NOTE: Brute owns no LLM configuration and no LLM library. All
@@ -42,7 +42,7 @@ module Brute
42
42
  #
43
43
  # agent = Brute.agent
44
44
  # .use(Brute::Middleware::SystemPrompt)
45
- # .use(Brute::Middleware::ToolPipeline, tools: Brute::Tools::ALL)
45
+ # .use(Brute::Middleware::DefaultToolPipeline, tools: Brute::Tools::ALL)
46
46
  # .run ->(env) { ... } # the LLM-call proc (provider/model/creds here)
47
47
  #
48
48
  # agent.start("what changed?")
@@ -68,7 +68,9 @@ module Brute
68
68
  #
69
69
  def self.load_agent(path = "agent.ru")
70
70
  path = File.expand_path(path)
71
- raise ArgumentError, "no such agent file: #{path}" unless File.file?(path)
71
+ unless File.file?(path)
72
+ raise ArgumentError, "no such agent file: #{path}"
73
+ end
72
74
 
73
75
  Brute::Turn::AgentPipeline.parse_file(path)
74
76
  end
@@ -93,7 +95,14 @@ end
93
95
  require_relative "brute/version"
94
96
  require_relative "brute/hooks"
95
97
  require_relative "brute/messages"
98
+ require_relative "brute/env"
96
99
  require_relative "brute/middleware/000_base"
100
+ require_relative "brute/compaction"
101
+ require_relative "brute/compaction/transcript"
102
+ require_relative "brute/compaction/middleware/strategy"
103
+ require_relative "brute/compaction/middleware/tool_results"
104
+ require_relative "brute/compaction/middleware/sliding_window"
105
+ require_relative "brute/compaction/summarize"
97
106
  require_relative "brute/completion/open_router"
98
107
  require_relative "brute/completion/lang_chain"
99
108
  require_relative "brute/completion/llmrb"
@@ -124,10 +133,9 @@ require_relative "brute/middleware/008_checkpoint"
124
133
  require_relative "brute/middleware/010_max_iterations"
125
134
  require_relative "brute/middleware/020_system_prompt"
126
135
  require_relative "brute/middleware/025_skills"
127
- require_relative "brute/middleware/040_compaction_check"
136
+ require_relative "brute/middleware/040_default_compaction_pipeline"
128
137
  require_relative "brute/middleware/060_questions"
129
- require_relative "brute/middleware/070_tool_pipeline"
130
- require_relative "brute/middleware/event_handler"
138
+ require_relative "brute/middleware/070_default_tool_pipeline"
131
139
  require_relative "brute/middleware/user_queue"
132
140
  require_relative "brute/prompt_template"
133
141
  require_relative "brute/prompts"
@@ -157,6 +165,9 @@ require_relative "brute/prompts/tool_usage"
157
165
  require_relative "brute/rack/adapter"
158
166
  require_relative "brute/skill"
159
167
  require_relative "brute/system_prompt"
168
+ require_relative "brute/token_counter"
169
+ require_relative "brute/token_counter/approximate"
170
+ require_relative "brute/token_counter/tiktoken"
160
171
  require_relative "brute/tool"
161
172
  require_relative "brute/tools"
162
173
  require_relative "brute/tools/adapter"
@@ -179,7 +190,9 @@ require_relative "brute/tools/todo_write"
179
190
  require_relative "brute/truncation"
180
191
  require_relative "brute/turn/agent_pipeline"
181
192
  require_relative "brute/turn/pipeline"
193
+ require_relative "brute/turn/compaction_pipeline"
182
194
  require_relative "brute/turn/tool_pipeline"
195
+ require_relative "brute/eval"
183
196
  require_relative "brute/utils/diff"
184
197
 
185
198
  # gangsta g-dogg bruh...
@@ -44,12 +44,13 @@ module BruteCLI
44
44
 
45
45
  # nil text means we received tool results (second call) —
46
46
  # return an empty assistant response so the agent loop exits.
47
- return ShellResponse.new(model: model, tools: tools) if text.nil?
48
-
49
- wrap = INTERPRETERS.fetch(model, INTERPRETERS["bash"])
50
- command = wrap.call(text)
51
-
52
- ShellResponse.new(command: command, model: model, tools: tools)
47
+ if text.nil?
48
+ ShellResponse.new(model: model, tools: tools)
49
+ else
50
+ wrap = INTERPRETERS.fetch(model, INTERPRETERS["bash"])
51
+ command = wrap.call(text)
52
+ ShellResponse.new(command: command, model: model, tools: tools)
53
+ end
53
54
  end
54
55
 
55
56
  # For the REPL model picker: provider.models.all.select(&:chat?)
@@ -61,38 +62,40 @@ module BruteCLI
61
62
 
62
63
  private
63
64
 
64
- # Extract the user's text from the messages array.
65
- # Returns nil when the messages contain tool results (the second
66
- # round-trip) so #complete knows to return an empty response.
67
- def extract_text(prompt)
68
- case prompt
69
- when String
70
- prompt
71
- when ::Array
72
- return nil if prompt.any? { |m| m.respond_to?(:role) && m.role == :tool }
73
-
74
- user_msg = prompt.reverse_each.find { |m| m.respond_to?(:role) && m.role == :user }
75
- user_msg&.content.to_s
76
- else
77
- prompt.to_s
65
+ # Extract the user's text from the messages array.
66
+ # Returns nil when the messages contain tool results (the second
67
+ # round-trip) so #complete knows to return an empty response.
68
+ def extract_text(prompt)
69
+ case prompt
70
+ when String
71
+ prompt
72
+ when ::Array
73
+ if prompt.any? { |m| m.respond_to?(:role) && m.role == :tool }
74
+ nil
75
+ else
76
+ user_msg = prompt.reverse_each.find { |m| m.respond_to?(:role) && m.role == :user }
77
+ user_msg&.content.to_s
78
+ end
79
+ else
80
+ prompt.to_s
81
+ end
78
82
  end
79
- end
80
83
 
81
84
  # ── ModelList ──────────────────────────────────────────────────
82
85
 
83
86
  # Minimal object that quacks like provider.models so the REPL's
84
87
  # fetch_models can call provider.models.all.select(&:chat?).
85
- class ModelList
86
- ModelEntry = Struct.new(:id, :chat?, keyword_init: true)
88
+ class ModelList
89
+ ModelEntry = Struct.new(:id, :chat?, keyword_init: true)
87
90
 
88
- def initialize(names)
89
- @entries = names.map { |n| ModelEntry.new(id: n, chat?: true) }
90
- end
91
+ def initialize(names)
92
+ @entries = names.map { |n| ModelEntry.new(id: n, chat?: true) }
93
+ end
91
94
 
92
- def all
93
- @entries
95
+ def all
96
+ @entries
97
+ end
94
98
  end
95
- end
96
99
  end
97
100
  end
98
101
  end
@@ -25,22 +25,24 @@ module BruteCLI
25
25
  end
26
26
 
27
27
  def messages
28
- return [empty_assistant] if @command.nil?
29
-
30
- call_id = "shell_#{SecureRandom.hex(8)}"
31
- tool_calls = [
32
- Brute::ToolCall.new(
33
- id: call_id,
34
- name: "shell",
35
- arguments: { "command" => @command },
28
+ if @command.nil?
29
+ [empty_assistant]
30
+ else
31
+ call_id = "shell_#{SecureRandom.hex(8)}"
32
+ tool_calls = [
33
+ Brute::ToolCall.new(
34
+ id: call_id,
35
+ name: "shell",
36
+ arguments: { "command" => @command },
37
+ )
38
+ ]
39
+ [Brute::Message.new(
40
+ role: :assistant,
41
+ content: "",
42
+ tool_calls: tool_calls,
36
43
  )
37
- ]
38
-
39
- [Brute::Message.new(
40
- role: :assistant,
41
- content: "",
42
- tool_calls: tool_calls,
43
- )]
44
+ ]
45
+ end
44
46
  end
45
47
  alias_method :choices, :messages
46
48
 
@@ -83,9 +85,9 @@ module BruteCLI
83
85
 
84
86
  private
85
87
 
86
- def empty_assistant
87
- Brute::Message.new(role: :assistant, content: "")
88
- end
88
+ def empty_assistant
89
+ Brute::Message.new(role: :assistant, content: "")
90
+ end
89
91
  end
90
92
  end
91
93
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brute
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.5
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brute Contributors
@@ -219,6 +219,20 @@ dependencies:
219
219
  - - ">="
220
220
  - !ruby/object:Gem::Version
221
221
  version: '0'
222
+ - !ruby/object:Gem::Dependency
223
+ name: rubocop
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - ">="
227
+ - !ruby/object:Gem::Version
228
+ version: '0'
229
+ type: :development
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ version: '0'
222
236
  - !ruby/object:Gem::Dependency
223
237
  name: rake
224
238
  requirement: !ruby/object:Gem::Requirement
@@ -233,6 +247,20 @@ dependencies:
233
247
  - - "~>"
234
248
  - !ruby/object:Gem::Version
235
249
  version: '13.0'
250
+ - !ruby/object:Gem::Dependency
251
+ name: rdoc
252
+ requirement: !ruby/object:Gem::Requirement
253
+ requirements:
254
+ - - "~>"
255
+ - !ruby/object:Gem::Version
256
+ version: '8.0'
257
+ type: :development
258
+ prerelease: false
259
+ version_requirements: !ruby/object:Gem::Requirement
260
+ requirements:
261
+ - - "~>"
262
+ - !ruby/object:Gem::Version
263
+ version: '8.0'
236
264
  - !ruby/object:Gem::Dependency
237
265
  name: scampi
238
266
  requirement: !ruby/object:Gem::Requirement
@@ -282,12 +310,24 @@ extensions: []
282
310
  extra_rdoc_files: []
283
311
  files:
284
312
  - lib/brute.rb
313
+ - lib/brute/compaction.rb
314
+ - lib/brute/compaction/middleware/sliding_window.rb
315
+ - lib/brute/compaction/middleware/strategy.rb
316
+ - lib/brute/compaction/middleware/tool_results.rb
317
+ - lib/brute/compaction/summarize.rb
318
+ - lib/brute/compaction/transcript.rb
285
319
  - lib/brute/completion/lang_chain.rb
286
320
  - lib/brute/completion/llmrb.rb
287
321
  - lib/brute/completion/open_router.rb
288
322
  - lib/brute/completion/ruby_llm.rb
289
323
  - lib/brute/contrib/log_file.rb
290
324
  - lib/brute/contrib/otel.rb
325
+ - lib/brute/env.rb
326
+ - lib/brute/eval.rb
327
+ - lib/brute/eval/case.rb
328
+ - lib/brute/eval/suite.rb
329
+ - lib/brute/eval/transcript.rb
330
+ - lib/brute/eval/world.rb
291
331
  - lib/brute/events/handler.rb
292
332
  - lib/brute/events/prefixed_terminal_output.rb
293
333
  - lib/brute/events/terminal_output_handler.rb
@@ -310,10 +350,9 @@ files:
310
350
  - lib/brute/middleware/010_max_iterations.rb
311
351
  - lib/brute/middleware/020_system_prompt.rb
312
352
  - lib/brute/middleware/025_skills.rb
313
- - lib/brute/middleware/040_compaction_check.rb
353
+ - lib/brute/middleware/040_default_compaction_pipeline.rb
314
354
  - lib/brute/middleware/060_questions.rb
315
- - lib/brute/middleware/070_tool_pipeline.rb
316
- - lib/brute/middleware/event_handler.rb
355
+ - lib/brute/middleware/070_default_tool_pipeline.rb
317
356
  - lib/brute/middleware/user_queue.rb
318
357
  - lib/brute/prompt_template.rb
319
358
  - lib/brute/prompts.rb
@@ -362,6 +401,9 @@ files:
362
401
  - lib/brute/rack/adapter.rb
363
402
  - lib/brute/skill.rb
364
403
  - lib/brute/system_prompt.rb
404
+ - lib/brute/token_counter.rb
405
+ - lib/brute/token_counter/approximate.rb
406
+ - lib/brute/token_counter/tiktoken.rb
365
407
  - lib/brute/tool.rb
366
408
  - lib/brute/tools.rb
367
409
  - lib/brute/tools/adapter.rb
@@ -383,6 +425,7 @@ files:
383
425
  - lib/brute/tools/todo_write.rb
384
426
  - lib/brute/truncation.rb
385
427
  - lib/brute/turn/agent_pipeline.rb
428
+ - lib/brute/turn/compaction_pipeline.rb
386
429
  - lib/brute/turn/pipeline.rb
387
430
  - lib/brute/turn/tool_pipeline.rb
388
431
  - lib/brute/usage_detection/lang_chain.rb
@@ -397,7 +440,7 @@ files:
397
440
  - lib/brute_cli/providers/shell_response.rb
398
441
  homepage: https://github.com/general-intelligence-systems/brute
399
442
  licenses:
400
- - MIT
443
+ - Apache-2.0
401
444
  metadata:
402
445
  documentation_uri: https://general-intelligence-systems.github.io/brute/
403
446
  rdoc_options: []
@@ -1,157 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/setup"
4
- require "brute"
5
-
6
- module Brute
7
- module Middleware
8
- # Checks context size after each LLM call and triggers compaction
9
- # when thresholds are exceeded.
10
- #
11
- # It should add a compaction event to the logs with the context token
12
- # total listed... this way a model that supports extra context can
13
- # include the compaction as well as the previous messages...
14
- #
15
- # Or an LLM that doesn't support it can just use the messages
16
- # that come after the compaction
17
- #
18
- class CompactionCheck < Brute::Middleware::Base
19
- def initialize(app, compactor: nil, system_prompt:, **compactor_opts)
20
- @app = app
21
- @compactor = compactor
22
- @compactor_opts = compactor_opts
23
- @system_prompt = system_prompt
24
- end
25
-
26
- def call(env)
27
- #@compactor ||= Compactor.new(env[:provider], **@compactor_opts)
28
-
29
- #messages = env[:messages]
30
- #usage = env[:metadata].dig(:tokens, :last_call)
31
-
32
- #if @compactor.should_compact?(messages, usage: usage)
33
- # result = @compactor.compact(messages)
34
- # if result
35
- # summary_text, _recent = result
36
- # env[:metadata][:compaction] = {
37
- # messages_before: messages.size,
38
- # timestamp: Time.now.iso8601,
39
- # }
40
- # # Replace the message history with the summary
41
- # env[:messages] = [
42
- # Brute::Message.new(role: :system, content: @system_prompt),
43
- # Brute::Message.new(role: :user, content: "[Previous conversation summary]\n\n#{summary_text}"),
44
- # ]
45
- # end
46
- #end
47
-
48
- @app.call(env)
49
- end
50
-
51
- # Context compaction service. When the conversation grows past configurable
52
- # thresholds, older messages are summarized into a condensed form and the
53
- # original messages are dropped, keeping the context window manageable.
54
- class Compactor
55
- DEFAULTS = {
56
- token_threshold: 100_000, # Compact when estimated tokens exceed this
57
- message_threshold: 200, # Compact when message count exceeds this
58
- retention_window: 6, # Minimum recent messages to always keep
59
- summary_model: nil, # Model for summarization (uses agent's model if nil)
60
- }.freeze
61
-
62
- attr_reader :config
63
-
64
- def initialize(provider, **opts)
65
- @provider = provider
66
- @config = DEFAULTS.merge(opts)
67
- end
68
-
69
- # Check whether compaction should run based on current context state.
70
- def should_compact?(messages, usage: nil)
71
- return true if messages.size > @config[:message_threshold]
72
- return true if usage && (usage[:total] || 0) > @config[:token_threshold]
73
- false
74
- end
75
-
76
- # Compact the message history by summarizing older messages.
77
- #
78
- # Returns [summary_message, kept_messages] — the caller rebuilds
79
- # the context from these.
80
- def compact(messages)
81
- total = messages.size
82
- keep_count = [@config[:retention_window], total].min
83
- return nil if total <= keep_count
84
-
85
- old_messages = messages[0...(total - keep_count)]
86
- recent_messages = messages[(total - keep_count)..]
87
-
88
- summary_text = summarize(old_messages)
89
-
90
- [summary_text, recent_messages]
91
- end
92
-
93
- private
94
-
95
- def summarize(messages)
96
- # Build a condensed representation of the conversation for the summarizer
97
- conversation_text = messages.map { |m|
98
- role = if m.respond_to?(:role)
99
- m.role.to_s
100
- else
101
- "unknown"
102
- end
103
- content = if m.respond_to?(:content)
104
- m.content.to_s[0..1000]
105
- else
106
- m.to_s[0..1000]
107
- end
108
-
109
- # Include tool call info for assistant messages
110
- tool_info = ""
111
- if m.respond_to?(:functions) && m.functions&.any?
112
- calls = m.functions.map { |f| "#{f.name}(#{f.arguments.to_s[0..200]})" }
113
- tool_info = " [tools: #{calls.join(", ")}]"
114
- end
115
-
116
- "#{role}:#{tool_info} #{content}"
117
- }.join("\n---\n")
118
-
119
- prompt = <<~PROMPT
120
- Summarize this conversation history for context continuity. The summary will replace
121
- these messages in the context window, so include everything the agent needs to continue
122
- working effectively.
123
-
124
- Structure your summary as:
125
- ## Goal
126
- What the user asked for.
127
-
128
- ## Progress
129
- - Files read, created, or modified (list paths)
130
- - Commands executed and their outcomes
131
- - Key decisions made
132
-
133
- ## Current State
134
- Where things stand right now — what's done and what remains.
135
-
136
- ## Next Steps
137
- What should happen next based on the conversation.
138
-
139
- ---
140
- CONVERSATION:
141
- #{conversation_text}
142
- PROMPT
143
-
144
- model = @config[:summary_model] || "claude-sonnet-4-20250514"
145
- res = @provider.complete(prompt, model: model)
146
- res.content
147
- end
148
- end
149
- end
150
- end
151
- end
152
-
153
- __END__
154
-
155
- describe "brute/middleware/040_compaction_check" do
156
- # not implemented
157
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'brute'
5
-
6
- module Brute
7
- module Middleware
8
- class EventHandler < Brute::Middleware::Base
9
- def initialize(app, handler_class:, **opts)
10
- @app = app
11
- @handler_class = handler_class
12
- @opts = opts
13
- end
14
-
15
- def call(env)
16
- env[:events] = @handler_class.new(env[:events], **@opts)
17
- @app.call(env)
18
- end
19
- end
20
- end
21
- end
22
-
23
- __END__
24
-
25
- describe "brute/middleware/event_handler" do
26
- # not implemented
27
- end