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.



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

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.



85
86
87
88
89
90
91
92
93
# File 'lib/react_on_rails_pro/utils.rb', line 85

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)


137
138
139
# File 'lib/react_on_rails_pro/utils.rb', line 137

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



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/react_on_rails_pro/utils.rb', line 123

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



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

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)


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

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



56
57
58
59
60
# File 'lib/react_on_rails_pro/utils.rb', line 56

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



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/react_on_rails_pro/utils.rb', line 63

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



228
229
230
# File 'lib/react_on_rails_pro/utils.rb', line 228

def self.license_info
  ReactOnRailsPro::LicenseValidator.license_info
end

.license_statusSymbol

Returns the current license status

Returns:

  • (Symbol)

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



52
53
54
# File 'lib/react_on_rails_pro/utils.rb', line 52

def self.license_status
  LicenseValidator.license_status
end

.mine_type_from_file_name(filename) ⇒ Object



180
181
182
183
# File 'lib/react_on_rails_pro/utils.rb', line 180

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



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

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)



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/react_on_rails_pro/utils.rb', line 203

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



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

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.



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/react_on_rails_pro/utils.rb', line 34

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



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

def self.resolve_renderer_cache_dir
  ReactOnRailsPro::RendererCachePath.resolve
end

.rorp_puts(message) ⇒ Object

PUBLIC API



11
12
13
# File 'lib/react_on_rails_pro/utils.rb', line 11

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

.rsc_bundle_hashObject



95
96
97
98
99
100
101
102
103
# File 'lib/react_on_rails_pro/utils.rb', line 95

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



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

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)


46
47
48
# File 'lib/react_on_rails_pro/utils.rb', line 46

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.



116
117
118
119
120
121
# File 'lib/react_on_rails_pro/utils.rb', line 116

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



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/react_on_rails_pro/utils.rb', line 147

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