Skip to content

Commit 3449c0d

Browse files
justin808claude
andcommitted
Refactor bin/dev to use ReactOnRails::Dev classes with clear Procfile management
This commit significantly refactors the bin/dev development server script to improve maintainability, testing, and user experience with clear Procfile documentation. ## Key Changes ### 🏗️ **Architecture Improvements** - **Extracted bin/dev logic** into `ReactOnRails::Dev` module classes: - `ServerManager` - Main server orchestration and command routing - `ProcessManager` - Overmind/Foreman process management - `PackGenerator` - React on Rails pack generation - `FileManager` - Cleanup of stale socket/pid files - **Modular design** allows for easy extension and testing - **Clean API** for programmatic usage: `ReactOnRails::Dev::ServerManager.start(:mode, "procfile")` ### 📋 **Clear Procfile Management** - **Explicit Procfile mapping** for each command: - `bin/dev` (HMR) → `Procfile.dev` - `bin/dev static` → `Procfile.dev-static-assets` - `bin/dev prod` → `Procfile.dev-prod-assets` - **Prominent boxed display** shows which Procfile is being used: ``` ┌──────────────────────────────────────────────────────────┐ │ 📋 Using Procfile: Procfile.dev │ │ 💡 Access at: http://localhost:3000 │ │ 🔧 Customize this file for your app's needs │ └──────────────────────────────────────────────────────────┘ ``` - **Port information** prominently displayed (3000 for dev/static, 3001 for prod) ### ✅ **Fixed Pack Generation** - **Single pack generation** per mode (eliminates duplication) - **Clean shakapacker binstubs** - removed React on Rails specific code - **Proper execution order**: Pack generation → Procfile start - **Maintainable approach** - no custom webpack binary modifications ### 📚 **Enhanced Documentation** - **Comprehensive help** shows Procfile mapping and customization options - **Clear comments** in bin/dev explain customization hierarchy - **Process manager detection** in installation success message ### 🧪 **Testing Infrastructure** - **Complete RSpec test suite** for all ReactOnRails::Dev classes - **Isolated unit tests** with proper mocking - **Updated existing tests** to work with new architecture ## Breaking Changes None - bin/dev commands work identically to before. ## Migration Path Existing bin/dev usage remains unchanged. For advanced customization: 1. Edit appropriate Procfile (recommended) 2. Modify bin/dev script for project-specific behavior 3. Extend ReactOnRails::Dev classes for advanced customization ## Benefits - **🔧 Clear customization path** via Procfiles - **🧹 Cleaner codebase** with separated concerns - **🧪 Testable components** with full RSpec coverage - **📖 Better documentation** and user guidance - **⚡ Improved development experience** with prominent UI 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 47fdabc commit 3449c0d

28 files changed

+830
-341
lines changed

lib/generators/react_on_rails/adapt_for_older_shakapacker_generator.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module ReactOnRails
77
module Generators
88
class AdaptForOlderShakapackerGenerator < Rails::Generators::Base
99
include GeneratorHelper
10+
1011
Rails::Generators.hide_namespace(namespace)
1112

1213
def change_spelling_to_webpacker

lib/generators/react_on_rails/base_generator.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module ReactOnRails
77
module Generators
88
class BaseGenerator < Rails::Generators::Base
99
include GeneratorHelper
10+
1011
Rails::Generators.hide_namespace(namespace)
1112
source_root(File.expand_path("templates", __dir__))
1213

@@ -33,6 +34,7 @@ def copy_base_files
3334
base_path = "base/base/"
3435
base_files = %w[app/controllers/hello_world_controller.rb
3536
app/views/layouts/hello_world.html.erb
37+
bin/dev
3638
Procfile.dev
3739
Procfile.dev-static-assets
3840
Procfile.dev-prod-assets]
@@ -258,7 +260,6 @@ def append_to_spec_rails_helper
258260
if File.exist?(spec_helper)
259261
add_configure_rspec_to_compile_assets(spec_helper)
260262
else
261-
# rubocop:disable Layout/EmptyLinesAroundArguments
262263
GeneratorMessages.add_info(
263264
<<-MSG.strip_heredoc
264265
@@ -271,8 +272,6 @@ def append_to_spec_rails_helper
271272
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
272273
MSG
273274
)
274-
# rubocop:enable Layout/EmptyLinesAroundArguments
275-
276275
end
277276
end
278277
end

lib/generators/react_on_rails/bin/dev

Lines changed: 29 additions & 244 deletions
Original file line numberDiff line numberDiff line change
@@ -1,259 +1,44 @@
11
#!/usr/bin/env ruby
22
# frozen_string_literal: true
33

