Class: Torikago::Gateway

Inherits:
Object
  • Object
show all
Defined in:
lib/torikago/gateway.rb,
sig/torikago.rbs

Overview

Runtime entrypoint for all cross-module calls. It validates the package API manifest before dispatching into the target module container.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(registry:, configuration:, manifest_loader: nil) ⇒ Gateway

Returns a new instance of Gateway.

Parameters:



18
19
20
21
22
23
# File 'lib/torikago/gateway.rb', line 18

def initialize(registry:, configuration:, manifest_loader: nil)
  @registry = registry
  @configuration = configuration
  @manifest_loader = manifest_loader || method(:load_manifest)
  @manifests = Hash.new
end

Instance Attribute Details

#configurationConfiguration (readonly)

Returns the value of attribute configuration.

Returns:



64
65
66
# File 'lib/torikago/gateway.rb', line 64

def configuration
  @configuration
end

#manifest_loaderObject (readonly)

Returns the value of attribute manifest_loader.

Returns:

  • (Object)


64
65
66
# File 'lib/torikago/gateway.rb', line 64

def manifest_loader
  @manifest_loader
end

#manifestsHash[Symbol, Hash[String, untyped]] (readonly)

Returns the value of attribute manifests.

Returns:

  • (Hash[Symbol, Hash[String, untyped]])


64
65
66
# File 'lib/torikago/gateway.rb', line 64

def manifests
  @manifests
end

#registryRegistry (readonly)

Returns the value of attribute registry.

Returns:



64
65
66
# File 'lib/torikago/gateway.rb', line 64

def registry
  @registry
end

Class Method Details

.buildInvocation

Parameters:

  • public_api_class_name (String)
  • (Object)
  • (Object)

Returns:



9
10
11
# File 'lib/torikago/gateway.rb', line 9

def build(...)
  Torikago.gateway.build(...)
end

.invokeObject

Parameters:

  • public_api_class_name (String)
  • method_name (Symbol, String)
  • (Object)
  • (Object)

Returns:

  • (Object)


13
14
15
# File 'lib/torikago/gateway.rb', line 13

def invoke(...)
  Torikago.gateway.invoke(...)
end

Instance Method Details

#allowed_callers(public_api_entry) ⇒ Array[untyped]

Parameters:

  • public_api_entry (Object)

Returns:

  • (Array[untyped])


138
139
140
141
142
143
144
145
# File 'lib/torikago/gateway.rb', line 138

def allowed_callers(public_api_entry)
  return Array.new unless public_api_entry.is_a?(Hash)

  allowed = public_api_entry["allowed_callers"]
  return allowed if allowed.is_a?(Array)

  Array.new
end

#build(public_api_class_name, *constructor_args, **constructor_kwargs) ⇒ Invocation

Parameters:

  • public_api_class_name (String)
  • (Object)
  • (Object)

Returns:



25
26
27
28
29
30
31
32
# File 'lib/torikago/gateway.rb', line 25

def build(public_api_class_name, *constructor_args, **constructor_kwargs)
  Invocation.new(
    gateway: self,
    public_api_class_name: public_api_class_name,
    constructor_args: constructor_args,
    constructor_kwargs: constructor_kwargs
  )
end

#dependency_allowed?(public_api_entry, caller_name) ⇒ Boolean

Parameters:

  • public_api_entry (Object)
  • caller_name (Symbol)

Returns:

  • (Boolean)


125
126
127
# File 'lib/torikago/gateway.rb', line 125

def dependency_allowed?(public_api_entry, caller_name)
  allowed_callers(public_api_entry).map { |caller| caller.to_sym }.include?(caller_name)
end

#dispatch(public_api_class_name:, method_name:, constructor_args:, constructor_kwargs:, method_args:, method_kwargs:) ⇒ Object

Internal interface used by Invocation. Validation deliberately precedes Registry resolution so rejected calls cannot boot the target Box.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/torikago/gateway.rb', line 47

def dispatch(public_api_class_name:, method_name:, constructor_args:, constructor_kwargs:, method_args:, method_kwargs:)
  target_module = infer_target_module(public_api_class_name)
  caller_module = CurrentExecution.current_box

  validate_public_api!(target_module, public_api_class_name, method_name, caller_module)
  registry.resolve(target_module).invoke(
    public_api_class_name,
    method_name,
    constructor_args: constructor_args,
    constructor_kwargs: constructor_kwargs,
    method_args: method_args,
    method_kwargs: method_kwargs
  )
