rooibos 0.5.0 → 0.6.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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +9 -5
  3. data/.builds/ruby-3.3.yml +9 -5
  4. data/.builds/ruby-3.4.yml +9 -5
  5. data/.builds/ruby-4.0.0.yml +9 -5
  6. data/AGENTS.md +1 -1
  7. data/CHANGELOG.md +46 -0
  8. data/README.md +2 -2
  9. data/README.rdoc +374 -0
  10. data/REUSE.toml +5 -0
  11. data/Rakefile +1 -1
  12. data/doc/best_practices/forms_and_validation.md +20 -0
  13. data/doc/best_practices/http_workflows.md +20 -0
  14. data/doc/best_practices/index.md +26 -0
  15. data/doc/best_practices/lists_and_tables.md +20 -0
  16. data/doc/best_practices/modal_dialogs.md +20 -0
  17. data/doc/best_practices/no_stateful_widgets.md +184 -0
  18. data/doc/best_practices/orchestration.md +20 -0
  19. data/doc/best_practices/streaming_data.md +20 -0
  20. data/doc/contributors/design/commands_and_outlets.md +1 -1
  21. data/doc/contributors/documentation_plan.md +616 -0
  22. data/doc/contributors/documentation_stub_audit.md +112 -0
  23. data/doc/contributors/documentation_style.md +275 -0
  24. data/doc/contributors/e2e_pty.md +168 -0
  25. data/doc/contributors/specs/earliest_tutorial_steps_per_story.md +70 -0
  26. data/doc/contributors/specs/file_browser.md +789 -0
  27. data/doc/contributors/specs/file_browser_stories.md +774 -0
  28. data/doc/contributors/specs/tutorials_to_stories.rb +167 -0
  29. data/doc/contributors/todo/scrollbar.md +118 -0
  30. data/doc/contributors/tutorial_old/01_project_setup.md +20 -0
  31. data/doc/contributors/tutorial_old/02_hello_world.md +24 -0
  32. data/doc/contributors/tutorial_old/03_adding_state.md +26 -0
  33. data/doc/contributors/tutorial_old/06_organizing_your_code.md +20 -0
  34. data/doc/contributors/tutorial_old/07_your_first_command.md +21 -0
  35. data/doc/contributors/tutorial_old/08_the_preview_pane.md +20 -0
  36. data/doc/contributors/tutorial_old/09_loading_states.md +20 -0
  37. data/doc/contributors/tutorial_old/10_testing_your_app.md +20 -0
  38. data/doc/contributors/tutorial_old/11_polish_and_refine.md +20 -0
  39. data/doc/contributors/tutorial_old/12_going_further.md +20 -0
  40. data/doc/contributors/tutorial_old/index.md +20 -0
  41. data/doc/essentials/commands.md +20 -0
  42. data/doc/essentials/index.md +31 -0
  43. data/doc/essentials/messages.md +21 -0
  44. data/doc/essentials/models.md +21 -0
  45. data/doc/essentials/shortcuts.md +19 -0
  46. data/doc/essentials/the_elm_architecture.md +24 -0
  47. data/doc/essentials/the_runtime.md +21 -0
  48. data/doc/essentials/update_functions.md +20 -0
  49. data/doc/essentials/views.md +22 -0
  50. data/doc/getting_started/for_go_developers.md +16 -0
  51. data/doc/getting_started/for_python_developers.md +16 -0
  52. data/doc/getting_started/for_react_developers.md +17 -0
  53. data/doc/getting_started/index.md +52 -0
  54. data/doc/getting_started/install.md +20 -0
  55. data/doc/getting_started/quickstart.md +9 -45
  56. data/doc/getting_started/ruby_primer.md +19 -0
  57. data/doc/getting_started/why_rooibos.md +20 -0
  58. data/doc/index.md +79 -11
  59. data/doc/scaling_up/async_patterns.md +20 -0
  60. data/doc/scaling_up/command_composition.md +20 -0
  61. data/doc/scaling_up/custom_commands.md +21 -0
  62. data/doc/scaling_up/fractal_architecture.md +20 -0
  63. data/doc/scaling_up/index.md +30 -0
  64. data/doc/scaling_up/message_routing.md +20 -0
  65. data/doc/scaling_up/ractor_safety.md +20 -0
  66. data/doc/scaling_up/testing.md +21 -0
  67. data/doc/troubleshooting/common_errors.md +20 -0
  68. data/doc/troubleshooting/debugging.md +21 -0
  69. data/doc/troubleshooting/index.md +23 -0
  70. data/doc/troubleshooting/performance.md +20 -0
  71. data/doc/tutorial/01_project_setup.md +44 -0
  72. data/doc/tutorial/02_hello_world.md +45 -0
  73. data/doc/tutorial/03_static_file_list.md +44 -0
  74. data/doc/tutorial/04_arrow_navigation.md +47 -0
  75. data/doc/tutorial/05_real_files.md +45 -0
  76. data/doc/tutorial/06_safe_refactoring.md +21 -0
  77. data/doc/tutorial/07_red_first_tdd.md +26 -0
  78. data/doc/tutorial/08_file_metadata.md +42 -0
  79. data/doc/tutorial/09_text_preview.md +44 -0
  80. data/doc/tutorial/10_directory_tree.md +42 -0
  81. data/doc/tutorial/11_pane_focus.md +40 -0
  82. data/doc/tutorial/12_sorting.md +41 -0
  83. data/doc/tutorial/13_filtering.md +43 -0
  84. data/doc/tutorial/14_toggle_hidden.md +41 -0
  85. data/doc/tutorial/15_text_input_widget.md +43 -0
  86. data/doc/tutorial/16_rename_files.md +42 -0
  87. data/doc/tutorial/17_confirmation_dialogs.md +43 -0
  88. data/doc/tutorial/18_progress_indicators.md +43 -0
  89. data/doc/tutorial/19_atomic_operations.md +42 -0
  90. data/doc/tutorial/20_external_editor.md +42 -0
  91. data/doc/tutorial/21_modal_overlays.md +41 -0
  92. data/doc/tutorial/22_error_handling.md +43 -0
  93. data/doc/tutorial/23_terminal_capabilities.md +53 -0
  94. data/doc/tutorial/24_mouse_events.md +43 -0
  95. data/doc/tutorial/25_resize_events.md +43 -0
  96. data/doc/tutorial/26_loading_states.md +42 -0
  97. data/doc/tutorial/27_performance.md +43 -0
  98. data/doc/tutorial/28_color_schemes.md +47 -0
  99. data/doc/tutorial/29_configuration.md +124 -0
  100. data/doc/tutorial/30_going_further.md +17 -0
  101. data/doc/tutorial/index.md +17 -0
  102. data/examples/app_file_browser/app.rb +40 -0
  103. data/examples/app_fractal_dashboard/dashboard/update_manual.rb +7 -7
  104. data/examples/app_fractal_dashboard/fragments/custom_shell_input.rb +5 -5
  105. data/examples/app_fractal_dashboard/fragments/custom_shell_modal.rb +1 -1
  106. data/examples/app_fractal_dashboard/fragments/disk_usage.rb +2 -2
  107. data/examples/app_fractal_dashboard/fragments/network_panel.rb +4 -4
  108. data/examples/app_fractal_dashboard/fragments/ping.rb +2 -2
  109. data/examples/app_fractal_dashboard/fragments/stats_panel.rb +4 -4
  110. data/examples/app_fractal_dashboard/fragments/system_info.rb +2 -2
  111. data/examples/app_fractal_dashboard/fragments/uptime.rb +2 -2
  112. data/examples/verify_website_first_app/app.rb +85 -0
  113. data/examples/verify_website_hello_mvu/app.rb +31 -0
  114. data/examples/widget_command_system/app.rb +15 -13
  115. data/exe/rooibos +10 -0
  116. data/generate_tutorial_stubs.rb +126 -0
  117. data/lib/rooibos/cli/commands/new.rb +373 -0
  118. data/lib/rooibos/cli/commands/run.rb +98 -0
  119. data/lib/rooibos/cli.rb +78 -0
  120. data/lib/rooibos/command/all.rb +25 -20
  121. data/lib/rooibos/command/batch.rb +26 -25
  122. data/lib/rooibos/command/custom.rb +84 -1
  123. data/lib/rooibos/command/http.rb +59 -55
  124. data/lib/rooibos/command/lifecycle.rb +5 -5
  125. data/lib/rooibos/command/open.rb +86 -0
  126. data/lib/rooibos/command/outlet.rb +105 -3
  127. data/lib/rooibos/command/wait.rb +5 -5
  128. data/lib/rooibos/command.rb +57 -74
  129. data/lib/rooibos/message/batch.rb +39 -0
  130. data/lib/rooibos/message/canceled.rb +51 -0
  131. data/lib/rooibos/message/error.rb +48 -0
  132. data/lib/rooibos/message/open.rb +30 -0
  133. data/lib/rooibos/message.rb +84 -4
  134. data/lib/rooibos/router.rb +11 -14
  135. data/lib/rooibos/runtime.rb +40 -43
  136. data/lib/rooibos/shortcuts.rb +47 -0
  137. data/lib/rooibos/test_helper.rb +71 -6
  138. data/lib/rooibos/version.rb +1 -1
  139. data/lib/rooibos/welcome.rb +237 -0
  140. data/lib/rooibos.rb +4 -3
  141. data/mise.toml +1 -1
  142. data/rbs_collection.lock.yaml +2 -2
  143. data/sig/concurrent.rbs +3 -0
  144. data/sig/gem.rbs +20 -0
  145. data/sig/rooibos/cli.rbs +42 -0
  146. data/sig/rooibos/command.rbs +48 -0
  147. data/sig/rooibos/message.rbs +60 -0
  148. data/sig/rooibos/shortcuts.rbs +14 -0
  149. data/sig/rooibos/test_helper.rbs +6 -2
  150. data/sig/rooibos/welcome.rbs +75 -0
  151. data/tasks/install.rake +29 -0
  152. data/tasks/resources/build.yml.erb +2 -0
  153. metadata +272 -38
  154. data/doc/concepts/application_architecture.md +0 -197
  155. data/doc/concepts/application_testing.md +0 -49
  156. data/doc/concepts/async_work.md +0 -164
  157. data/doc/concepts/commands.md +0 -530
  158. data/doc/concepts/message_processing.md +0 -51
  159. data/doc/contributors/WIP/decomposition_strategies_analysis.md +0 -258
  160. data/doc/contributors/WIP/implementation_plan.md +0 -409
  161. data/doc/contributors/WIP/init_callable_proposal.md +0 -344
  162. data/doc/contributors/WIP/runtime_refactoring_status.md +0 -47
  163. data/doc/contributors/WIP/task.md +0 -36
  164. data/doc/contributors/WIP/v0.4.0_todo.md +0 -468
  165. data/doc/contributors/kit-no-outlet.md +0 -238
  166. data/doc/contributors/priorities.md +0 -38
  167. data/doc/images/.gitkeep +0 -0
  168. data/exe/.gitkeep +0 -0
  169. /data/doc/contributors/{WIP → design}/mvu_tea_implementations_research.md +0 -0
