Module: ReactOnRailsPro::Utils

Defined in:
lib/react_on_rails_pro/utils.rb,
sig/react_on_rails_pro/utils.rbs

Overview

rubocop:disable Metrics/ModuleLength

Constant Summary collapse

ARTIFACT_ID_MUTEX =
Mutex.new

Class Method Summary collapse

Class Method Details

.bundle_file_name(bundle_name) ⇒ Object

Returns the hashed file name when using Shakapacker. Useful for creating cache keys.



174
175
176
177
178
179
180
# File 'lib/react_on_rails_pro/utils.rb', line 174

def self.bundle_file_name(bundle_name)
  # bundle_js_uri_from_packer can return a file path or a HTTP URL (for files served from the dev server)
  # Pathname can handle both cases
  full_path = ReactOnRails::PackerUtils.bundle_js_uri_from_packer(bundle_name)
  pathname = Pathname.new(full_path)
  pathname.basename.to_s
end

.bundle_hashObject

Returns a versioned ID for the complete renderer artifact: bundle bytes, artifact role, and the final flat companion-file mapping. Keep the legacy method names because they are part of the Ruby/Node wire contract; the values remain opaque safe directory names to the Node renderer.



101
102
103
# File 'lib/react_on_rails_pro/utils.rb', line 101

def self.bundle_hash
  artifact_id(:server)
end

.bundle_js_file_pathString

Parameters:

  • bundle_name (String)

Returns:

  • (String)


16
# File 'sig/react_on_rails_pro/utils.rbs', line 16

def self.bundle_js_file_path: (String bundle_name) -> String

.bundle_mtime_same?(server_bundle_js_file_path) ⇒ Boolean

Returns:

  • (Boolean)


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

def self.bundle_mtime_same?(server_bundle_js_file_path)
  @test_dev_server_bundle_mtime == File.mtime(server_bundle_js_file_path)
end

.calc_bundle_hash(server_bundle_js_file_path) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/react_on_rails_pro/utils.rb', line 191

def self.calc_bundle_hash(server_bundle_js_file_path)
  if Rails.env.development? || Rails.env.test?
    @test_dev_server_bundle_mtime = File.mtime(server_bundle_js_file_path)
  end

  server_bundle_basename = Pathname.new(server_bundle_js_file_path).basename.to_s

  if contains_hash?(server_bundle_basename)
    server_bundle_basename
  else
    "#{Digest::MD5.file(server_bundle_js_file_path)}-#{Rails.env}"
  end
end

.common_form_data(artifacts: nil) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/react_on_rails_pro/utils.rb', line 229

def self.common_form_data(artifacts: nil)
  dependencies = if ReactOnRailsPro.configuration.enable_rsc_support
                   if artifacts
                     ids_by_role = artifacts.to_h { |artifact| [artifact.role, artifact.id] }
                     [ids_by_role.fetch(:rsc), ids_by_role.fetch(:server)]
                   else
                     pool = ReactOnRailsPro::ServerRenderingPool::NodeRenderingPool
                     [pool.rsc_bundle_hash, pool.server_bundle_hash]
                   end
                 end

  {
    "gemVersion" => ReactOnRailsPro::VERSION,
    "protocolVersion" => ReactOnRailsPro::PROTOCOL_VERSION,
    "password" => ReactOnRailsPro.configuration.renderer_password,
    "dependencyBundleTimestamps" => dependencies,
    "railsEnv" => Rails.env.to_s
  }
end

.contains_hash?(server_bundle_basename) ⇒ Boolean

Returns:

  • (Boolean)


209
210
211
212
213
# File 'lib/react_on_rails_pro/utils.rb', line 209

def self.contains_hash?(server_bundle_basename)
  # TODO: Need to consider if the configuration value has the ".js" on the end.
  ReactOnRails.configuration.server_bundle_js_file != server_bundle_basename &&
    ReactOnRailsPro.configuration.rsc_bundle_js_file != server_bundle_basename
end

.copy_assetsObject



72
73
74
75
76
# File 'lib/react_on_rails_pro/utils.rb', line 72

def self.copy_assets
  return if ReactOnRailsPro.configuration.assets_to_copy.blank?

  ReactOnRailsPro::Request.upload_assets
end

.digest_of_globs(globs) ⇒ Object

takes an array of globs, removes excluded_dependency_globs & returns a digest



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/react_on_rails_pro/utils.rb', line 79

def self.digest_of_globs(globs)
  # NOTE: Dir.glob is not stable between machines, even with same OS. So we must sort.
  # .uniq was added to remove redundancies in the case digest_of_globs is used on a union of
  # dependency_globs & source code in order to create a cache key for production bundles
  # We've tested it to make sure that it adds less than a second even in the case of thousands of files
  files = Dir.glob(globs).uniq
  excluded_dependency_globs = ReactOnRailsPro.configuration.excluded_dependency_globs
  if excluded_dependency_globs.present?
    excluded_files = Dir.glob(excluded_dependency_globs).uniq
    files -= excluded_files
  end
  files.sort!

  digest = Digest::MD5.new
  files.each { |f| digest.file(f) unless File.directory?(f) }
  digest
end

.license_infoHash

