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.



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

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.



89
90
91
92
93
94
95
96
97
# File 'lib/react_on_rails_pro/utils.rb', line 89

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)


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

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



129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/react_on_rails_pro/utils.rb', line 129

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



167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/react_on_rails_pro/utils.rb', line 167

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)


147
148
149
150
151
# File 'lib/react_on_rails_pro/utils.rb', line 147

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



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

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



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/react_on_rails_pro/utils.rb', line 67

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



230
231
232
# File 'lib/react_on_rails_pro/utils.rb', line 230

def self.license_info
  ReactOnRailsPro::LicenseValidator.license_info
end

.license_statusSymbol

Returns the current license status

Returns:

  • (Symbol)

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



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

def self.license_status
  LicenseValidator.license_status
end

.mine_type_from_file_name(filename) ⇒ Object



182
183
184
185
# File 'lib/react_on_rails_pro/utils.rb', line 182

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



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

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)



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

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



29
30
31
32
33
34
# File 'lib/react_on_rails_pro/utils.rb', line 29

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.



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/react_on_rails_pro/utils.rb', line 38

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

.rorp_puts(message) ⇒ Object

PUBLIC API



15
16
17
# File 'lib/react_on_rails_pro/utils.rb', line 15

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

.rsc_bundle_hashObject



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

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



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

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)


50
51
52
# File 'lib/react_on_rails_pro/utils.rb', line 50

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.



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

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

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

.with_trace(message = nil) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/react_on_rails_pro/utils.rb', line 153

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