@@ -135,12 +135,25 @@ module Rooibos
135
135
 
136
136
  # Mixin for user-defined custom commands.
137
137
  module Custom
138
+ # Interface for classes with a members method (Data.define, Struct).
139
+ interface _HasMembers
140
+ def members: () -> Array[Symbol]
141
+ end
142
+
143
+ # Methods excluded from deconstruct_keys introspection.
144
+ # Computed from Object, Data.define, Struct, and PP prototypes.
145
+ INFRASTRUCTURE_METHODS: Array[Symbol]
146
+
138
147
  # Brand predicate for command identification.
139
148
  def rooibos_command?: () -> true
140
149
 
141
150
  # Cleanup time after cancellation is requested. In seconds.
142
151
  # Default: 0.1 seconds (100 milliseconds).
143
152
  def rooibos_cancellation_grace_period: () -> Float
153
+
154
+ # Deconstructs for hash-based pattern matching.
155
+ # Introspects public query methods and returns a hash with :type discriminator.
156
+ def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
144
157
  end
145
158
 
146
159
  # Minimal interface for callables accepted by Lifecycle.run_sync.
@@ -179,8 +192,17 @@ module Rooibos
179
192
 
180
193
  # Messaging gateway for custom commands.
181
194
  class Outlet
195
+ # Internal handle for async streaming commands.
196
+ class AsyncHandle < Data
197
+ attr_reader future: Concurrent::Promises::Future[void]
198
+
199
+ def self.new: (future: Concurrent::Promises::Future[void]) -> instance
200
+ end
201
+
182
202
  @channel: Concurrent::Promises::Channel