4-
require "English"
5-
6-
def installed?(process)
7-
IO.popen "#{process} -v"
8-
rescue Errno::ENOENT
9-
false
10-
end
11-
12-
def generate_packs
13-
puts "📦 Generating React on Rails packs..."
14-
system "bundle exec rake react_on_rails:generate_packs"
15-
16-
return if $CHILD_STATUS.success?
17-
18-
puts "❌ Pack generation failed"
19-
exit 1
20-
end
21-
22-
def ensure_procfile(procfile)
23-
return if File.exist?(procfile)
24-
25-
warn <<~MSG
26-
ERROR:
27-
Please ensure `#{procfile}` exists in your project!
28-
MSG
29-
exit 1
30-
end
31-
32-
def cleanup_stale_files
33-
cleaned_any = false
34-
35-
# Check for stale overmind socket files
36-
socket_files = [".overmind.sock", "tmp/sockets/overmind.sock"]
37-
38-
# Only clean up if overmind is not actually running
39-
overmind_pids = `pgrep -f "overmind" 2>/dev/null`.split("\n").map(&:to_i)
40-
41-
if overmind_pids.empty?
42-
socket_files.each do |socket_file|
43-
if File.exist?(socket_file)
44-
puts " 🧹 Cleaning up stale socket: #{socket_file}"
45-
File.delete(socket_file) rescue nil
46-
cleaned_any = true
47-
end
48-
end
49-
end
50-
51-
# Check for stale Rails server.pid file
52-
server_pid_file = "tmp/pids/server.pid"
53-
if File.exist?(server_pid_file)
54-
pid = File.read(server_pid_file).to_i
55-
# Check if process is actually running
56-
begin
57-
Process.kill(0, pid)
58-
# Process exists, don't clean up
59-
rescue Errno::ESRCH
60-
# Process doesn't exist, clean up stale pid file
61-
puts " 🧹 Cleaning up stale Rails pid file: #{server_pid_file}"
62-
File.delete(server_pid_file) rescue nil
63-
cleaned_any = true
64-
end
65-
end
66-
67-
cleaned_any
68-
end
69-
70-
def run_with_process_manager(procfile)
71-
# Clean up stale files before starting
72-
cleanup_stale_files
73-
74-
if installed?("overmind")
75-
system "overmind start -f #{procfile}"
76-
elsif installed?("foreman")
77-
system "foreman start -f #{procfile}"
78-
else
79-
warn <<~MSG
80-
NOTICE:
81-
For this script to run, you need either 'overmind' or 'foreman' installed on your machine. Please try this script after installing one of them.
82-
MSG
83-
exit 1
84-
end
85-
end
86-
87-
def print_server_info(title, features, port = 3000)
88-
puts "#{title}"
89-
features.each { |feature| puts " - #{feature}" }
90-
puts ""
91-
puts "💡 Access at: http://localhost:#{port}"
92-
puts ""
93-
end
94-
95-
def run_production_like
96-
print_server_info(
97-
"🏭 Starting production-like development server...",
98-
[
99-
"Generating React on Rails packs",
100-
"Precompiling assets with production optimizations",
101-
"Running Rails server on port 3001",
102-
"No HMR (Hot Module Replacement)",
103-
"CSS extracted to separate files (no FOUC)"
104-
],
105-
3001
106-
)
107-
108-
generate_packs
109-
110-
# Precompile assets in production mode
111-
puts "🔨 Precompiling assets..."
112-
system "RAILS_ENV=production NODE_ENV=production bundle exec rails assets:precompile"
113-
114-
if $CHILD_STATUS.success?
115-
puts "✅ Assets precompiled successfully"
116-
puts "🚀 Starting Rails server in production mode..."
117-
puts ""
118-
puts "Press Ctrl+C to stop the server"
119-
puts "To clean up: rm -rf public/packs && bin/dev"
120-
puts ""
121-
122-
# Start Rails in production mode
123-
system "RAILS_ENV=production bundle exec rails server -p 3001"
124-
else
125-
puts "❌ Asset precompilation failed"
126-
exit 1
127-
end
128-
end
129-
130-
def run_static_development
131-
print_server_info(
132-
"⚡ Starting development server with static assets...",
133-
[
134-
"Generating React on Rails packs",
135-
"Using shakapacker --watch (no HMR)",
136-
"CSS extracted to separate files (no FOUC)",
137-
"Development environment (source maps, faster builds)",
138-
"Auto-recompiles on file changes"
139-
]
140-
)
141-
142-
generate_packs
143-
144-
procfile = "Procfile.dev-static-assets"
145-
ensure_procfile(procfile)
146-
run_with_process_manager(procfile)
147-
end
148-
149-
def run_development
150-
generate_packs
151-
152-
procfile = "Procfile.dev"
153-
ensure_procfile(procfile)
154-
run_with_process_manager(procfile)
155-
end
156-
157-
def kill_processes
158-
puts "🔪 Killing all development processes..."
159-
puts ""
160-
161-
processes = {
162-
"rails" => "Rails server",
163-
"node.*react[-_]on[-_]rails" => "React on Rails Node processes",
164-
"overmind" => "Overmind process manager",
165-
"foreman" => "Foreman process manager",
166-
"ruby.*puma" => "Puma server",
167-
"webpack-dev-server" => "Webpack dev server",
168-
"bin/shakapacker-dev-server" => "Shakapacker dev server"
169-
}
170-
171-
killed_any = false
172-
173-
processes.each do |pattern, description|
174-
pids = `pgrep -f "#{pattern}" 2>/dev/null`.split("\n").map(&:to_i).reject { |pid| pid == Process.pid }
175-
176-
if pids.any?
177-
puts " ☠️ Killing #{description} (PIDs: #{pids.join(', ')})"
178-
pids.each { |pid| Process.kill("TERM", pid) rescue nil }
179-
killed_any = true
180-
end
181-
end
182-
183-
# Clean up socket and pid files
184-
cleanup_files = [
185-
".overmind.sock",
186-
"tmp/sockets/overmind.sock",
187-
"tmp/pids/server.pid"
188-
]
189-
190-
cleanup_files.each do |file|
191-
if File.exist?(file)
192-
puts " 🧹 Removing #{file}"
193-
File.delete(file) rescue nil
194-
killed_any = true
195-
end
196-
end
197-
198-
if killed_any
199-
puts ""
200-
puts "✅ All processes terminated and sockets cleaned"
201-
puts "💡 You can now run 'bin/dev' for a clean start"
202-
else
203-
puts " ℹ️ No development processes found running"
204-
end
205-
end
206-
207-
def show_help
208-
puts <<~HELP
209-
Usage: bin/dev [command]
210-
211-
Commands:
212-
(none) / hmr Start development server with HMR (default)
213-
static Start development server with static assets (no HMR, no FOUC)
214-
production-assets Start with production-optimized assets (no HMR)
215-
prod Alias for production-assets
216-
kill Kill all development processes for a clean start
217-
help Show this help message
218-
#{' '}
219-
HMR Development mode (default):
220-
• Hot Module Replacement (HMR) enabled
221-
• Automatic React on Rails pack generation
222-
• Source maps for debugging
223-
• May have Flash of Unstyled Content (FOUC)
224-
• Fast recompilation
225-
• Access at: http://localhost:3000
226-
227-
Static development mode:
228-
• No HMR (static assets with auto-recompilation)
229-
• Automatic React on Rails pack generation
230-
• CSS extracted to separate files (no FOUC)
231-
• Development environment (faster builds than production)
232-
• Source maps for debugging
233-
• Access at: http://localhost:3000
234-
235-
Production-assets mode:
236-
• Automatic React on Rails pack generation
237-
• Optimized, minified bundles
238-
• Extracted CSS files (no FOUC)
239-
• No HMR (static assets)
240-
• Slower recompilation
241-
• Access at: http://localhost:3001
242-
HELP
4+
# ReactOnRails Development Server
5+
#
6+
# This script provides a simple interface to the ReactOnRails development
7+
# server management. The core logic is implemented in ReactOnRails::Dev
8+
# classes for better maintainability and testing.
9+
#
10+
# Each command uses a specific Procfile for process management:
11+
# - bin/dev (default/hmr): Uses Procfile.dev
12+
# - bin/dev static: Uses Procfile.dev-static-assets
13+
# - bin/dev prod: Uses Procfile.dev-prod-assets
14+
#
15+
# To customize development environment:
16+
# 1. Edit the appropriate Procfile to modify which processes run
17+
# 2. Modify this script for project-specific command-line behavior
18+
# 3. Extend ReactOnRails::Dev classes in your Rails app for advanced customization
19+
# 4. Use classes directly: ReactOnRails::Dev::ServerManager.start(:development, "Custom.procfile")
20+
21+
begin
22+
require "bundler/setup"
23+
require "react_on_rails/dev"
24+
rescue LoadError
25+
# Fallback for when gem is not yet installed
26+
puts "Loading ReactOnRails development tools..."
27+
require_relative "../../../react_on_rails/dev"
24328
end
24429

24530
# Main execution
24631
case ARGV[0]
24732
when "production-assets", "prod"
248-
run_production_like
33+
ReactOnRails::Dev::ServerManager.start(:production_like)
24934
when "static"
250-
run_static_development
35+
ReactOnRails::Dev::ServerManager.start(:static, "Procfile.dev-static-assets")
25136
when "kill"
252-
kill_processes
37+
ReactOnRails::Dev::ServerManager.kill_processes
25338
when "help", "--help", "-h"
254-
show_help
39+
ReactOnRails::Dev::ServerManager.show_help
25540
when "hmr", nil
256-
run_development
41+
ReactOnRails::Dev::ServerManager.start(:development, "Procfile.dev")
25742
else
25843
puts "Unknown argument: #{ARGV[0]}"
25944
puts "Run 'bin/dev help' for usage information"

lib/generators/react_on_rails/dev_tests_generator.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module ReactOnRails
77
module Generators
88
class DevTestsGenerator < Rails::Generators::Base
99
include GeneratorHelper
10+
1011
Rails::Generators.hide_namespace(namespace)
1112
source_root(File.expand_path("templates/dev_tests", __dir__))
1213

0 commit comments

Comments
 (0)