Returns license information for use in helpers and components Delegates to LicenseValidator.license_info

Returns:

  • (Hash)

    License info including org, plan, status, and attribution_required



296
297
298
# File 'lib/react_on_rails_pro/utils.rb', line 296

def self.license_info
  ReactOnRailsPro::LicenseValidator.license_info
end

.license_statusSymbol

Returns the current license status

Returns:

  • (Symbol)

    One of :valid, :expired, :invalid, :missing



68
69
70
# File 'lib/react_on_rails_pro/utils.rb', line 68

def self.license_status
  LicenseValidator.license_status
end

.mine_type_from_file_name(filename) ⇒ Object



253
254
255
256
# File 'lib/react_on_rails_pro/utils.rb', line 253

def self.mine_type_from_file_name(filename)
  extension = File.extname(filename)
  Rack::Mime.mime_type(extension)
end

.printable_cache_key(cache_key) ⇒ Object

TODO: write test



259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/react_on_rails_pro/utils.rb', line 259

def self.printable_cache_key(cache_key)
  cache_key.map do |key|
    if key.is_a?(Enumerable)
      printable_cache_key(key)
    elsif key.respond_to?(:cache_key_with_version)
      key.cache_key_with_version
    elsif key.respond_to?(:cache_key)
      key.cache_key
    else
      key.to_s
    end
  end.join("_").underscore
end

.pro_attribution_commentObject

Generates the Pro-specific HTML attribution comment based on license status. Called by the React on Rails helper as the product-level attribution provider. Organization and plan details remain available only in private server diagnostics.



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

def self.pro_attribution_comment
  base = "Powered by React on Rails Pro (c) ShakaCode"

  comment = case ReactOnRailsPro::LicenseValidator.license_status
            when :valid
              "#{base} | Licensed"
            when :expired
              "#{base} | LICENSE EXPIRED"
            when :invalid
              "#{base} | INVALID LICENSE"
            when :missing
              "#{base} | UNLICENSED"
            end

  "<!-- #{comment} -->"
end

.react_client_manifest_file_pathObject



41
42
43
44
45
46
# File 'lib/react_on_rails_pro/utils.rb', line 41

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

  file_name = ReactOnRailsPro.configuration.react_client_manifest_file
  @react_client_manifest_path = ReactOnRails::PackerUtils.asset_uri_from_packer(file_name)
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.



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/react_on_rails_pro/utils.rb', line 50

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

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

  @react_server_manifest_path = ReactOnRails::Utils.bundle_js_file_path(asset_name)
end

.renderer_artifacts(action_description: "computing current artifact identity", roles: nil) ⇒ Object



109
110
111
# File 'lib/react_on_rails_pro/utils.rb', line 109

def self.renderer_artifacts(action_description: "computing current artifact identity", roles: nil)
  RendererCacheHelpers.build_current_artifacts(action_description:, roles:)
end

.resolve_renderer_cache_dirObject



249
250
251
# File 'lib/react_on_rails_pro/utils.rb', line 249

def self.resolve_renderer_cache_dir
  ReactOnRailsPro::RendererCachePath.resolve
end

.rorp_puts(message) ⇒ Object

PUBLIC API



27
28
29
# File 'lib/react_on_rails_pro/utils.rb', line 27

def self.rorp_puts(message)
  puts "[ReactOnRailsPro] #{message}"
end

.rsc_bundle_hashObject



105
106
107
# File 'lib/react_on_rails_pro/utils.rb', line 105

def self.rsc_bundle_hash
  artifact_id(:rsc)
end

.rsc_bundle_js_file_pathObject

RSC Configuration Utility Methods These methods were moved from ReactOnRails::Utils as they are Pro-only features



34
35
36
37
38
39
# File 'lib/react_on_rails_pro/utils.rb', line 34

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

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

.rsc_support_enabled?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/react_on_rails_pro/utils.rb', line 62

def self.rsc_support_enabled?
  ReactOnRailsPro.configuration.enable_rsc_support
end

.running_on_windows?Boolean

Returns:

  • (Boolean)


18
# File 'sig/react_on_rails_pro/utils.rbs', line 18

def self.running_on_windows?: () -> bool

.server_bundle_file_nameObject

Returns the hashed file name of the server bundle when using Shakapacker. Necessary for fragment-caching keys.



184
185
186
187
188
189
# File 'lib/react_on_rails_pro/utils.rb', line 184

def self.server_bundle_file_name
  return @server_bundle_hash if @server_bundle_hash && !Rails.env.development?

  server_bundle_name = ReactOnRails.configuration.server_bundle_js_file
  @server_bundle_hash = bundle_file_name(server_bundle_name)
end

.with_trace(message = nil) ⇒ Object



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

def self.with_trace(message = nil)
  return yield unless ReactOnRailsPro.configuration.tracing && Rails.logger.info?

  start = Time.current
  result = yield
  finish = Time.current

  caller_method = caller(1..1).first[/[`'][^']*'/][1..-2]
  timing = "#{((finish - start) * 1_000).round(1)}ms"
  Rails.logger.info "[ReactOnRailsPro] PID:#{Process.pid} #{caller_method}: #{[message, timing].compact.join(', ')}"

  result
end