183
203
  @live: Lifecycle
204
+ @pending_async: Array[AsyncHandle]
205
+
184
206
 
185
207
  def initialize: (Concurrent::Promises::Channel channel, lifecycle: Lifecycle) -> void
186
208
 
@@ -192,6 +214,14 @@ module Rooibos
192
214
 
193
215
  # Runs a child command synchronously, returning its result.
194
216
  def source: (_Callable command, Concurrent::Cancellation token, ?timeout: Float) -> Object?
217
+
218
+ # Spawns an async streaming command.
219
+ # Returns a handle for use with wait.
220
+ def standing: (_Callable command, Concurrent::Cancellation token) -> AsyncHandle
221
+
222
+ # Blocks until async commands complete.
223
+ # If no handles given, waits for all pending async commands.
224
+ def wait: (*AsyncHandle handles, ?token: Concurrent::Cancellation?) -> void
195
225
  end
196
226
 
197
227
  # A fire-and-forget parallel command.
@@ -261,5 +291,23 @@ module Rooibos
261
291
  # DWIM: accepts positional args, method keywords (get:, post:, etc.), or explicit keywords.
262
292
  # See Http class and documentation for all supported patterns.
263
293
  def self.http: (*(Symbol | String | nil), **(Symbol | String | Integer | Float | Hash[String, String] | ^(String, Hash[String, String]?, Integer?) -> Object | nil)) -> Http
