Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v4

- name: Set up Ruby
uses: ruby/setup-ruby@v1
Expand All @@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v4

- name: Set up Ruby
uses: ruby/setup-ruby@v1
Expand All @@ -40,27 +40,36 @@ jobs:
test:
runs-on: ubuntu-latest

# services:
# redis:
# image: redis
# ports:
# - 6379:6379
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
services:
postgres:
image: postgres:14
ports:
- 5432:5432
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Install packages
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config

- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v4

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true

- name: Setup test database
env:
RAILS_ENV: test
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
run: bin/rails db:create db:schema:load

- name: Run tests
env:
RAILS_ENV: test
# REDIS_URL: redis://localhost:6379/0
run: bin/rails db:test:prepare test
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
run: bin/rails test
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,14 @@

# Ignore master key for decrypting credentials and more.
/config/master.key

/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*
.yarn-integrity

# Generated React on Rails packs
**/generated/**
ssr-generated
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.3.4
16 changes: 12 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ source "https://rubygems.org"

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 8.0.3"
# The modern asset pipeline for Rails [https://github.com/rails/propshaft]
gem "propshaft"

# Use postgresql as the database for Active Record
gem "pg", "~> 1.1"

# Remove propshaft since we'll use shakapacker
# gem "propshaft"
# Use the Puma web server [https://github.com/puma/puma]
gem "puma", ">= 5.0"
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
gem "jbuilder"

# React on Rails and Shakapacker for webpack integration
gem "shakapacker", "9.0.0.beta.3"
gem "react_on_rails", "16.1.1"

# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
# gem "bcrypt", "~> 3.1.7"

Expand All @@ -19,10 +27,10 @@ gem "tzinfo-data", platforms: %i[ windows jruby ]
gem "bootsnap", require: false

# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
gem "kamal", require: false
# gem "kamal", require: false

# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
gem "thruster", require: false
# gem "thruster", require: false

group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
Expand Down
68 changes: 30 additions & 38 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ GEM
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
ast (2.4.3)
base64 (0.3.0)
bcrypt_pbkdf (1.1.1)
benchmark (0.4.1)
bigdecimal (3.2.3)
bindex (0.8.1)
Expand All @@ -90,11 +91,10 @@ GEM
debug (1.11.0)
irb (~> 1.10)
reline (>= 0.3.8)
dotenv (3.1.8)
drb (2.2.3)
ed25519 (1.4.0)
erb (5.0.2)
erubi (1.13.1)
execjs (2.10.0)
globalid (1.3.0)
activesupport (>= 6.1)
i18n (1.14.7)
Expand All @@ -108,17 +108,6 @@ GEM
actionview (>= 7.0.0)
activesupport (>= 7.0.0)
json (2.15.0)
kamal (2.7.0)
activesupport (>= 7.0)
base64 (~> 0.2)
bcrypt_pbkdf (~> 1.0)
concurrent-ruby (~> 1.2)
dotenv (~> 3.1)
ed25519 (~> 1.4)
net-ssh (~> 7.3)
sshkit (>= 1.23.0, < 2.0)
thor (~> 1.3)
zeitwerk (>= 2.6.18, < 3.0)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
logger (1.7.0)
Expand All @@ -141,13 +130,8 @@ GEM
net-protocol
net-protocol (0.2.2)
timeout
net-scp (4.1.0)
net-ssh (>= 2.6.5, < 8.0.0)
net-sftp (4.0.0)
net-ssh (>= 5.0.0, < 8.0.0)
net-smtp (0.5.1)
net-protocol
net-ssh (7.3.0)
nio4r (2.7.4)
nokogiri (1.18.10-aarch64-linux-gnu)
racc (~> 1.4)
Expand All @@ -163,26 +147,31 @@ GEM
racc (~> 1.4)
nokogiri (1.18.10-x86_64-linux-musl)
racc (~> 1.4)
ostruct (0.6.3)
package_json (0.1.0)
parallel (1.27.0)
parser (3.3.9.0)
ast (~> 2.4.1)
racc
pg (1.6.2)
pg (1.6.2-aarch64-linux)
pg (1.6.2-aarch64-linux-musl)
pg (1.6.2-arm64-darwin)
pg (1.6.2-x86_64-linux)
pg (1.6.2-x86_64-linux-musl)
pp (0.6.2)
prettyprint
prettyprint (0.2.0)
prism (1.5.1)
propshaft (1.3.1)
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
rack
psych (5.2.6)
date
stringio
public_suffix (6.0.2)
puma (7.0.4)
nio4r (~> 2.0)
racc (1.8.1)
rack (3.2.1)
rack-proxy (0.7.7)
rack
rack-session (2.1.1)
base64 (>= 0.1.0)
rack (>= 3.0.0)
Expand Down Expand Up @@ -225,6 +214,13 @@ GEM
rdoc (6.14.2)
erb
psych (>= 4.0.0)
react_on_rails (16.1.1)
addressable
connection_pool
execjs (~> 2.5)
rails (>= 5.2)
rainbow (~> 3.0)
shakapacker (>= 6.0)
regexp_parser (2.11.3)
reline (0.6.2)
io-console (~> 0.5)
Expand Down Expand Up @@ -258,19 +254,15 @@ GEM
rubocop-rails (>= 2.30)
ruby-progressbar (1.13.0)
securerandom (0.4.1)
sshkit (1.24.0)
base64
logger
net-scp (>= 1.1.2)
net-sftp (>= 2.1.2)
net-ssh (>= 2.8.0)
ostruct
semantic_range (3.1.0)
shakapacker (9.0.0.beta.3)
activesupport (>= 5.2)
package_json
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
stringio (3.1.7)
thor (1.4.0)
thruster (0.1.15)
thruster (0.1.15-aarch64-linux)
thruster (0.1.15-arm64-darwin)
thruster (0.1.15-x86_64-linux)
timeout (0.4.3)
tsort (0.2.0)
tzinfo (2.0.6)
Expand Down Expand Up @@ -307,12 +299,12 @@ DEPENDENCIES
brakeman
debug
jbuilder
kamal
propshaft
pg (~> 1.1)
puma (>= 5.0)
rails (~> 8.0.3)
react_on_rails (= 16.1.1)
rubocop-rails-omakase
thruster
shakapacker (= 9.0.0.beta.3)
tzinfo-data
web-console

Expand Down
5 changes: 5 additions & 0 deletions Procfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Procfile for development using HMR
# You can run these commands in separate shells
rails: bundle exec rails s -p 3000
wp-client: WEBPACK_SERVE=true bin/shakapacker-dev-server
wp-server: SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch
8 changes: 8 additions & 0 deletions Procfile.dev-prod-assets
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Procfile for development with production assets
# Uses production-optimized, precompiled assets with development environment
# Uncomment additional processes as needed for your app

rails: bundle exec rails s -p 3001
# sidekiq: bundle exec sidekiq -C config/sidekiq.yml
# redis: redis-server
# mailcatcher: mailcatcher --foreground
2 changes: 2 additions & 0 deletions Procfile.dev-static-assets
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
web: bin/rails server -p 3000
js: bin/shakapacker --watch
9 changes: 9 additions & 0 deletions app/controllers/hello_world_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class HelloWorldController < ApplicationController
layout "hello_world"

def index
@hello_world_props = { name: "Stranger" }
end
end
15 changes: 15 additions & 0 deletions app/javascript/packs/application.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb

// Uncomment to copy all static images under ./images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('./images', true)
// const imagePath = (name) => images(name, true)
1 change: 1 addition & 0 deletions app/javascript/packs/server-bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Placeholder comment - auto-generated imports will be prepended here by react_on_rails:generate_packs
21 changes: 21 additions & 0 deletions app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { useState } from 'react';
import * as style from './HelloWorld.module.css';

const HelloWorld = (props) => {
const [name, setName] = useState(props.name);

return (
<div>
<h3>Hello, {name}!</h3>
<hr />
<form>
<label className={style.bright} htmlFor="name">
Say hello to:
<input id="name" type="text" value={name} onChange={(e) => setName(e.target.value)} />
</label>
</form>
Comment on lines +11 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Prevent form submission reload

Submitting the form (e.g., pressing Enter) currently performs a full page reload, wiping local state and undermining the interactive demo. Add a preventDefault handler on the form submit so the component stays SPA-friendly.

-      <form>
+      <form onSubmit={(event) => event.preventDefault()}>
         <label className={style.bright} htmlFor="name">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<form>
<label className={style.bright} htmlFor="name">
Say hello to:
<input id="name" type="text" value={name} onChange={(e) => setName(e.target.value)} />
</label>
</form>
<form onSubmit={(event) => event.preventDefault()}>
<label className={style.bright} htmlFor="name">
Say hello to:
<input
id="name"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
</form>
🤖 Prompt for AI Agents
In app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx around
lines 11 to 16, the <form> currently submits and reloads the page; add an
onSubmit handler to the form that calls event.preventDefault() (e.g.,
onSubmit={(e) => e.preventDefault()}) so pressing Enter won't trigger a full
reload and local React state (name) is preserved — update the JSX to include
this handler on the form element.

</div>
);
};

export default HelloWorld;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.bright {
color: green;
font-weight: bold;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import HelloWorld from './HelloWorld.client';
// This could be specialized for server rendering
// For example, if using React Router, we'd have the SSR setup here.

export default HelloWorld;
2 changes: 2 additions & 0 deletions app/views/hello_world/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Hello World</h1>
<%= react_component("HelloWorld", props: @hello_world_props, prerender: true) %>
1 change: 1 addition & 0 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

<%# Includes all stylesheet files in app/assets/stylesheets %>
<%= stylesheet_link_tag :app %>
<%= javascript_pack_tag "application" %>
</head>

<body>
Expand Down
15 changes: 15 additions & 0 deletions app/views/layouts/hello_world.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>ReactOnRailsWithShakapacker</title>
<%= csrf_meta_tags %>

<!-- Empty pack tags - React on Rails injects component CSS/JS here -->
<%= stylesheet_pack_tag %>
<%= javascript_pack_tag %>
Comment on lines +8 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix pack helper usage to avoid runtime ArgumentError.

Line 8 invokes stylesheet_pack_tag (and Line 9 javascript_pack_tag) with no arguments. These helpers require at least one pack name; calling them empty raises ArgumentError: wrong number of arguments (given 0, expected 1+), so any request using this layout will blow up before rendering. Pass the appropriate pack names (e.g., 'application' or your HelloWorld bundle) or remove the helpers if they’re not needed.

-    <%= stylesheet_pack_tag %>
-    <%= javascript_pack_tag %>
+    <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbo-track': 'reload' %>
+    <%= javascript_pack_tag 'application', 'data-turbo-track': 'reload', defer: true %>

Adjust the pack name(s) to match the entries you actually build.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<%= stylesheet_pack_tag %>
<%= javascript_pack_tag %>
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbo-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbo-track': 'reload', defer: true %>
🤖 Prompt for AI Agents
In app/views/layouts/hello_world.html.erb around lines 8 to 9, the layout calls
stylesheet_pack_tag and javascript_pack_tag with no arguments which raises
ArgumentError; update those calls to pass the actual pack names you build (for
example 'application' or your HelloWorld bundle) or remove the helpers if you
don't use webpacker packs, ensuring the pack names match your webpacker/webpack
entries.

</head>

<body>
<%= yield %>
</body>
</html>
Loading
Loading