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



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

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



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

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)


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

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

.gem_available?(name) ⇒ Boolean

Returns:

  • (Boolean)


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

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



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

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



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

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



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

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



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

def self.public_bundles_full_path
  ReactOnRails::PackerUtils.packer_public_output_path
end

.rails_version_less_than(version) ⇒ Object



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

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



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

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)


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

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)


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

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



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

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.



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

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 = File.join(public_bundles_full_path, asset_name)
end

.rsc_bundle_js_file_pathObject



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

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)


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

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)


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

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

.server_bundle_js_file_pathObject



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

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



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

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



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

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)


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

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