294
+
295
+ # Opens a file or URL with the system's default application.
296
+ class Open < Data
297
+ include Custom
298
+
299
+ attr_reader path: String
300
+ attr_reader envelope: String
301
+
302
+ def self.new: (path: String, envelope: String) -> instance
303
+
304
+ # Builds the platform-specific open command.
305
+ def self.system_command: (String path, ?String platform) -> String
306
+
307
+ def call: (Outlet out, Concurrent::Cancellation token) -> void
308
+ end
309
+
310
+ # Creates an open command for launching files with system opener.
311
+ def self.open: (String path, ?String envelope) -> Open
264
312
  end
265
313
  end
@@ -8,9 +8,18 @@ module Rooibos
8
8
  module Message
9
9
  # Fallback predicate mixin.
10
10
  module Predicates
11
+ # Converts the message to a Symbol representation.
12
+ def to_sym: () -> Symbol
13
+
14
+ # Compares the message with another object.
15
+ def ==: (top other) -> bool
16
+
11
17
  # Returns false for unknown predicate methods.
12
18
  def method_missing: (Symbol name, *untyped args, **untyped kwargs) ?{ () -> untyped } -> bool
13
19
 
20
+ # Default deconstruct_keys for classes that don't define their own.
21
+ def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
22
+
14
23
  # Responds to all predicate methods.
15
24
  def respond_to_missing?: (Symbol name, ?bool include_private) -> bool
16
25
  end
@@ -100,6 +109,19 @@ module Rooibos
100
109
  def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
101
110
  end
102
111
 
112
+ # Completion sentinel from Command.batch.
113
+ # Signals when all children of a batch command have finished.
114
+ class Batch < Data
115
+ include Predicates
116
+
117
+ attr_reader command: Command::Batch
118
+
119
+ def self.new: (command: Command::Batch) -> instance
120
+
121
+ def batch?: () -> bool
122
+ def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
123
+ end
124
+
103
125
  # Response from Command.all aggregating parallel execution.
104
126
  class All
105
127
  include Predicates
