Class: Shojiku::TemplateRoot

Inherits:
Object
  • Object
show all
Defined in:
lib/shojiku/template_root.rb

Overview

Resolving a template NAME to the sources behind it.

A name is an identifier, never a path. A bundle format will take this lookup over later, so nothing outside this class may assume a directory is how names resolve — callers ask for "receipt_ja" and get sources back.

The rejection rules are the union across platforms, not the host's. Windows is a first-class target (it is what the .NET SDK's market runs on), so a backslash is a separator, C:name is drive-relative, \\host\share is a UNC path and CON/NUL are reserved devices — every one of them refused on EVERY platform. A template name that is valid on one machine is valid on all of them, which is the only way the same application deploys to both.

Defined Under Namespace

Classes: Rejected

Constant Summary collapse

DEVICES =

Reserved DOS device names. Windows resolves these no matter what directory you are in and no matter what extension you append.

(%w[CON PRN AUX NUL] +
(1..9).flat_map { |n| ["COM#{n}", "LPT#{n}"] }).freeze
SEPARATORS =

A name is ONE segment. Refusing both separators outright subsumes traversal, absolute paths and nested lookups in a single rule — the simplest thing six other SDKs can mirror without drifting.

%r{[/\\]}
DRIVE_RELATIVE =
/\A[A-Za-z]:/
CONTROL =
/[\x00-\x1f\x7f]/
TEMPLATE_FILE =
"templates.yml"
DEFINITIONS_FILE =
"definitions.yml"
RULES =

Each rule, and what a caller is told when it fires. The keys are the predicate names below, so adding a rule is one entry plus one method.

{
  "separator" => "a name is one segment, so `/` and `\\` are never part of it " \
                 "(which is also what makes `..` traversal impossible)",
  "control" => "it contains a control character",
  "drive_relative" => "it is drive-relative, which Windows resolves against " \
                      "that drive's current directory",
  "device" => "it is a reserved device name on Windows"
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ TemplateRoot

Returns a new instance of TemplateRoot.



46
47
48
# File 'lib/shojiku/template_root.rb', line 46

def initialize(path)
  @path = path
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



44
45
46
# File 'lib/shojiku/template_root.rb', line 44

def path
  @path
end

Instance Method Details

#resolve(name) ⇒ Object

Resolves name, or raises Rejected naming why it will not.

Rejection is an exception INSIDE this class and a failed Result outside it (see Client#generate) — a hostile template name is a fact about the request, not a bug in the calling program.



55
56
57
58
59
60
61
62
63
64
65
# File 'lib/shojiku/template_root.rb', line 55

def resolve(name)
  identifier!(name)
  reject!(name)
  dir = File.join(@path, name)
  real = contained!(dir)
  Sources.new(
    template: read!(File.join(real, TEMPLATE_FILE)),
    definitions: optional(File.join(real, DEFINITIONS_FILE)),
    assets_dir: real
  )
end