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.



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

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.



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

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)


145
146
147
# File 'lib/react_on_rails_pro/utils.rb', line 145

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



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

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



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

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)


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

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



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

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



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

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

.mine_type_from_file_name(filename) ⇒ Object



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

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



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

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



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

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

  # Check if in grace period
  grace_days = ReactOnRailsPro::LicenseValidator.grace_days_remaining
  comment = if grace_days
              "#{base} | Licensed (Expired - Grace Period: #{grace_days} day(s) remaining)"
            elsif ReactOnRailsPro::LicenseValidator.evaluation?
              "#{base} | Evaluation License"
            else
              "#{base} | Licensed"
            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



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

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.



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

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

.validated_license_data!Boolean

Validates the license and raises an exception if invalid.

Returns:

  • (Boolean)

    true if license is valid

Raises:



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

def self.validated_license_data!
  LicenseValidator.validated_license_data!
end

.with_trace(message = nil) ⇒ Object



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

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