end

#exported_methods(public_api_entry) ⇒ Array[String]

Parameters:

  • public_api_entry (Object)

Returns:

  • (Array[String])


129
130
131
132
133
134
135
136
# File 'lib/torikago/gateway.rb', line 129

def exported_methods(public_api_entry)
  return Array.new unless public_api_entry.is_a?(Hash)

  methods = public_api_entry["methods"]
  return methods.map(&:to_s) if methods.is_a?(Array)

  Array.new
end

#exported_package_apis(manifest) ⇒ Hash[String, untyped]

Parameters:

  • manifest (Hash[String, untyped])

Returns:

  • (Hash[String, untyped])


121
122
123
# File 'lib/torikago/gateway.rb', line 121

def exported_package_apis(manifest)
  manifest.fetch("exports") { manifest.fetch("public_api", Hash.new) }
end

#infer_target_module(public_api_class_name) ⇒ Symbol

Parameters:

  • public_api_class_name (String)

Returns:

  • (Symbol)


117
118
119
# File 'lib/torikago/gateway.rb', line 117

def infer_target_module(public_api_class_name)
  public_api_class_name.split("::").first.downcase.to_sym
end

#invoke(public_api_class_name, method_name, *method_args, **method_kwargs) ⇒ Object

Parameters:

  • public_api_class_name (String)
  • method_name (Symbol, String)
  • (Object)
  • (Object)

Returns:

  • (Object)


34
35
36
37
38
39
40
41
42
43
# File 'lib/torikago/gateway.rb', line 34

def invoke(public_api_class_name, method_name, *method_args, **method_kwargs)
  dispatch(
    public_api_class_name: public_api_class_name,
    method_name: method_name,
    constructor_args: Array.new,
    constructor_kwargs: Hash.new,
    method_args: method_args,
    method_kwargs: method_kwargs
  )
end

#load_manifest(definition) ⇒ Hash[String, untyped]

Parameters:

Returns:

  • (Hash[String, untyped])


102
103
104
105
106
107
108
109
110
111
# File 'lib/torikago/gateway.rb', line 102

def load_manifest(definition)
  manifest_path = package_api_manifest_path(definition)

  unless manifest_path.exist?
    raise DependencyError,
          "package_api manifest not found for #{definition.name}: #{manifest_path}"
  end

  YAML.safe_load(manifest_path.read, permitted_classes: Array.new, aliases: false) || Hash.new
end

#package_api_manifest_path(definition) ⇒ Pathname

Parameters:

Returns:

  • (Pathname)


113
114
115
# File 'lib/torikago/gateway.rb', line 113

def package_api_manifest_path(definition)
  Pathname(definition.root).join("package_api.yml")
end

#validate_public_api!(target_module, public_api_class_name, method_name, caller_module) ⇒ nil

Parameters:

  • target_module (Symbol)
  • public_api_class_name (String)
  • method_name (Symbol, String)
  • caller_module (Symbol, nil)

Returns:

  • (nil)

Raises:



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/torikago/gateway.rb', line 66

def validate_public_api!(target_module, public_api_class_name, method_name, caller_module)
  target_name = target_module.to_sym
  definition = configuration.fetch(target_name)
  manifest = manifests.fetch(target_name) do
    manifests[target_name] = manifest_loader.call(definition)
  end

  public_api_entry = exported_package_apis(manifest).fetch(public_api_class_name, nil)
  if public_api_entry.nil?
    raise PublicApiError,
          "package api export not declared for #{target_name}: #{public_api_class_name}##{method_name}"
  end

  methods = exported_methods(public_api_entry)
  if methods.empty?
    raise PublicApiError,
          "package api methods are not configured: #{public_api_class_name}##{method_name}"
  end

  unless methods.include?(method_name.to_s)
    raise PublicApiError,
          "package api method is not exported: #{public_api_class_name}##{method_name}"
  end

  return if caller_module.nil?

  caller_name = caller_module.to_sym
  # A module may always call its own public API; allowed_callers only governs
  # calls crossing from one module box into another.
  return if caller_name == target_name
  return if dependency_allowed?(public_api_entry, caller_name)

  raise DependencyError,
        "module dependency not allowed: #{caller_name} -> #{target_name}##{public_api_class_name}##{method_name}"
end