Class: ReactOnRailsPro::RendererArtifact

Inherits:
Object
  • Object
show all
Defined in:
lib/react_on_rails_pro/renderer_artifact.rb

Overview

Immutable description of one renderer bundle and the flat companion files that must be staged beside it. The ID is safe to use as a cache directory name and changes whenever any runtime-visible byte or destination changes.

Defined Under Namespace

Classes: Identity, InlineCompanion

Constant Summary collapse

ID_PREFIX =
"rorp-v2"
ROLE_CODES =
{ server: "s", rsc: "r" }.freeze
ID_PATTERN =
/\Arorp-v2-[sr]-[0-9a-f]{64}\z/
SAFE_COMPANION_NAME_PATTERN =
%r{\A(?!\.{1,2}\z)[^/\\:\x00-\x1f\x7f]+\z}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(role:, bundle:, companions:, bundle_body: nil, companion_bodies: nil) ⇒ RendererArtifact

Returns a new instance of RendererArtifact.



57
58
59
60
61
62
63
64
65
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 57

def initialize(role:, bundle:, companions:, bundle_body: nil, companion_bodies: nil)
  @role = role.to_sym
  @bundle = Pathname.new(bundle.to_s)
  @bundle_body = capture_body(bundle_body || File.binread(@bundle))
  @companions = normalize_companions(companions)
  @companion_bodies = capture_companion_bodies(companion_bodies)
  @id = build_id.freeze
  freeze
end

Instance Attribute Details

#bundleObject (readonly)

Returns the value of attribute bundle.



55
56
57
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 55

def bundle
  @bundle
end

#bundle_bodyObject (readonly)

Returns the value of attribute bundle_body.



55
56
57
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 55

def bundle_body
  @bundle_body
end

#companion_bodiesObject (readonly)

Returns the value of attribute companion_bodies.



55
56
57
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 55

def companion_bodies
  @companion_bodies
end

#companionsObject (readonly)

Returns the value of attribute companions.



55
56
57
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 55

def companions
  @companions
end

#idObject (readonly)

Returns the value of attribute id.



55
56
57
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 55

def id
  @id
end

#roleObject (readonly)

Returns the value of attribute role.



55
56
57
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 55

def role
  @role
end

Class Method Details

.role_from_id(value) ⇒ Object



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

def self.role_from_id(value)
  match = ID_PATTERN.match(value.to_s)
  return nil unless match

  ROLE_CODES.key(value.to_s.delete_prefix("#{ID_PREFIX}-").slice(0))
end

.safe_companion_name?(value) ⇒ Boolean

Returns:

  • (Boolean)


78
79
80
81
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 78

def self.safe_companion_name?(value)
  name = value.to_s.dup.force_encoding(Encoding::UTF_8)
  name.valid_encoding? && SAFE_COMPANION_NAME_PATTERN.match?(name)
end

.versioned_id?(value) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 67

def self.versioned_id?(value)
  ID_PATTERN.match?(value.to_s)
end

Instance Method Details

#with_materialized_files(bundle_name: File.basename(bundle.to_s)) ⇒ Object

Materializes only this value object's captured bytes and removes them as soon as the synchronous consumer returns. This lets path-based adapter APIs and tarball composition use the exact bytes bound into id without retaining large snapshots in a process-wide cache.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/react_on_rails_pro/renderer_artifact.rb', line 87

def with_materialized_files(bundle_name: File.basename(bundle.to_s))
  bundle_name = bundle_name.to_s
  conflicting_name = companion_bodies.each_key.find { |basename| basename.casecmp?(bundle_name) }
  if conflicting_name
    raise ArgumentError,
          "Renderer companion name #{conflicting_name.inspect} conflicts with bundle filename " \
          "#{bundle_name.inspect}"
  end

  Dir.mktmpdir("rorp-artifact-") do |directory|
    bundle_path = File.join(directory, bundle_name)
    File.binwrite(bundle_path, bundle_body)
    materialized_companions = companion_bodies.to_h do |basename, body|
      path = File.join(directory, basename)
      File.binwrite(path, body)
      [basename, Pathname.new(path)]
    end.freeze
    yield Pathname.new(bundle_path), materialized_companions
  end
end