Module: ReactOnRails::Utils

Defined in:
lib/react_on_rails/utils.rb

Defined Under Namespace

Modules: Required

Constant Summary collapse

TRUNCATION_FILLER =
"\n... TRUNCATED #{
  Rainbow('To see the full output, set FULL_TEXT_ERRORS=true.').red
} ...\n".freeze

Class Method Summary collapse

Class Method Details

.bundle_js_file_path(bundle_name) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/react_on_rails/utils.rb', line 74

def self.bundle_js_file_path(bundle_name)
  # Priority order depends on bundle type:
  # SERVER BUNDLES (normal case): Try private non-public locations first, then manifest, then legacy
  # CLIENT BUNDLES (normal case): Try manifest first, then fallback locations
  if bundle_name == "manifest.json"
    # Default to the non-hashed name in the specified output directory, which, for legacy
    # React on Rails, this is the output directory picked up by the asset pipeline.
    # For Shakapacker, this is the public output path defined in the (shaka/web)packer.yml file.
    File.join(public_bundles_full_path, bundle_name)
  else
    bundle_js_file_path_with_packer(bundle_name)
  end
end

.default_troubleshooting_sectionObject



315
316
317
318
319
320
321
322
323
# File 'lib/react_on_rails/utils.rb', line 315

def self.default_troubleshooting_section
  <<~DEFAULT
    📞 Get Help & Support:
       • 🚀 Professional Support: react_on_rails@shakacode.com (fastest resolution)
       • 💬 React + Rails Slack: https://invite.reactrails.com
       • 🆓 GitHub Issues: https://github.com/shakacode/react_on_rails/issues
       • 📖 Discussions: https://github.com/shakacode/react_on_rails/discussions
  DEFAULT
end

.find_most_recent_mtime(files) ⇒ Object



293
294
295
296
297
298
# File 'lib/react_on_rails/utils.rb', line 293

def self.find_most_recent_mtime(files)
  files.reduce(1.year.ago) do |newest_time, file|
    mt = File.mtime(file)
    [mt, newest_time].max
  end
end

.full_text_errors_enabled?Boolean

Returns:

  • (Boolean)


273
274
275
# File 'lib/react_on_rails/utils.rb', line 273

def self.full_text_errors_enabled?
  ENV["FULL_TEXT_ERRORS"] == "true"
end

.gem_available?(name) ⇒ Boolean

Returns:

  • (Boolean)


219
220
221
222
223
224
225
226
227
228
229
# File 'lib/react_on_rails/utils.rb', line 219

def self.gem_available?(name)
  Gem.loaded_specs[name].present?
rescue Gem::LoadError
  false
rescue StandardError
  begin
    Gem.available?(name).present?
  rescue NoMethodError
    false
  end
end

.generated_assets_full_pathObject

DEPRECATED: Use public_bundles_full_path for clarity about public vs private bundle paths



215
216
217
# File 'lib/react_on_rails/utils.rb', line 215

def self.generated_assets_full_path
  public_bundles_full_path
end

.invoke_and_exit_if_failed(cmd, failure_message) ⇒ Object

Invokes command, exiting with a detailed message if there’s a failure.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/react_on_rails/utils.rb', line 48

def self.invoke_and_exit_if_failed(cmd, failure_message)
  stdout, stderr, status = Open3.capture3(cmd)
  unless status.success?
    stdout_msg = stdout.present? ? "\nstdout:\n#{stdout.strip}\n" : ""
    stderr_msg = stderr.present? ? "\nstderr:\n#{stderr.strip}\n" : ""
    msg = <<~MSG
      React on Rails FATAL ERROR!
      #{failure_message}
      cmd: #{cmd}
      exitstatus: #{status.exitstatus}#{stdout_msg}#{stderr_msg}
    MSG

    puts wrap_message(msg)
    puts ""
    puts default_troubleshooting_section

    # Rspec catches exit without! in the exit callbacks
    exit!(1)
  end
  [stdout, stderr, status]
end

.object_to_boolean(value) ⇒ Object



39
40
41
# File 'lib/react_on_rails/utils.rb', line 39

