Exception: ReactOnRails::SmartError
- Defined in:
- lib/react_on_rails/smart_error.rb,
sig/react_on_rails/smart_error.rbs
Overview
SmartError provides enhanced error messages with actionable suggestions rubocop:disable Metrics/ClassLength
Constant Summary collapse
- DOCS_BASE_URL =
"https://reactonrails.com/docs/reference/error-reference"- UNKNOWN_ERROR_DEFINITION =
{ code: "ROR000", title: "Unknown Error", summary: "An unexpected SmartError type was raised.", sample_context: {}.freeze }.freeze
- ERROR_DEFINITIONS =
{ component_not_registered: { code: "ROR001", title: "Component Not Registered", summary: "React on Rails could not find the component in the client-side component registry.", sample_context: { component_name: "ProductCard", available_components: %w[ProductList ProductDetails UserProfile].freeze }.freeze }.freeze, missing_auto_loaded_bundle: { code: "ROR002", title: "Auto-loaded Bundle Missing", summary: "A component is configured for auto-loading, but its generated bundle is missing.", sample_context: { component_name: "Dashboard", expected_path: "app/javascript/packs/generated/Dashboard.js" }.freeze }.freeze, missing_auto_loaded_store_bundle: { code: "ROR003", title: "Auto-loaded Store Bundle Missing", summary: "A Redux store is configured for auto-loading, but its generated store bundle is missing.", sample_context: { component_name: "AppStore", expected_path: "app/javascript/packs/generated/AppStore.js" }.freeze }.freeze, hydration_mismatch: { code: "ROR004", title: "Hydration Mismatch", summary: "The server-rendered HTML does not match the React tree rendered in the browser.", sample_context: { component_name: "UserProfile" }.freeze }.freeze, server_rendering_error: { code: "ROR005", title: "Server Rendering Failed", summary: "Server-side rendering failed while rendering a React component.", sample_context: { component_name: "ComplexComponent", error_message: "window is not defined" }.freeze }.freeze, redux_store_not_found: { code: "ROR006", title: "Redux Store Not Found", summary: "A component requested a Redux store that was not registered.", sample_context: { store_name: "AppStore", available_stores: %w[UserStore ProductStore].freeze }.freeze }.freeze, configuration_error: { code: "ROR007", title: "Configuration Error", summary: "React on Rails detected invalid or incomplete configuration.", sample_context: { details: "config.server_bundle_js_file points to a missing file" }.freeze }.freeze }.freeze
Instance Attribute Summary collapse
-
#additional_context ⇒ Hash[Symbol, untyped]
readonly
Returns the value of attribute additional_context.
-
#component_name ⇒ String?
readonly
Returns the value of attribute component_name.
-
#error_type ⇒ Symbol, String
readonly
Returns the value of attribute error_type.
-
#js_code ⇒ String?
readonly
Returns the value of attribute js_code.
-
#props ⇒ String?
readonly
Returns the value of attribute props.
Class Method Summary collapse
- .docs_url_for(error_type) ⇒ String?
- .error_definition_for(error_type) ⇒ error_definition
- .error_definitions ⇒ Hash[Symbol, error_definition]
- .normalize_error_type(error_type) ⇒ Symbol
Instance Method Summary collapse
-
#additional_info ⇒ String
rubocop:disable Metrics/AbcSize.
- #build_error_message ⇒ String
- #code ⇒ String
-
#component_not_registered_solution ⇒ String
rubocop:disable Metrics/AbcSize.
- #configuration_error_solution ⇒ String
- #default_solution ⇒ String
- #docs_url ⇒ String?
- #error_definition ⇒ error_definition
-
#error_description ⇒ String
rubocop:disable Metrics/CyclomaticComplexity.
- #error_reference_section ⇒ String
- #error_type_title ⇒ String
-
#find_similar_components(name) ⇒ Array[String]
rubocop:disable Metrics/CyclomaticComplexity.
- #hydration_mismatch_solution ⇒ String
-
#initialize(error_type:, component_name: nil, props: nil, js_code: nil, **additional_context) ⇒ SmartError
constructor
A new instance of SmartError.
-
#missing_auto_loaded_bundle_solution ⇒ String
rubocop:enable Metrics/AbcSize.
- #missing_auto_loaded_store_bundle_solution ⇒ String
- #normalized_error_type ⇒ Symbol
- #packer_source_path_for_message ⇒ String
- #redux_store_not_found_solution ⇒ String
- #server_rendering_error_solution ⇒ String
- #solution ⇒ String
-
#troubleshooting_section ⇒ String
rubocop:enable Metrics/AbcSize.
Constructor Details
#initialize(error_type:, component_name: nil, props: nil, js_code: nil, **additional_context) ⇒ SmartError
Returns a new instance of SmartError.
101 102 103 104 105 106 107 108 109 110 |
# File 'lib/react_on_rails/smart_error.rb', line 101 def initialize(error_type:, component_name: nil, props: nil, js_code: nil, **additional_context) @error_type = error_type @component_name = component_name @props = props @js_code = js_code @additional_context = additional_context = super() end |
Instance Attribute Details
#additional_context ⇒ Hash[Symbol, untyped] (readonly)
Returns the value of attribute additional_context.
99 100 101 |
# File 'lib/react_on_rails/smart_error.rb', line 99 def additional_context @additional_context end |
#component_name ⇒ String? (readonly)
Returns the value of attribute component_name.
99 100 101 |
# File 'lib/react_on_rails/smart_error.rb', line 99 def component_name @component_name end |
#error_type ⇒ Symbol, String (readonly)
Returns the value of attribute error_type.
99 100 101 |
# File 'lib/react_on_rails/smart_error.rb', line 99 def error_type @error_type end |
#js_code ⇒ String? (readonly)
Returns the value of attribute js_code.
99 100 101 |
# File 'lib/react_on_rails/smart_error.rb', line 99 def js_code @js_code end |
#props ⇒ String? (readonly)
Returns the value of attribute props.
99 100 101 |
# File 'lib/react_on_rails/smart_error.rb', line 99 def props @props end |
Class Method Details
.docs_url_for(error_type) ⇒ String?
88 89 90 91 92 93 |
# File 'lib/react_on_rails/smart_error.rb', line 88 def self.docs_url_for(error_type) normalized_error_type = normalize_error_type(error_type) return unless error_definitions.key?(normalized_error_type) "#{DOCS_BASE_URL}##{error_definition_for(normalized_error_type).fetch(:code).downcase}" end |
.error_definition_for(error_type) ⇒ error_definition
84 85 86 |
# File 'lib/react_on_rails/smart_error.rb', line 84 def self.error_definition_for(error_type) ERROR_DEFINITIONS.fetch(normalize_error_type(error_type), UNKNOWN_ERROR_DEFINITION) end |
.error_definitions ⇒ Hash[Symbol, error_definition]
80 81 82 |
# File 'lib/react_on_rails/smart_error.rb', line 80 def self.error_definitions ERROR_DEFINITIONS end |
.normalize_error_type(error_type) ⇒ Symbol
95 96 97 |
# File 'lib/react_on_rails/smart_error.rb', line 95 def self.normalize_error_type(error_type) error_type.respond_to?(:to_sym) ? error_type.to_sym : error_type end |
Instance Method Details
#additional_info ⇒ String
rubocop:disable Metrics/AbcSize
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/react_on_rails/smart_error.rb', line 427 def additional_info info = [] info << "#{Rainbow('Component:').blue} #{component_name}" if component_name if additional_context[:available_components]&.any? info << "#{Rainbow('Registered components:').blue} #{additional_context[:available_components].join(', ')}" end info << "#{Rainbow('Rails Environment:').blue} development (detailed errors enabled)" if Rails.env.development? info << "#{Rainbow('Auto-load bundles:').blue} enabled" if ReactOnRails.configuration.auto_load_bundle return "" if info.empty? "\n#{Rainbow('📋 Context:').blue.bright}\n#{info.join("\n")}" end |
#build_error_message ⇒ String
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/react_on_rails/smart_error.rb', line 145 def header = Rainbow("❌ React on Rails Error [#{code}]: #{error_type_title}").red.bright = <<~MSG #{header} #{error_description} #{error_reference_section} #{Rainbow('💡 Suggested Solution:').yellow.bright} #{solution} #{additional_info} #{troubleshooting_section} MSG .strip end |
#code ⇒ String
112 113 114 |
# File 'lib/react_on_rails/smart_error.rb', line 112 def code error_definition.fetch(:code) end |
#component_not_registered_solution ⇒ String
rubocop:disable Metrics/AbcSize
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/react_on_rails/smart_error.rb', line 249 def component_not_registered_solution suggestions = [] # Check for similar component names if component_name && !component_name.empty? similar = find_similar_components(component_name) suggestions << "Did you mean one of these? #{similar.map { |s| Rainbow(s).green }.join(', ')}" if similar.any? end suggestions << <<~SOLUTION #{Rainbow('🚀 Recommended: Use Auto-Bundling (No Registration Required!)').green.bright} 1. Enable auto-bundling in your view: #{Rainbow("<%= react_component(\"#{component_name}\", props: {}, auto_load_bundle: true) %>").cyan} 2. Place your component in the components directory: #{Rainbow("app/javascript/#{ReactOnRails.configuration.components_subdirectory || 'components'}/#{component_name}/#{component_name}.jsx").cyan} #{' '} Component structure: #{Rainbow("#{ReactOnRails.configuration.components_subdirectory || 'components'}/").cyan} #{Rainbow("└── #{component_name}/").cyan} #{Rainbow(" └── #{component_name}.jsx").cyan} (must export default) 3. Generate the bundle: #{Rainbow('bundle exec rake react_on_rails:generate_packs').cyan} #{Rainbow("✨ That's it! No manual registration needed.").yellow} ───────────────────────────────────────────── #{Rainbow('Alternative: Manual Registration').gray} If you prefer manual registration: 1. Register in your entry file: #{Rainbow("ReactOnRails.register({ #{component_name}: #{component_name} });").cyan} 2. Import the component: #{Rainbow("import #{component_name} from './components/#{component_name}';").cyan} SOLUTION suggestions.join("\n") end |
#configuration_error_solution ⇒ String
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
# File 'lib/react_on_rails/smart_error.rb', line 401 def configuration_error_solution <<~SOLUTION Review your React on Rails configuration: 1. Check #{Rainbow('config/initializers/react_on_rails.rb').cyan} 2. Common configuration issues: - Invalid bundle paths - Missing Node modules location - Incorrect component subdirectory 3. Run configuration doctor: #{Rainbow('rake react_on_rails:doctor').cyan} SOLUTION end |
#default_solution ⇒ String
417 418 419 420 421 422 423 424 |
# File 'lib/react_on_rails/smart_error.rb', line 417 def default_solution <<~SOLUTION 1. Check the browser console for JavaScript errors 2. Review your server logs: #{Rainbow('tail -f log/development.log').cyan} 3. Run diagnostics: #{Rainbow('rake react_on_rails:doctor').cyan} 4. Set #{Rainbow('FULL_TEXT_ERRORS=true').cyan} for complete error output SOLUTION end |
#docs_url ⇒ String?
116 117 118 119 120 |
# File 'lib/react_on_rails/smart_error.rb', line 116 def docs_url return unless self.class.error_definitions.key?(normalized_error_type) self.class.docs_url_for(normalized_error_type) end |
#error_definition ⇒ error_definition
169 170 171 |
# File 'lib/react_on_rails/smart_error.rb', line 169 def error_definition self.class.error_definitions.fetch(normalized_error_type, UNKNOWN_ERROR_DEFINITION) end |
#error_description ⇒ String
rubocop:disable Metrics/CyclomaticComplexity
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/react_on_rails/smart_error.rb', line 201 def error_description case normalized_error_type when :component_not_registered <<~DESC Component '#{component_name}' was not found in the component registry. React on Rails offers two approaches: • Auto-bundling (recommended): Components load automatically, no registration needed • Manual registration: Traditional approach requiring explicit registration DESC when :missing_auto_loaded_bundle <<~DESC Component '#{component_name}' is configured for auto-loading but its bundle is missing. Expected location: #{additional_context[:expected_path]} DESC when :missing_auto_loaded_store_bundle <<~DESC Redux store '#{component_name}' is configured for auto-loading but its bundle is missing. Expected location: #{additional_context[:expected_path]} DESC when :hydration_mismatch <<~DESC The server-rendered HTML doesn't match what React rendered on the client. Component: #{component_name} DESC when :server_rendering_error <<~DESC An error occurred while server-side rendering component '#{component_name}'. #{additional_context[:]} DESC when :redux_store_not_found <<~DESC Redux store '#{additional_context[:store_name]}' was not found. Available stores: #{additional_context[:available_stores]&.join(', ') || 'none'} DESC when :configuration_error <<~DESC Invalid configuration detected. #{additional_context[:details]} DESC else "An unexpected error occurred." end end |
#error_reference_section ⇒ String
173 174 175 176 177 |
# File 'lib/react_on_rails/smart_error.rb', line 173 def error_reference_section lines = ["#{Rainbow('Code:').blue} #{code}"] lines << "#{Rainbow('Docs:').blue} #{docs_url}" if docs_url lines.join("\n") end |
#error_type_title ⇒ String
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/react_on_rails/smart_error.rb', line 179 def error_type_title case normalized_error_type when :component_not_registered "Component '#{component_name}' Not Registered" when :missing_auto_loaded_bundle "Auto-loaded Bundle Missing" when :missing_auto_loaded_store_bundle "Auto-loaded Store Bundle Missing" when :hydration_mismatch "Hydration Mismatch" when :server_rendering_error "Server Rendering Failed" when :redux_store_not_found "Redux Store Not Found" when :configuration_error "Configuration Error" else "Unknown Error" end end |
#find_similar_components(name) ⇒ Array[String]
rubocop:disable Metrics/CyclomaticComplexity
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/react_on_rails/smart_error.rb', line 451 def find_similar_components(name) return [] unless additional_context[:available_components] available = additional_context[:available_components] return [] if available.empty? available = available.uniq # Simple similarity check - could be enhanced with Levenshtein distance similar = available.select do |comp| comp.downcase.include?(name.downcase) || name.downcase.include?(comp.downcase) end # Also check for common naming patterns if similar.empty? # Check if user forgot to capitalize capitalized = name.capitalize similar = available.select { |comp| comp == capitalized } # Check for common suffixes if similar.empty? && !name.end_with?("Component") with_suffix = "#{name}Component" similar = available.select { |comp| comp == with_suffix } end end similar.take(3) # Limit suggestions # rubocop:enable Metrics/CyclomaticComplexity end |
#hydration_mismatch_solution ⇒ String
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/react_on_rails/smart_error.rb', line 341 def hydration_mismatch_solution <<~SOLUTION Common causes and solutions: 1. **Random IDs or timestamps**: Use consistent values between server and client #{Rainbow('// Bad: Math.random() or Date.now()').red} #{Rainbow('// Good: Use props or deterministic values').green} 2. **Browser-only APIs**: Check for client-side before using: #{Rainbow("if (typeof window !== 'undefined') { ... }").cyan} 3. **Different data**: Ensure props are identical on server and client - Check your redux store initialization - Verify railsContext is consistent 4. **Conditional rendering**: Avoid using user agent or viewport checks Debug tips: - Set #{Rainbow('prerender: false').cyan} temporarily to isolate the issue - Check browser console for hydration warnings - Compare server HTML with client render SOLUTION end |
#missing_auto_loaded_bundle_solution ⇒ String
rubocop:enable Metrics/AbcSize
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/react_on_rails/smart_error.rb', line 293 def missing_auto_loaded_bundle_solution <<~SOLUTION 1. Run the pack generation task: #{Rainbow('bundle exec rake react_on_rails:generate_packs').cyan} 2. Ensure your component is in the correct directory: #{Rainbow("app/javascript/#{ReactOnRails.configuration.components_subdirectory || 'components'}/#{component_name}/").cyan} 3. Check that the component file follows naming conventions: - Component file: #{Rainbow("#{component_name}.jsx").cyan} or #{Rainbow("#{component_name}.tsx").cyan} - Must export default 4. Verify webpack/shakapacker is configured for nested entries: #{Rainbow("config.nested_entries_dir = 'components'").cyan} SOLUTION end |
#missing_auto_loaded_store_bundle_solution ⇒ String
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/react_on_rails/smart_error.rb', line 310 def missing_auto_loaded_store_bundle_solution source_path = store_subdirectory = ReactOnRails.configuration.stores_subdirectory || "ror_stores" store_source_path = "#{source_path}/**/#{store_subdirectory}/#{component_name}.js" <<~SOLUTION 1. Run the pack generation task: #{Rainbow('bundle exec rake react_on_rails:generate_packs').cyan} 2. Ensure your store is in a directory matching stores_subdirectory under packer_source_path: #{Rainbow(store_source_path).cyan} 3. Check that the store file follows naming conventions: - Store file: #{Rainbow("#{component_name}.js").cyan} or #{Rainbow("#{component_name}.ts").cyan} - Must export default a store generator function 4. Verify stores_subdirectory is configured: #{Rainbow("config.stores_subdirectory = 'ror_stores'").cyan} SOLUTION end |
#normalized_error_type ⇒ Symbol
165 166 167 |
# File 'lib/react_on_rails/smart_error.rb', line 165 def normalized_error_type self.class.normalize_error_type(error_type) end |
#packer_source_path_for_message ⇒ String
331 332 333 334 335 336 337 338 339 |
# File 'lib/react_on_rails/smart_error.rb', line 331 def return "app/javascript" unless defined?(::Shakapacker) && ::Shakapacker.respond_to?(:config) config = ::Shakapacker.config return "app/javascript" unless config.respond_to?(:config_path) && config.config_path.exist? return "app/javascript" unless config.respond_to?(:source_path) config.source_path end |
#redux_store_not_found_solution ⇒ String
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/react_on_rails/smart_error.rb', line 385 def redux_store_not_found_solution <<~SOLUTION 1. Register your Redux store: #{Rainbow("ReactOnRails.registerStore({ #{additional_context[:store_name]}: #{additional_context[:store_name]} });").cyan} 2. Ensure the store is imported: #{Rainbow("import #{additional_context[:store_name]} from './store/#{additional_context[:store_name]}';").cyan} 3. Initialize the store before rendering components that depend on it: #{Rainbow("<%= redux_store('#{additional_context[:store_name]}', props: {}) %>").cyan} 4. Check store dependencies in your component: #{Rainbow("store_dependencies: ['#{additional_context[:store_name]}']").cyan} SOLUTION end |
#server_rendering_error_solution ⇒ String
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/react_on_rails/smart_error.rb', line 365 def server_rendering_error_solution <<~SOLUTION 1. Check your JavaScript console output: #{Rainbow("tail -f log/development.log | grep 'React on Rails'").cyan} 2. Common issues: - Missing Node.js dependencies: #{Rainbow('cd client && npm install').cyan} - Syntax errors in component code - Using browser-only APIs without checks 3. Debug server rendering: - Set #{Rainbow('config.trace = true').cyan} in your configuration - Set #{Rainbow('config.development_mode = true').cyan} for better errors - Check #{Rainbow('config.server_bundle_js_file').cyan} points to correct file 4. Verify your server bundle: #{Rainbow('bin/shakapacker').cyan} or #{Rainbow('bin/webpack').cyan} SOLUTION end |
#solution ⇒ String
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/react_on_rails/smart_error.rb', line 122 def solution case normalized_error_type when :component_not_registered component_not_registered_solution when :missing_auto_loaded_bundle missing_auto_loaded_bundle_solution when :missing_auto_loaded_store_bundle missing_auto_loaded_store_bundle_solution when :hydration_mismatch hydration_mismatch_solution when :server_rendering_error server_rendering_error_solution when :redux_store_not_found redux_store_not_found_solution when :configuration_error configuration_error_solution else default_solution end end |
#troubleshooting_section ⇒ String
rubocop:enable Metrics/AbcSize
446 447 448 |
# File 'lib/react_on_rails/smart_error.rb', line 446 def troubleshooting_section "\n#{Rainbow('🔧 Need More Help?').magenta.bright}\n#{Utils.default_troubleshooting_section}" end |