
Security News
Astral Launches pyx: A Python-Native Package Registry
Astral unveils pyx, a Python-native package registry in beta, designed to speed installs, enhance security, and integrate deeply with uv.
Clapton is a Ruby on Rails gem for building web apps with pure Ruby only (no JavaScript and no HTML templates).
Add this line to your application's Gemfile:
gem 'clapton'
And then execute:
$ bundle install
To use a Clapton component in your view:
# app/components/task_list_component.rb
class TaskListComponent < Clapton::Component
def render
div = c(:div)
@state.tasks.each do |task|
div.add(TaskItemComponent.new(id: task[:id], title: task[:title], due: task[:due], done: task[:done]))
end
btn = c(:button)
btn.add(c(:text, "Add Task"))
btn.add_action(:click, :TaskListState, :add_task)
div.add(btn)
end
end
# app/components/task_item_component.rb
class TaskItemComponent < Clapton::Component
def render
div = c(:div)
btn = c(:button)
btn.add(c(:text, @state.done ? "✅" : "🟩"))
btn.add_action(:click, :TaskListState, :toggle_done)
tf = c(:input, @state, :title)
tf.add_action(:input, :TaskListState, :update_title)
dt = c(:datetime, @state, :due)
dt.add_action(:input, :TaskListState, :update_due)
div.add(btn).add(tf).add(dt)
end
end
# app/states/task_list_state.rb
class TaskListState < Clapton::State
attribute :tasks
def add_task(params)
task = Task.create(title: "New Task", due: Date.today, done: false)
self.tasks << { id: task.id, title: task.title, due: task.due, done: task.done }
end
def toggle_done(params)
task = Task.find(params[:id])
task.update(done: !params[:done])
self.tasks.find { |t| t[:id] == params[:id] }[:done] = task.done
end
def update_title(params)
task = Task.find(params[:id])
task.update(title: params[:title])
self.tasks.find { |t| t[:id] == params[:id] }[:title] = task.title
end
def update_due(params)
task = Task.find(params[:id])
task.update(due: params[:due])
self.tasks.find { |t| t[:id] == params[:id] }[:due] = task.due
end
end
# app/states/task_item_state.rb
class TaskItemState < Clapton::State
attribute :id
attribute :title
attribute :due
attribute :done
end
# app/controllers/tasks_controller.rb
class TasksController < ApplicationController
def index
@tasks = Task.all
@components = [
[:TaskListComponent, { tasks: @tasks.map { |task| { id: task.id, title: task.title, due: task.due, done: task.done } } }]
]
end
end
# app/views/layouts/application.html.erb
<%= clapton_javascript_tag %>
# app/views/tasks/index.html.erb
<%= clapton_tag %>
Make sure to include the necessary route in your config/routes.rb
:
mount Clapton::Engine => "/clapton"
<%= clapton_component_tag(
:TaskListComponent,
{
tasks: @tasks.map { |task| { id: task.id, title: task.title, due: task.due, done: task.done } }
}
) %>
rails generate clapton TaskList
After running the generator, you will see the following files:
app/components/task_list_component.rb
app/states/task_list_state.rb
The render
event is a special event that is triggered when the component is rendered.
# app/components/task_list_component.rb
class TaskListComponent < Clapton::Component
def render
# ...
div = c(:div)
div.add_action(:render, :TaskListState, :add_empty_task, debounce: 500)
end
end
The effect
method is a method that is triggered when the state is changed.
# app/components/task_list_component.rb
class TaskListComponent < Clapton::Component
effect [:tasks] do |state|
puts state[:tasks]
end
end
If dependencies are not specified, the effect will be triggered on the first render.
# app/components/video_player_component.rb
class VideoPlayerComponent < Clapton::Component
effect [] do
puts "First render"
end
end
Clapton supports streaming.
# app/states/chat_state.rb
class ChatState < Clapton::State
attribute :messages
def send(params)
self.messages << { role: "user", content: params[:content] }
yield continue: true # Continue the streaming
client = OpenAI::Client.new(
access_token: ENV.fetch("OPENAI_ACCESS_TOKEN"),
log_errors: true
)
self.messages << { role: "assistant", content: "" }
client.chat(
parameters: {
model: "gpt-4o-mini",
messages: messages,
stream: proc do |chunk, _bytesize|
if chunk.dig("choices", 0, "finish_reason") == "stop"
yield continue: false # Stop the streaming
end
self.messages.last[:content] << chunk.dig("choices", 0, "delta", "content")
yield continue: true
end
}
)
end
end
Clapton uses Action Cable to broadcast state changes to the client.
If you want to identify the user, you can set the current_user
in the connection.
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private
def find_verified_user
if verified_user = User.find_by(id: cookies.signed[:user_id])
verified_user
else
reject_unauthorized_connection
end
end
end
end
Use clapton_javascript_tag
instead of javascript_importmap_tags
.
- <%= javascript_importmap_tags %>
+ <%= clapton_javascript_tag %>
The clapton:render
event is a custom event that is triggered when the component is rendered.
document.addEventListener("clapton:render", () => {
console.log("clapton:render");
});
# spec/spec_helper.rb
require "clapton/test_helper/rspec"
RSpec.configure do |config|
config.include Clapton::TestHelper::RSpec, type: :component
end
# spec/components/task_list_component_spec.rb
describe "TaskListComponent", type: :component do
it "renders" do
render_component("TaskListComponent", tasks: [{ id: 1, title: "Task 1", done: false, due: Time.current }])
# You can use Capybara matchers here
expect(page).to have_selector("input[type='text']")
end
end
# test/test_helper.rb
require "clapton/test_helper/minitest"
class ActiveSupport::TestCase
include Clapton::TestHelper::Minitest
end
# test/components/task_list_component_test.rb
class TaskListComponentTest < ActiveSupport::TestCase
test "renders" do
render_component("TaskListComponent", tasks: [{ id: 1, title: "Task 1", done: false, due: Time.current }])
# You can use Capybara matchers here
assert_select "input[type='text']"
end
end
Run bundle exec rake clapton:compile
to compile the components.
app/components
is codes that are compiled to JavaScript.
So, you need to ignore the directory from autoloading.
# config/application.rb
Rails.autoloaders.main.ignore(Rails.root.join("app/components"))
After checking out the repo, run bin/setup
to install dependencies. Then, run bin/dev
to start the development server.
Run bundle exec rake test
to run the test suite.
Run cd test/dummy && bundle exec rake test
to run the test suite for the dummy app.
Run cd test/dummy && bundle exec rspec
to run the test suite for the dummy app with RSpec.
Run cd lib/clapton/javascripts && npm run test
to run the test suite for the JavaScript part.
Bug reports and pull requests are welcome on GitHub at https://github.com/kawakamimoeki/clapton. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
FAQs
Unknown package
We found that clapton demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Astral unveils pyx, a Python-native package registry in beta, designed to speed installs, enhance security, and integrate deeply with uv.
Security News
The Latio podcast explores how static and runtime reachability help teams prioritize exploitable vulnerabilities and streamline AppSec workflows.
Security News
The latest Opengrep releases add Apex scanning, precision rule tuning, and performance gains for open source static code analysis.