def self.object_to_boolean(value)
  [true, "true", "yes", 1, "1", "t"].include?(value.instance_of?(String) ? value.downcase : value)
end

.prepend_cd_node_modules_directory(cmd) ⇒ Object



197
198
199
# File 'lib/react_on_rails/utils.rb', line 197

def self.prepend_cd_node_modules_directory(cmd)
  "cd \"#{ReactOnRails.configuration.node_modules_location}\" && #{cmd}"
end

.prepend_to_file_if_text_not_present(file:, text_to_prepend:, regex:) ⇒ Object



300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/react_on_rails/utils.rb', line 300

def self.prepend_to_file_if_text_not_present(file:, text_to_prepend:, regex:)
  if File.exist?(file)
    file_content = File.read(file)

    return if file_content.match(regex)

    content_with_prepended_text = text_to_prepend + file_content
    File.write(file, content_with_prepended_text, mode: "w")
  else
    File.write(file, text_to_prepend, mode: "w+")
  end

  puts "Prepended\n#{text_to_prepend}to #{file}."
end

.public_bundles_full_pathObject



210
211
212
# File 'lib/react_on_rails/utils.rb', line 210

def self.public_bundles_full_path
  ReactOnRails::PackerUtils.packer_public_output_path
end

.rails_version_less_than(version) ⇒ Object



181
182
183
184
185
186
187
188
189
# File 'lib/react_on_rails/utils.rb', line 181

def self.rails_version_less_than(version)
  @rails_version_less_than ||= {}

  return @rails_version_less_than[version] if @rails_version_less_than.key?(version)

  @rails_version_less_than[version] = begin
    Gem::Version.new(Rails.version) < Gem::Version.new(version)
  end
end

.react_client_manifest_file_pathObject



156
157
158
159
160
161
# File 'lib/react_on_rails/utils.rb', line 156

def self.react_client_manifest_file_path
  return @react_client_manifest_path if @react_client_manifest_path && !Rails.env.development?

  file_name = ReactOnRails.configuration.react_client_manifest_file
  @react_client_manifest_path = ReactOnRails::PackerUtils.asset_uri_from_packer(file_name)
end

.react_on_rails_pro?Boolean

Todo – remove this for v13, as we don’t need both boolean and number

Returns:

  • (Boolean)


232
233
234
235
236
# File 'lib/react_on_rails/utils.rb', line 232

def self.react_on_rails_pro?
  return @react_on_rails_pro if defined?(@react_on_rails_pro)

  @react_on_rails_pro = gem_available?("react_on_rails_pro")
end

.react_on_rails_pro_licence_valid?Boolean

Returns:

  • (Boolean)


249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/react_on_rails/utils.rb', line 249

def self.react_on_rails_pro_licence_valid?
  return @react_on_rails_pro_licence_valid if defined?(@react_on_rails_pro_licence_valid)

  @react_on_rails_pro_licence_valid = begin
    return false unless react_on_rails_pro?

    # Maintain compatibility with legacy versions of React on Rails Pro:
    # Earlier releases did not require license validation, as they were distributed as private gems.
    # This check ensures that the method works correctly regardless of the installed version.
    return true unless ReactOnRailsPro::Utils.respond_to?(:licence_valid?)

    ReactOnRailsPro::Utils.licence_valid?
  end
end

.react_on_rails_pro_versionObject

Return an empty string if React on Rails Pro is not installed



239
240
241
242
243
244
245
246
247
# File 'lib/react_on_rails/utils.rb', line 239

def self.react_on_rails_pro_version
  return @react_on_rails_pro_version if defined?(@react_on_rails_pro_version)

  @react_on_rails_pro_version = if react_on_rails_pro?
                                  Gem.loaded_specs["react_on_rails_pro"].version.to_s
                                else
                                  ""
                                end
end

.react_server_client_manifest_file_pathObject

React Server Manifest is generated by the server bundle. So, it will never be served from the dev server.



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/react_on_rails/utils.rb', line 165

def self.react_server_client_manifest_file_path
  return @react_server_manifest_path if @react_server_manifest_path && !Rails.env.development?

  asset_name = ReactOnRails.configuration.react_server_client_manifest_file
  if asset_name.nil?
    raise ReactOnRails::Error,
          "react_server_client_manifest_file is nil, ensure it is set in your configuration"
  end

  @react_server_manifest_path = bundle_js_file_path(asset_name)
