Time: 10 minutes You’ll build: A working extension gem with a runner, a polling actor, and passing specs. Prerequisites: Ruby >= 3.4, Bundler
This quickstart shows the extension system (LEX) — how LegionIO grows through composable gems. By the end, you’ll have a working extension that integrates with the framework automatically.
gem install legionio
legionio lex create weather_checker
» Creating lex-weather_checker (template: basic)...
» Files generated
» Git initialized
» Bundle installed
» Extension lex-weather_checker created in ./lex-weather_checker
Next steps:
cd lex-weather_checker
# Add runners: legionio generate runner my_runner
# Add actors: legionio generate actor my_actor
cd lex-weather_checker
The scaffold creates a complete gem layout:
lex-weather_checker/
lex-weather_checker.gemspec
Gemfile
.gitignore
.rubocop.yml
LICENSE
README.md
lib/
legion/
extensions/
weather_checker/
runners/
actors/
tools/
.gitkeep
version.rb
client.rb
weather_checker.rb
spec/
spec_helper.rb
legion/
extensions/
weather_checker_spec.rb
.github/
workflows/
rspec.yml
rubocop.yml
The template: basic is the default. Other available templates are: llm-agent, service-integration, data-pipeline, scheduled-task, and webhook-handler. Pass --template to select one.
Runners hold your business logic — callable functions that do the actual work.
legionio generate runner forecast
» Created lib/legion/extensions/weather_checker/runners/forecast.rb
» Created spec/runners/forecast_spec.rb
Functions scaffolded: execute
Add actors with: legionio generate actor forecast --type subscription
Edit the generated runner to add your logic:
# lib/legion/extensions/weather_checker/runners/forecast.rb
module Legion
module Extensions
module WeatherChecker
module Runners
module Forecast
def get(city:, **)
connection = Faraday.new("https://wttr.in")
response = connection.get("/#{city}?format=j1")
Legion::JSON.load(response.body)
end
end
end
end
end
end
Actors define how a runner is invoked. A polling actor calls your runner on a fixed interval.
legionio generate actor checker
» Created lib/legion/extensions/weather_checker/actors/checker.rb
» Created spec/actors/checker_spec.rb
Configure it to poll every 60 seconds:
# lib/legion/extensions/weather_checker/actors/checker.rb
module Legion
module Extensions
module WeatherChecker
module Actors
class Checker < Legion::Extensions::Actors::Poll
def action
result = run_runner(Legion::Extensions::WeatherChecker::Runners::Forecast,
:get, city: "Minneapolis")
Legion::Logging::Logger.info "Weather: #{result}"
end
def poll_interval
60
end
end
end
end
end
end
bundle exec rspec
Legion::Extensions::WeatherChecker
is a valid extension
has a version
loads without error
Finished in 0.00412 seconds (files took 0.31 seconds to load)
3 examples, 0 failures
The scaffold ships with passing specs out of the box. Add your own for the runner logic in spec/runners/forecast_spec.rb.
bundle exec rubocop
Inspecting 6 files
......
6 files inspected, no offenses detected
Add the gem to a LegionIO project’s Gemfile:
gem "lex-weather_checker", path: "../lex-weather_checker"
Then start the engine:
legionio start
LegionIO auto-discovers extensions by scanning Bundler.load.specs at boot. No registration, no config — add the gem to the Gemfile and it’s live. The Checker polling actor starts immediately and calls the Forecast runner every 60 seconds.
You built a complete extension in 10 minutes:
The extension system is how LegionIO grows. The core stays small. Your extension composes with everything else — other extensions can call your runners, chain tasks through conditioners and transformers, or subscribe to results over AMQP.