Module: ReactOnRails::Utils

Defined in:
lib/react_on_rails/utils.rb

Overview

rubocop:disable Metrics/ModuleLength

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



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/react_on_rails/utils.rb', line 71

def self.bundle_js_file_path(bundle_name)
  # Either:
  # 1. Using same bundle for both server and client, so server bundle will be hashed in manifest
  # 2. Using a different bundle (different Webpack config), so file is not hashed, and
  #    bundle_js_path will throw so the default path is used without a hash.
  # 3. The third option of having the server bundle hashed and a different configuration than
  #    the client bundle is not supported for 2 reasons:
  #    a. The webpack manifest plugin would have a race condition where the same manifest.json
  #       is edited by both the webpack-dev-server
  #    b. There is no good reason to hash the server bundle name.
  if ReactOnRails::PackerUtils.using_packer? && bundle_name != "manifest.json"
    begin
      ReactOnRails::PackerUtils.bundle_js_uri_from_packer(bundle_name)
    rescue Object.const_get(
      ReactOnRails::PackerUtils.packer_type.capitalize
    )::Manifest::MissingEntryError
      File.expand_path(
        File.join(ReactOnRails::PackerUtils.packer_public_output_path,
                  bundle_name)
      )
    end
  else
    # 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(generated_assets_full_path, bundle_name)
  end
end

.find_most_recent_mtime(files) ⇒ Object



260
261
262
263
264
265
# File 'lib/react_on_rails/utils.rb', line 260

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)


240
241
242
# File 'lib/react_on_rails/utils.rb', line 240

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

.gem_available?(name) ⇒ Boolean

Returns:

  • (Boolean)


186
187
188
189
190
191
192
193
194
195
196
# File 'lib/react_on_rails/utils.rb', line 186

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



178
179
180
181
182
183
184
# File 'lib/react_on_rails/utils.rb', line 178

def self.generated_assets_full_path
  if ReactOnRails::PackerUtils.using_packer?
    ReactOnRails::PackerUtils.packer_public_output_path
  else
    File.expand_path(ReactOnRails.configuration.generated_assets_dir)
  end
end

.invoke_and_exit_if_failed(cmd, failure_message) ⇒ Object

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



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

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)

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

.object_to_boolean(value) ⇒ Object



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

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



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

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



267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/react_on_rails/utils.rb', line 267

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

.rails_version_less_than(version) ⇒ Object



143
144
145
146
147
148
149
150
151
# File 'lib/react_on_rails/utils.rb', line 143

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



114
115
116
117
118
119
120
121
122
123
# File 'lib/react_on_rails/utils.rb', line 114

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 = if ReactOnRails::PackerUtils.using_packer?
                                  ReactOnRails::PackerUtils.asset_uri_from_packer(file_name)
                                else
                                  File.join(generated_assets_full_path, file_name)
                                end
end

.react_on_rails_pro?Boolean

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

Returns:

  • (Boolean)


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

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)


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

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



206
207
208
209
210
211
212
213
214
# File 'lib/react_on_rails/utils.rb', line 206

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.



127
128
129
130
131
132
133
134
135
136
137
# File 'lib/react_on_rails/utils.rb', line 127

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(generated_assets_full_path, asset_name)
end

.rsc_bundle_js_file_pathObject



107
108
109
110
111
112
# File 'lib/react_on_rails/utils.rb', line 107

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)


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

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)


139
140
141
# File 'lib/react_on_rails/utils.rb', line 139

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

.server_bundle_js_file_pathObject



100
101
102
103
104
105
# File 'lib/react_on_rails/utils.rb', line 100

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)


67
68
69
# File 'lib/react_on_rails/utils.rb', line 67

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

.server_rendering_is_enabled?Boolean

Returns:

  • (Boolean)


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

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

.smart_trim(str, max_length = 1000) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/react_on_rails/utils.rb', line 244

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



163
164
165
166
167
168
169
# File 'lib/react_on_rails/utils.rb', line 163

def self.source_path
  if ReactOnRails::PackerUtils.using_packer?
    ReactOnRails::PackerUtils.packer_source_path
  else
    ReactOnRails.configuration.node_modules_location
  end
end

.truthy_presence(obj) ⇒ Object



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

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)


171
172
173
174
175
176
# File 'lib/react_on_rails/utils.rb', line 171

def self.using_packer_source_path_is_not_defined_and_custom_node_modules?
  return false unless ReactOnRails::PackerUtils.using_packer?

  !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.



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

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