end

.rsc_bundle_js_file_pathObject



149
150
151
152
153
154
# File 'lib/react_on_rails/utils.rb', line 149

def self.rsc_bundle_js_file_path
  return @rsc_bundle_path if @rsc_bundle_path && !Rails.env.development?

  bundle_name = ReactOnRails.configuration.rsc_bundle_js_file
  @rsc_bundle_path = bundle_js_file_path(bundle_name)
end

.rsc_support_enabled?Boolean

Returns:

  • (Boolean)


264
265
266
267
268
269
270
271
# File 'lib/react_on_rails/utils.rb', line 264

def self.rsc_support_enabled?
  return false unless react_on_rails_pro?

  return @rsc_support_enabled if defined?(@rsc_support_enabled)

  rorp_config = ReactOnRailsPro.configuration
  @rsc_support_enabled = rorp_config.respond_to?(:enable_rsc_support) && rorp_config.enable_rsc_support
end

.running_on_windows?Boolean

Returns:

  • (Boolean)


177
178
179
# File 'lib/react_on_rails/utils.rb', line 177

def self.running_on_windows?
  (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
end

.server_bundle_js_file_pathObject



142
143
144
145
146
147
# File 'lib/react_on_rails/utils.rb', line 142

def self.server_bundle_js_file_path
  return @server_bundle_path if @server_bundle_path && !Rails.env.development?

  bundle_name = ReactOnRails.configuration.server_bundle_js_file
  @server_bundle_path = bundle_js_file_path(bundle_name)
end

.server_bundle_path_is_http?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/react_on_rails/utils.rb', line 70

def self.server_bundle_path_is_http?
  server_bundle_js_file_path =~ %r{https?://}
end

.server_rendering_is_enabled?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/react_on_rails/utils.rb', line 43

def self.server_rendering_is_enabled?
  ReactOnRails.configuration.server_bundle_js_file.present?
end

.smart_trim(str, max_length = 1000) ⇒ Object



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/react_on_rails/utils.rb', line 277

def self.smart_trim(str, max_length = 1000)
  # From https://stackoverflow.com/a/831583/1009332
  str = str.to_s
  return str if full_text_errors_enabled?
  return str unless str.present? && max_length >= 1
  return str if str.length <= max_length

  return str[0, 1] + TRUNCATION_FILLER if max_length == 1

  midpoint = (str.length / 2.0).ceil
  to_remove = str.length - max_length
  lstrip = (to_remove / 2.0).ceil
  rstrip = to_remove - lstrip
  str[0..(midpoint - lstrip - 1)] + TRUNCATION_FILLER + str[(midpoint + rstrip)..]
end

.source_pathObject



201
202
203
# File 'lib/react_on_rails/utils.rb', line 201

def self.source_path
  ReactOnRails::PackerUtils.packer_source_path
end

.truthy_presence(obj) ⇒ Object



18
19
20
21
22
23
24
# File 'lib/react_on_rails/utils.rb', line 18

def self.truthy_presence(obj)
  if obj.nil? || obj == false
    nil
  else
    obj
  end
end

.using_packer_source_path_is_not_defined_and_custom_node_modules?Boolean

Returns:

  • (Boolean)


205
206
207
208
# File 'lib/react_on_rails/utils.rb', line 205

def self.using_packer_source_path_is_not_defined_and_custom_node_modules?
  !ReactOnRails::PackerUtils.packer_source_path_explicit? &&
    ReactOnRails.configuration.node_modules_location.present?
end

.wrap_message(msg, color = :red) ⇒ Object

Wraps message and makes it colored. Pass in the msg and color as a symbol.



28
29
30
31
32
33
34
35
36
37
# File 'lib/react_on_rails/utils.rb', line 28

def self.wrap_message(msg, color = :red)
  wrapper_line = ("=" * 80).to_s
  fenced_msg = <<~MSG
    #{wrapper_line}
    #{msg.strip}
    #{wrapper_line}
  MSG

  Rainbow(fenced_msg).color(color)
end