react_on_rails 16.3.0 → 16.4.0.rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/Steepfile +1 -0
- data/lib/generators/react_on_rails/dev_tests_generator.rb +10 -3
- data/lib/generators/react_on_rails/install_generator.rb +15 -11
- data/lib/generators/react_on_rails/templates/base/base/bin/dev +49 -0
- data/lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook +5 -0
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +3 -3
- data/lib/react_on_rails/configuration.rb +14 -4
- data/lib/react_on_rails/dev/database_checker.rb +168 -0
- data/lib/react_on_rails/dev/server_manager.rb +72 -16
- data/lib/react_on_rails/doctor.rb +2 -2
- data/lib/react_on_rails/helper.rb +53 -2
- data/lib/react_on_rails/locales/base.rb +15 -0
- data/lib/react_on_rails/packer_utils.rb +37 -5
- data/lib/react_on_rails/packs_generator.rb +141 -11
- data/lib/react_on_rails/smart_error.rb +26 -0
- data/lib/react_on_rails/test_helper.rb +1 -1
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker.rb +11 -3
- data/lib/tasks/generate_packs.rake +7 -3
- data/rakelib/dummy_apps.rake +3 -3
- data/rakelib/node_package.rake +3 -3
- data/rakelib/rbs.rake +1 -1
- data/rakelib/shakapacker_examples.rake +14 -6
- data/sig/react_on_rails/configuration.rbs +2 -0
- data/sig/react_on_rails/dev/database_checker.rbs +27 -0
- data/sig/react_on_rails/packer_utils.rbs +5 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e73e8a4df085484ab44226919e7e642d67b609b03375b34845ea43f896e2ca43
|
|
4
|
+
data.tar.gz: c94d4c27cd4ae57c9b382b80327686d918ad2906f561879c04cc90817f89d195
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5eac27c292bd94ffbdb2c61a3d64d0021b39320c7b82778df631c4e231cfafa190a943101a41d57cf053a0b52f948b3dd0f8c0db5d48cc4a7f9e12177043c770
|
|
7
|
+
data.tar.gz: ab3b3beeda1c9ae9d5bf21b2e6ba946d1ead835ed9e0b6fc7b3017e3cdfdb8c101aab5f25a3fd35f6486c0f2dfdd4e68927f9b0a9d0a9020e709f9e33d0a29bb
|
data/Gemfile.lock
CHANGED
data/Steepfile
CHANGED
|
@@ -28,6 +28,7 @@ target :lib do
|
|
|
28
28
|
check "lib/react_on_rails.rb"
|
|
29
29
|
check "lib/react_on_rails/configuration.rb"
|
|
30
30
|
check "lib/react_on_rails/controller.rb"
|
|
31
|
+
check "lib/react_on_rails/dev/database_checker.rb"
|
|
31
32
|
check "lib/react_on_rails/dev/file_manager.rb"
|
|
32
33
|
check "lib/react_on_rails/dev/pack_generator.rb"
|
|
33
34
|
check "lib/react_on_rails/dev/process_manager.rb"
|
|
@@ -47,11 +47,18 @@ module ReactOnRails
|
|
|
47
47
|
File.open(hello_world_index, "w+") { |f| f.puts new_hello_world_contents }
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
def
|
|
50
|
+
def add_react_on_rails_as_file_dependency
|
|
51
|
+
# Add react-on-rails as a file dependency pointing to the local package
|
|
52
|
+
# This allows testing with the local npm package without needing yalc
|
|
51
53
|
package_json = File.join(destination_root, "package.json")
|
|
52
54
|
contents = JSON.parse(File.read(package_json))
|
|
53
|
-
contents["
|
|
54
|
-
|
|
55
|
+
contents["dependencies"] ||= {}
|
|
56
|
+
|
|
57
|
+
# Calculate relative path from the generated example to the npm package
|
|
58
|
+
# Generated examples are in gen-examples/examples/<name>/
|
|
59
|
+
# The npm package is in packages/react-on-rails/
|
|
60
|
+
contents["dependencies"]["react-on-rails"] = "file:../../../packages/react-on-rails"
|
|
61
|
+
|
|
55
62
|
File.open(package_json, "w+") { |f| f.puts JSON.pretty_generate(contents) }
|
|
56
63
|
end
|
|
57
64
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "rails/generators"
|
|
4
4
|
require "json"
|
|
5
|
+
require "bundler"
|
|
5
6
|
require_relative "generator_helper"
|
|
6
7
|
require_relative "generator_messages"
|
|
7
8
|
require_relative "js_dependency_manager"
|
|
@@ -218,10 +219,10 @@ module ReactOnRails
|
|
|
218
219
|
end
|
|
219
220
|
|
|
220
221
|
def shakapacker_in_lockfile?(gem_name)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
# Always check the target app's Gemfile.lock, not inherited BUNDLE_GEMFILE
|
|
223
|
+
# See: https://github.com/shakacode/react_on_rails/issues/2287
|
|
224
|
+
File.file?("Gemfile.lock") &&
|
|
225
|
+
File.foreach("Gemfile.lock").any? { |l| l.match?(/^\s{4}#{Regexp.escape(gem_name)}\s\(/) }
|
|
225
226
|
end
|
|
226
227
|
|
|
227
228
|
def shakapacker_in_bundler_specs?(gem_name)
|
|
@@ -232,10 +233,10 @@ module ReactOnRails
|
|
|
232
233
|
end
|
|
233
234
|
|
|
234
235
|
def shakapacker_in_gemfile_text?(gem_name)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
File.file?(
|
|
238
|
-
File.foreach(
|
|
236
|
+
# Always check the target app's Gemfile, not inherited BUNDLE_GEMFILE
|
|
237
|
+
# See: https://github.com/shakacode/react_on_rails/issues/2287
|
|
238
|
+
File.file?("Gemfile") &&
|
|
239
|
+
File.foreach("Gemfile").any? { |l| l.match?(/^\s*gem\s+['"]#{Regexp.escape(gem_name)}['"]/) }
|
|
239
240
|
end
|
|
240
241
|
|
|
241
242
|
def cli_exists?(command)
|
|
@@ -263,7 +264,9 @@ module ReactOnRails
|
|
|
263
264
|
return if shakapacker_in_gemfile?
|
|
264
265
|
|
|
265
266
|
puts Rainbow("📝 Adding Shakapacker to Gemfile...").yellow
|
|
266
|
-
|
|
267
|
+
# Use with_unbundled_env to prevent inheriting BUNDLE_GEMFILE from parent process
|
|
268
|
+
# See: https://github.com/shakacode/react_on_rails/issues/2287
|
|
269
|
+
success = Bundler.with_unbundled_env { system("bundle add shakapacker --strict") }
|
|
267
270
|
return if success
|
|
268
271
|
|
|
269
272
|
handle_shakapacker_gemfile_error
|
|
@@ -273,15 +276,16 @@ module ReactOnRails
|
|
|
273
276
|
puts Rainbow("⚙️ Installing Shakapacker (required for webpack integration)...").yellow
|
|
274
277
|
|
|
275
278
|
# First run bundle install to make shakapacker available
|
|
279
|
+
# Use with_unbundled_env to prevent inheriting BUNDLE_GEMFILE from parent process
|
|
276
280
|
puts Rainbow("📦 Running bundle install...").yellow
|
|
277
|
-
bundle_success = system("bundle install")
|
|
281
|
+
bundle_success = Bundler.with_unbundled_env { system("bundle install") }
|
|
278
282
|
unless bundle_success
|
|
279
283
|
handle_shakapacker_install_error
|
|
280
284
|
return
|
|
281
285
|
end
|
|
282
286
|
|
|
283
287
|
# Then run the shakapacker installer
|
|
284
|
-
success = system("bundle exec rails shakapacker:install")
|
|
288
|
+
success = Bundler.with_unbundled_env { system("bundle exec rails shakapacker:install") }
|
|
285
289
|
return if success
|
|
286
290
|
|
|
287
291
|
handle_shakapacker_install_error
|
|
@@ -17,6 +17,49 @@
|
|
|
17
17
|
# 2. Modify this script for project-specific command-line behavior
|
|
18
18
|
# 3. Extend ReactOnRails::Dev classes in your Rails app for advanced customization
|
|
19
19
|
# 4. Use classes directly: ReactOnRails::Dev::ServerManager.start(:development, "Custom.procfile")
|
|
20
|
+
#
|
|
21
|
+
# EXTENSIBLE PRECOMPILE PATTERN (Alternative to precompile_hook)
|
|
22
|
+
# ===============================================================
|
|
23
|
+
# If your project has custom build requirements (e.g., ReScript, TypeScript
|
|
24
|
+
# compilation, custom locale generation), you can handle precompile tasks
|
|
25
|
+
# directly in this script instead of using the precompile_hook mechanism.
|
|
26
|
+
#
|
|
27
|
+
# Benefits of this approach:
|
|
28
|
+
# - Single place to manage all precompile tasks
|
|
29
|
+
# - Direct Ruby API calls (faster, no shell spawning overhead)
|
|
30
|
+
# - Better version manager compatibility (mise, asdf, rbenv)
|
|
31
|
+
# - Clean Procfiles without embedded precompile logic
|
|
32
|
+
#
|
|
33
|
+
# To use this pattern:
|
|
34
|
+
# 1. Uncomment and customize the run_precompile_tasks method below
|
|
35
|
+
# 2. Remove precompile_hook from config/shakapacker.yml if present
|
|
36
|
+
# 3. Uncomment the run_precompile_tasks call near the bottom of this file
|
|
37
|
+
#
|
|
38
|
+
# See documentation: https://www.shakacode.com/react-on-rails/docs/building-features/extensible-precompile-pattern
|
|
39
|
+
#
|
|
40
|
+
# def run_precompile_tasks
|
|
41
|
+
# require_relative "../config/environment"
|
|
42
|
+
#
|
|
43
|
+
# puts "📦 Running precompile tasks..."
|
|
44
|
+
#
|
|
45
|
+
# # Example: Build ReScript files
|
|
46
|
+
# # print " ReScript build... "
|
|
47
|
+
# # unless system("yarn res:build")
|
|
48
|
+
# # puts "❌"
|
|
49
|
+
# # exit(1)
|
|
50
|
+
# # end
|
|
51
|
+
# # puts "✅"
|
|
52
|
+
#
|
|
53
|
+
# # Example: Generate locales using direct Ruby API (faster than rake task)
|
|
54
|
+
# # This avoids shell spawning and version manager PATH issues.
|
|
55
|
+
# # Exceptions (e.g., missing directories) will bubble up and stop the server,
|
|
56
|
+
# # which is the desired behavior for catching configuration issues early.
|
|
57
|
+
# print " Locale generation... "
|
|
58
|
+
# ReactOnRails::Locales.compile if ReactOnRails.configuration.i18n_dir.present?
|
|
59
|
+
# puts "✅"
|
|
60
|
+
#
|
|
61
|
+
# puts ""
|
|
62
|
+
# end
|
|
20
63
|
|
|
21
64
|
require "bundler/setup"
|
|
22
65
|
require "react_on_rails/dev"
|
|
@@ -31,4 +74,10 @@ DEFAULT_ROUTE = "hello_world"
|
|
|
31
74
|
argv_with_defaults = ARGV.dup
|
|
32
75
|
argv_with_defaults.push("--route=#{DEFAULT_ROUTE}") unless argv_with_defaults.any? { |arg| arg.start_with?("--route") }
|
|
33
76
|
|
|
77
|
+
# Uncomment to run custom precompile tasks before starting the server
|
|
78
|
+
# Only run for server start commands (not kill/help)
|
|
79
|
+
# unless ARGV.include?("kill") || ARGV.include?("-h") || ARGV.include?("--help") || ARGV.include?("help")
|
|
80
|
+
# run_precompile_tasks
|
|
81
|
+
# end
|
|
82
|
+
|
|
34
83
|
ReactOnRails::Dev::ServerManager.run_from_command_line(argv_with_defaults)
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
# Self-guard: skip if bin/dev already ran precompile tasks.
|
|
5
|
+
# This prevents duplicate execution in HMR mode (two webpack processes)
|
|
6
|
+
# regardless of Shakapacker version.
|
|
7
|
+
exit 0 if ENV["SHAKAPACKER_SKIP_PRECOMPILE_HOOK"] == "true"
|
|
8
|
+
|
|
4
9
|
# Shakapacker precompile hook for React on Rails
|
|
5
10
|
#
|
|
6
11
|
# This script runs before webpack compilation to generate pack files
|
data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# React on Rails configuration
|
|
4
|
-
# See https://github.com/shakacode/react_on_rails/blob/master/docs/configuration/
|
|
4
|
+
# See https://github.com/shakacode/react_on_rails/blob/master/docs/configuration/README.md
|
|
5
5
|
# for complete documentation of all configuration options.
|
|
6
6
|
|
|
7
7
|
ReactOnRails.configure do |config|
|
|
@@ -43,7 +43,7 @@ ReactOnRails.configure do |config|
|
|
|
43
43
|
# ALTERNATIVE APPROACH: Uncomment below AND configure ReactOnRails::TestHelper
|
|
44
44
|
# - Provides explicit control over test asset compilation timing
|
|
45
45
|
# - Requires adding ReactOnRails::TestHelper to spec/rails_helper.rb
|
|
46
|
-
# - See: https://github.com/shakacode/react_on_rails/blob/master/docs/
|
|
46
|
+
# - See: https://github.com/shakacode/react_on_rails/blob/master/docs/building-features/testing-configuration.md
|
|
47
47
|
#
|
|
48
48
|
config.build_test_command = "RAILS_ENV=test bin/shakapacker"
|
|
49
49
|
|
|
@@ -62,5 +62,5 @@ ReactOnRails.configure do |config|
|
|
|
62
62
|
# - Custom rendering extensions
|
|
63
63
|
# - And more...
|
|
64
64
|
#
|
|
65
|
-
# See: https://github.com/shakacode/react_on_rails/blob/master/docs/configuration/
|
|
65
|
+
# See: https://github.com/shakacode/react_on_rails/blob/master/docs/configuration/README.md
|
|
66
66
|
end
|
|
@@ -58,6 +58,7 @@ module ReactOnRails
|
|
|
58
58
|
same_bundle_for_client_and_server: false,
|
|
59
59
|
i18n_output_format: nil,
|
|
60
60
|
components_subdirectory: nil,
|
|
61
|
+
stores_subdirectory: nil,
|
|
61
62
|
make_generated_server_bundle_the_entrypoint: false,
|
|
62
63
|
defer_generated_component_packs: false,
|
|
63
64
|
# Maximum time in milliseconds to wait for client-side component registration after page load.
|
|
@@ -66,7 +67,11 @@ module ReactOnRails
|
|
|
66
67
|
component_registry_timeout: DEFAULT_COMPONENT_REGISTRY_TIMEOUT,
|
|
67
68
|
generated_component_packs_loading_strategy: nil,
|
|
68
69
|
server_bundle_output_path: DEFAULT_SERVER_BUNDLE_OUTPUT_PATH,
|
|
69
|
-
enforce_private_server_bundles: false
|
|
70
|
+
enforce_private_server_bundles: false,
|
|
71
|
+
# Whether to check database connectivity before starting bin/dev.
|
|
72
|
+
# Set to false to disable (saves ~1-2 seconds startup time).
|
|
73
|
+
# Can also be disabled via SKIP_DATABASE_CHECK=true or bin/dev --skip-database-check
|
|
74
|
+
check_database_on_dev_start: true
|
|
70
75
|
)
|
|
71
76
|
end
|
|
72
77
|
|
|
@@ -75,6 +80,7 @@ module ReactOnRails
|
|
|
75
80
|
:trace, :development_mode, :logging_on_server, :server_renderer_pool_size,
|
|
76
81
|
:server_renderer_timeout, :skip_display_none, :raise_on_prerender_error,
|
|
77
82
|
:generated_assets_dirs, :generated_assets_dir, :components_subdirectory,
|
|
83
|
+
:stores_subdirectory,
|
|
78
84
|
:webpack_generated_files, :rendering_extension, :build_test_command,
|
|
79
85
|
:build_production_command, :i18n_dir, :i18n_yml_dir, :i18n_output_format,
|
|
80
86
|
:i18n_yml_safe_load_options, :defer_generated_component_packs,
|
|
@@ -83,7 +89,8 @@ module ReactOnRails
|
|
|
83
89
|
:make_generated_server_bundle_the_entrypoint,
|
|
84
90
|
:generated_component_packs_loading_strategy,
|
|
85
91
|
:component_registry_timeout,
|
|
86
|
-
:server_bundle_output_path, :enforce_private_server_bundles
|
|
92
|
+
:server_bundle_output_path, :enforce_private_server_bundles,
|
|
93
|
+
:check_database_on_dev_start
|
|
87
94
|
|
|
88
95
|
# Class instance variable and mutex to track if deprecation warning has been shown
|
|
89
96
|
# Using mutex to ensure thread-safety in multi-threaded environments
|
|
@@ -143,8 +150,9 @@ module ReactOnRails
|
|
|
143
150
|
same_bundle_for_client_and_server: nil,
|
|
144
151
|
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil,
|
|
145
152
|
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
|
|
146
|
-
components_subdirectory: nil, auto_load_bundle: nil,
|
|
147
|
-
component_registry_timeout: nil, server_bundle_output_path: nil, enforce_private_server_bundles: nil
|
|
153
|
+
components_subdirectory: nil, stores_subdirectory: nil, auto_load_bundle: nil,
|
|
154
|
+
component_registry_timeout: nil, server_bundle_output_path: nil, enforce_private_server_bundles: nil,
|
|
155
|
+
check_database_on_dev_start: nil)
|
|
148
156
|
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
|
|
149
157
|
self.generated_assets_dirs = generated_assets_dirs
|
|
150
158
|
self.generated_assets_dir = generated_assets_dir
|
|
@@ -181,12 +189,14 @@ module ReactOnRails
|
|
|
181
189
|
|
|
182
190
|
self.server_render_method = server_render_method
|
|
183
191
|
self.components_subdirectory = components_subdirectory
|
|
192
|
+
self.stores_subdirectory = stores_subdirectory
|
|
184
193
|
self.auto_load_bundle = auto_load_bundle
|
|
185
194
|
self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint
|
|
186
195
|
self.defer_generated_component_packs = defer_generated_component_packs
|
|
187
196
|
self.generated_component_packs_loading_strategy = generated_component_packs_loading_strategy
|
|
188
197
|
self.server_bundle_output_path = server_bundle_output_path
|
|
189
198
|
self.enforce_private_server_bundles = enforce_private_server_bundles
|
|
199
|
+
self.check_database_on_dev_start = check_database_on_dev_start.nil? ? true : check_database_on_dev_start
|
|
190
200
|
end
|
|
191
201
|
# rubocop:enable Metrics/AbcSize
|
|
192
202
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "rainbow"
|
|
5
|
+
|
|
6
|
+
module ReactOnRails
|
|
7
|
+
module Dev
|
|
8
|
+
# DatabaseChecker validates that the Rails database is properly set up
|
|
9
|
+
# before starting the development server.
|
|
10
|
+
#
|
|
11
|
+
# This prevents confusing errors when running bin/dev on a fresh checkout
|
|
12
|
+
# or after database cleanup.
|
|
13
|
+
#
|
|
14
|
+
# Checks performed:
|
|
15
|
+
# 1. Database exists and is accessible
|
|
16
|
+
# 2. Migrations are up to date (optional warning)
|
|
17
|
+
#
|
|
18
|
+
# Can be disabled via:
|
|
19
|
+
# - Environment variable: SKIP_DATABASE_CHECK=true
|
|
20
|
+
# - CLI flag: bin/dev --skip-database-check
|
|
21
|
+
# - Configuration: ReactOnRails.configure { |c| c.check_database_on_dev_start = false }
|
|
22
|
+
#
|
|
23
|
+
# Note: This check spawns a Rails runner process which adds ~1-2 seconds to startup.
|
|
24
|
+
# Disable it if this overhead is unacceptable for your workflow.
|
|
25
|
+
#
|
|
26
|
+
class DatabaseChecker
|
|
27
|
+
class << self
|
|
28
|
+
# Check if the database is set up and accessible
|
|
29
|
+
#
|
|
30
|
+
# @param skip [Boolean] if true, skip the check entirely
|
|
31
|
+
# @return [Boolean] true if database is ready (or check was skipped), false otherwise
|
|
32
|
+
def check_database(skip: false)
|
|
33
|
+
return true if should_skip_check?(skip)
|
|
34
|
+
|
|
35
|
+
print_checking_message
|
|
36
|
+
result = run_database_check
|
|
37
|
+
|
|
38
|
+
case result[:status]
|
|
39
|
+
when :ok
|
|
40
|
+
print_database_ok
|
|
41
|
+
check_pending_migrations
|
|
42
|
+
true
|
|
43
|
+
when :error
|
|
44
|
+
print_database_error(result[:error])
|
|
45
|
+
false
|
|
46
|
+
when :not_rails_app
|
|
47
|
+
print_skipped_message("bin/rails not found — skipping database check")
|
|
48
|
+
true
|
|
49
|
+
else
|
|
50
|
+
print_skipped_message(result[:error] || "unexpected status: #{result[:status]}")
|
|
51
|
+
true
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def should_skip_check?(skip_flag)
|
|
58
|
+
# 1. CLI flag takes highest priority
|
|
59
|
+
return true if skip_flag
|
|
60
|
+
|
|
61
|
+
# 2. Environment variable
|
|
62
|
+
return true if ENV["SKIP_DATABASE_CHECK"] == "true"
|
|
63
|
+
|
|
64
|
+
# 3. ReactOnRails configuration (if available)
|
|
65
|
+
return true if defined?(ReactOnRails) &&
|
|
66
|
+
ReactOnRails.respond_to?(:configuration) &&
|
|
67
|
+
ReactOnRails.configuration.check_database_on_dev_start == false
|
|
68
|
+
|
|
69
|
+
false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def run_database_check
|
|
73
|
+
check_script = <<~RUBY
|
|
74
|
+
unless defined?(ActiveRecord)
|
|
75
|
+
puts 'DATABASE_OK' # No ActiveRecord = no database to check
|
|
76
|
+
exit
|
|
77
|
+
end
|
|
78
|
+
begin
|
|
79
|
+
ActiveRecord::Base.connection.execute('SELECT 1')
|
|
80
|
+
puts 'DATABASE_OK'
|
|
81
|
+
rescue StandardError => e
|
|
82
|
+
puts 'DATABASE_ERROR'
|
|
83
|
+
puts e.message
|
|
84
|
+
end
|
|
85
|
+
RUBY
|
|
86
|
+
|
|
87
|
+
# SECURITY: Using array form of Open3.capture3 is safe from command injection.
|
|
88
|
+
# The check_script is hardcoded above and never includes user input.
|
|
89
|
+
stdout, stderr, status = Open3.capture3("bin/rails", "runner", check_script)
|
|
90
|
+
parse_check_result(stdout, stderr, status)
|
|
91
|
+
rescue Errno::ENOENT
|
|
92
|
+
{ status: :not_rails_app }
|
|
93
|
+
rescue StandardError => e
|
|
94
|
+
{ status: :unknown_error, error: e.message }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def parse_check_result(stdout, stderr, status)
|
|
98
|
+
return { status: :error, error: "#{stdout}\n#{stderr}".strip } unless status.success?
|
|
99
|
+
|
|
100
|
+
lines = stdout.strip.split("\n")
|
|
101
|
+
|
|
102
|
+
# Empty output with successful exit means the process completed without error.
|
|
103
|
+
# This can happen when ActiveRecord is not loaded or when Rails runner
|
|
104
|
+
# exits cleanly without printing anything.
|
|
105
|
+
return { status: :ok } if lines.empty?
|
|
106
|
+
|
|
107
|
+
case lines.first
|
|
108
|
+
when "DATABASE_OK" then { status: :ok }
|
|
109
|
+
when "DATABASE_ERROR" then { status: :error, error: lines[1..].join("\n") }
|
|
110
|
+
else
|
|
111
|
+
if lines.any? { |line| line.strip == "DATABASE_OK" }
|
|
112
|
+
{ status: :ok }
|
|
113
|
+
else
|
|
114
|
+
{ status: :error, error: "#{stdout}\n#{stderr}".strip }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def check_pending_migrations
|
|
120
|
+
stdout, _stderr, status = Open3.capture3("bin/rails", "db:migrate:status")
|
|
121
|
+
return unless status.success? && stdout.include?(" down ")
|
|
122
|
+
|
|
123
|
+
print_pending_migrations_warning
|
|
124
|
+
rescue StandardError => e
|
|
125
|
+
# Ignore errors - this is just a helpful warning.
|
|
126
|
+
# Common case: schema_migrations table doesn't exist yet on brand new databases.
|
|
127
|
+
warn "[ReactOnRails] Migration check failed: #{e.message}" if ENV["DEBUG"] == "1"
|
|
128
|
+
nil
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def print_checking_message
|
|
132
|
+
puts "\n#{Rainbow('Checking database setup...').cyan}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def print_database_ok
|
|
136
|
+
puts Rainbow(" Database is accessible").green
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def print_skipped_message(reason)
|
|
140
|
+
puts Rainbow(" #{reason}").yellow
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def print_pending_migrations_warning
|
|
144
|
+
puts Rainbow(" Pending migrations detected").yellow
|
|
145
|
+
puts Rainbow(" Run: bin/rails db:migrate").yellow
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def print_database_error(error)
|
|
149
|
+
puts Rainbow(" Database check failed").red
|
|
150
|
+
puts ""
|
|
151
|
+
puts "#{Rainbow(" Error: #{truncate_error(error)}").red}\n" if error && !error.empty?
|
|
152
|
+
puts Rainbow("To fix, run:").yellow
|
|
153
|
+
puts ""
|
|
154
|
+
puts " #{Rainbow('bin/rails db:prepare').green} # Creates database if needed and runs pending migrations"
|
|
155
|
+
puts ""
|
|
156
|
+
puts Rainbow("Then run bin/dev again.").blue
|
|
157
|
+
puts ""
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def truncate_error(error)
|
|
161
|
+
return error if error.length <= 500
|
|
162
|
+
|
|
163
|
+
"#{error[0, 500]}...\n (Set DEBUG=1 to see full error)"
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|