@@ -117,5 +139,43 @@ module Rooibos
117
139
  def all?: () -> bool
118
140
  def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
119
141
  end
142
+
143
+ # Error message from a failed command.
144
+ class Error < Data
145
+ include Predicates
146
+
147
+ attr_reader command: Command::Custom
148
+ attr_reader exception: Exception
149
+
150
+ def self.new: (command: Command::Custom, exception: Exception) -> instance
151
+
152
+ def error?: () -> bool
153
+ def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
154
+ end
155
+
156
+ # Cancellation notification from a canceled command.
157
+ class Canceled < Data
158
+ include Predicates
159
+
160
+ attr_reader command: untyped
161
+
162
+ def self.new: (command: untyped) -> instance
163
+
164
+ def canceled?: () -> bool
165
+ alias cancelled? canceled?
166
+ def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
167
+ end
168
+
169
+ # Success notification from Command.open.
170
+ class Open < Data
171
+ include Predicates
172
+
173
+ attr_reader envelope: String
174
+
175
+ def self.new: (envelope: String) -> instance
176
+
177
+ def open?: () -> bool
178
+ def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
179
+ end
120
180
  end
121
181
  end
@@ -12,5 +12,19 @@ module Rooibos
12
12
  def self.sh: (String command, Symbol | Class tag) -> Command::System
13
13
  def self.map: (Command::execution inner_command) { (Array[untyped]) -> Array[untyped] } -> Command::Mapped
14
14
  end
15
+
16
+ # Short aliases for Message types.
17
+ module Msg
18
+ Timer: singleton(Message::Timer)
19
+ Http: singleton(Message::HttpResponse)
20
+
21
+ module Sh
22
+ Batch: singleton(Message::System::Batch)
23
+ Stream: singleton(Message::System::Stream)
24
+ end
25
+
26
+ All: singleton(Message::All)
27
+ Batch: singleton(Message::Batch)
28
+ end
15
29
  end
16
30
  end
@@ -5,6 +5,10 @@
5
5
 
6
6
  module Rooibos
7
7
  module TestHelper
8
- def validate_rooibos_command!: (Runtime::_MaybeCommand) -> nil
9
- end
8
+ def validate_rooibos_command!: (Runtime::_MaybeCommand) -> nil
9
+
10
+ # Fails if any Command::Error is present in the messages array.
11
+ # Relies on Minitest's flunk method being available in the including class.
12
+ def assert_no_errors: (Array[untyped] messages, ?String? msg) -> void
13
+ end
10
14
  end
