Module: ReactOnRailsPro::Utils

Defined in:
lib/react_on_rails_pro/utils.rb

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.



119
120
121
122
123
124
125
# File 'lib/react_on_rails_pro/utils.rb', line 119

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 string which should be used as a component in any cache key for react_component or react_component_hash when server rendering. This value is either the server bundle filename with the hash from webpack or an MD5 digest of the entire bundle.



98
99
100
101
102
103
104
105
106
# File 'lib/react_on_rails_pro/utils.rb', line 98

def self.bundle_hash
  return @bundle_hash if @bundle_hash && !(Rails.env.development? || Rails.env.test?)

  server_bundle_js_file_path = ReactOnRails::Utils.server_bundle_js_file_path

  return @bundle_hash if @bundle_hash && bundle_mtime_same?(server_bundle_js_file_path)

  @bundle_hash = calc_bundle_hash(server_bundle_js_file_path)
end

.bundle_mtime_same?(server_bundle_js_file_path) ⇒ Boolean

Returns:

  • (Boolean)


150
151
152
# File 'lib/react_on_rails_pro/utils.rb', line 150

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



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/react_on_rails_pro/utils.rb', line 136

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_dataObject



174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/react_on_rails_pro/utils.rb', line 174

def self.common_form_data
  dependencies = if ReactOnRailsPro.configuration.enable_rsc_support
                   pool = ReactOnRailsPro::ServerRenderingPool::NodeRenderingPool
                   [pool.rsc_bundle_hash, pool.server_bundle_hash]
                 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)


154
155
156
157
158
# File 'lib/react_on_rails_pro/utils.rb', line 154

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



69
70
71
72
73
# File 'lib/react_on_rails_pro/utils.rb', line 69

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



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

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



241
242
243
# File 'lib/react_on_rails_pro/utils.rb', line 241

def self.license_info
  ReactOnRailsPro::LicenseValidator.license_info
end

.license_statusSymbol

Returns the current license status

Returns:

  • (Symbol)

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



65
66
67
# File 'lib/react_on_rails_pro/utils.rb', line 65

def self.license_status
  LicenseValidator.license_status
end

.mine_type_from_file_name(filename) ⇒ Object



193
194
195
196
# File 'lib/react_on_rails_pro/utils.rb', line 193

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



199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/react_on_rails_pro/utils.rb', line 199

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 React on Rails helper to generate license-specific attribution Includes organization name when available (plan is only shown in server logs for privacy)



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/react_on_rails_pro/utils.rb', line 216

def self.pro_attribution_comment
  base = "Powered by React on Rails Pro (c) ShakaCode"
  org = ReactOnRailsPro::LicenseValidator.license_organization

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

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

.react_client_manifest_file_pathObject



38
39
40
41
42
43
# File 'lib/react_on_rails_pro/utils.rb', line 38

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.



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/react_on_rails_pro/utils.rb', line 47

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

.resolve_renderer_cache_dirObject



189
190
191
# File 'lib/react_on_rails_pro/utils.rb', line 189

def self.resolve_renderer_cache_dir
  ReactOnRailsPro::RendererCachePath.resolve
end

.rorp_puts(message) ⇒ Object

PUBLIC API



24
25
26
# File 'lib/react_on_rails_pro/utils.rb', line 24

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

.rsc_bundle_hashObject



108
109
110
111
112
113
114
115
116
# File 'lib/react_on_rails_pro/utils.rb', line 108

def self.rsc_bundle_hash
  return @rsc_bundle_hash if @rsc_bundle_hash && !(Rails.env.development? || Rails.env.test?)

  server_rsc_bundle_js_file_path = rsc_bundle_js_file_path

  return @rsc_bundle_hash if @rsc_bundle_hash && bundle_mtime_same?(server_rsc_bundle_js_file_path)

  @rsc_bundle_hash = calc_bundle_hash(server_rsc_bundle_js_file_path)
end

.rsc_bundle_js_file_pathObject

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



31
32
33
34
35
36
# File 'lib/react_on_rails_pro/utils.rb', line 31

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)


59
60
61
# File 'lib/react_on_rails_pro/utils.rb', line 59

def self.rsc_support_enabled?
  ReactOnRailsPro.configuration.enable_rsc_support
end

.server_bundle_file_nameObject

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



129
130
131
132
133
134
# File 'lib/react_on_rails_pro/utils.rb', line 129

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



160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/react_on_rails_pro/utils.rb', line 160

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