Opal Stimulus for Rails
Write your Stimulus controllers in Ruby instead of JavaScript! 🎉
Opal Stimulus uses the Opal Ruby-to-JavaScript compiler to bring the elegance of Ruby to your frontend controllers. If you love Ruby and prefer its syntax over JavaScript, this gem is for you.
Compatibility: Rails 7.2+ to 8.0.2
Quick Start
Add to your Rails app and get started in seconds:
bundle add opal_stimulus
rails opal_stimulus:install
bin/dev
Generate your first Ruby controller:
bin/rails generate opal_stimulus hello
This creates app/opal/controllers/hello_controller.rb
. If you have an existing JavaScript controller, you can remove it:
bin/rails destroy stimulus hello # removes app/javascript/controllers/hello_controller.js
Example: Hello World
Here's the classic Stimulus example, but in Ruby! Compare with the JavaScript version.
Ruby Controller (app/opal/controllers/hello_controller.rb
):
class HelloController < StimulusController
self.targets = ["name", "output"]
def greet
output_target.text_content = "Hello, #{name_target.value}!"
end
end
HTML (unchanged from regular Stimulus):
<div data-controller="hello">
<input data-hello-target="name" type="text">
<button data-action="click->hello#greet">Greet</button>
<span data-hello-target="output"></span>
</div>
Key Differences from JavaScript Stimulus
-
Snake case: JavaScript's
containerTarget
becomescontainer_target
in Ruby -
DOM objects: All
target
,element
,document
,window
, andevent
objects areJS::Proxy
instances that provide Ruby-friendly access to JavaScript APIs - HTML stays the same: Your templates and data attributes work exactly like regular Stimulus
Reference Examples
Based on the Stimulus Reference, here's how common patterns work in Ruby:
Lifecycle Callbacks
class AlertController < StimulusController
def initialize; end
def connect; end
def disconnect; end
end
Actions & Events
class WindowResizeController < StimulusController
def resized(event)
if !@resized && event.target.inner_width >= 1080
puts "Full HD detected!"
@resized = true
end
end
end
Targets
class ContainerController < StimulusController
self.targets = ["container"]
def container_target_connected
container_target.inner_html = <<~HTML
<h1>Test connected!</h1>
HTML
end
def container_target_disconnected
puts "Container disconnected!"
end
end
Outlets
class ChatController < StimulusController
self.outlets = [ "user-status" ]
def connect
user_status_outlets.each do |status|
puts status
end
end
end
Values
class LoaderController < StimulusController
self.values = { url: :string }
def connect
window.fetch(url_value).then do |response|
response.json().then do |data|
load_data(data)
end
end
end
private
def load_data(data)
# ...
end
end
CSS Classes
class SearchController < StimulusController
self.classes = ["loading"]
def load_results
element.class_list.add(loading_class)
end
end
Working with the DOM
Opal Stimulus gives you Ruby-friendly access to all the browser APIs you know and love:
window
class WindowController < StimulusController
def connect
window.alert "Hello world!"
window.set_timeout(-> {
puts "1. Timeout test OK (1s delay)"
}, 1000)
end
end
document
class DocumentController < StimulusController
def connect
document.querySelectorAll("h1").each do |h1|
h1.text_content = "Opal is great!"
end
end
end
Development
Run the test suite:
bundle exec rake
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/josephschito/opal_stimulus.
License
The gem is available as open source under the terms of the MIT License.