@@ -0,0 +1,75 @@
1
+ #--
2
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ # SPDX-License-Identifier: LGPL-3.0-or-later
4
+ #++
5
+
6
+ module Rooibos
7
+ # Built-in welcome screen used by scaffolded applications.
8
+ module Welcome
9
+ WEBSITE_URL: String
10
+
11
+ # Internal UI module for styles and widgets.
12
+ module UI
13
+ module Styles
14
+ TEXT: RatatuiRuby::Style::Style
15
+ FILENAME: RatatuiRuby::Style::Style
16
+ COMMAND: RatatuiRuby::Style::Style
17
+ URL: RatatuiRuby::Style::Style
18
+ COMMAND_BUTTON: RatatuiRuby::Style::Style
19
+ COMMAND_BUTTON_FOCUS: RatatuiRuby::Style::Style
20
+ COMMAND_BUTTON_HOVER: RatatuiRuby::Style::Style
21
+ COMMAND_BUTTON_BOTH: RatatuiRuby::Style::Style
22
+ URL_BUTTON: RatatuiRuby::Style::Style
23
+ URL_BUTTON_FOCUS: RatatuiRuby::Style::Style
24
+ URL_BUTTON_HOVER: RatatuiRuby::Style::Style
25
+ URL_BUTTON_BOTH: RatatuiRuby::Style::Style
26
+ end
27
+
28
+ module Widgets
29
+ WELCOME_TEXT: Hash[String, RatatuiRuby::Style::Style]
30
+ PARAGRAPH: RatatuiRuby::Widgets::Paragraph
31
+ def self.website_button: (?focused: bool, ?hovered: bool) -> RatatuiRuby::Text::Span
32
+ def self.exit_button: (?focused: bool, ?hovered: bool) -> RatatuiRuby::Text::Span
33
+ end
34
+
35
+ BUTTON_SLOTS: Hash[Symbol, Integer]
36
+ FOCUS_ORDER: Array[Symbol]
37
+ BUTTON_BAR_CONSTRAINTS: Array[RatatuiRuby::Layout::Constraint]
38
+ CONTENT_CONSTRAINTS: Array[RatatuiRuby::Layout::Constraint]
39
+
40
+ def self.button_bar: (focused: Symbol?, hovered: Symbol?) -> RatatuiRuby::Layout::Layout
41
+ def self.content_layout: (focused: Symbol?, hovered: Symbol?) -> RatatuiRuby::Layout::Layout
42
+ def self.frame: (focused: Symbol?, hovered: Symbol?) -> RatatuiRuby::Widgets::Block
43
+
44
+ # Value object for button hit-test areas.
45
+ class ButtonAreas
46
+ attr_reader website: RatatuiRuby::Layout::Rect?
47
+ attr_reader exit: RatatuiRuby::Layout::Rect?
48
+ def initialize: (website: RatatuiRuby::Layout::Rect?, exit: RatatuiRuby::Layout::Rect?) -> void
49
+ def contains?: (Symbol name, Integer x, Integer y) -> bool
50
+ def button_at: (Integer x, Integer y) -> Symbol?
51
+ def for_viewport: (Integer width, Integer height) -> ButtonAreas
52
+ end
53
+ end
54
+
55
+ # Model for Welcome screen state.
56
+ class Model
57
+ attr_reader button_areas: UI::ButtonAreas
58
+ attr_reader focused: Symbol?
59
+ attr_reader hovered: Symbol?
60
+ def initialize: (button_areas: UI::ButtonAreas, focused: Symbol?, hovered: Symbol?) -> void
61
+ def tree: () -> RatatuiRuby::Widgets::Block
62
+ def active_button: () -> Symbol?
63
+ def with: (?button_areas: UI::ButtonAreas, ?focused: Symbol?, ?hovered: Symbol?) -> Model
64
+ end
65
+
66
+ View: ^(Model, untyped) -> RatatuiRuby::Widgets::Block
67
+ Update: ^(untyped, Model) -> (Model | Command::Exit | [Model, Command::System])
68
+ Init: ^() -> Model
69
+
70
+ def self.handle_click: (untyped message, Model model) -> (Model | Command::Exit | [Model, Command::System])
71
+ def self.handle_hover: (untyped message, Model model) -> Model
72
+ def self.cycle_focus: (Model model, Symbol direction) -> Model
73
+ def self.activate_button: (Symbol? button, Model model) -> (Model | Command::Exit | [Model, Command::System])
74
+ end
75
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ require "rubygems"
9
+
10
+ namespace :install do
11
+ desc "Force install rooibos gem globally (required for integration tests)"
12
+ task :force do
13
+ require "rooibos/version"
14
+
15
+ # Build the gem first
16
+ Rake::Task["build"].invoke
17
+
18
+ gem_file = "pkg/rooibos-#{Rooibos::VERSION}.gem"
19
+ unless File.exist?(gem_file)
20
+ abort "Gem not found at #{gem_file}. Run 'rake build' first."
21
+ end
22
+
23
+ puts "Installing rooibos #{Rooibos::VERSION} globally..."
24
+ system("gem", "install", gem_file, "--force", "--no-document") ||
25
+ abort("Failed to install gem")
26
+
27
+ puts "✓ rooibos #{Rooibos::VERSION} installed globally"
28
+ end
29
+ end
@@ -40,6 +40,8 @@ tasks:
40
40
  - test: |
41
41
  . ~/.buildenv
42
42
  cd <%= gem_name %>
43
+ echo "Installing rooibos gem globally for integration tests..."
44
+ mise x -- bundle exec rake install:force
43
45
  echo "Testing Ruby <%= ruby_version %>"
44
46
  mise x -- bundle exec rake test
45
47
  - lint: |