Class: Space::Core::Space

Inherits:
Object
  • Object
show all
Defined in:
lib/space_core/space.rb

Constant Summary collapse

METADATA_FILE =
"space.yaml"
VALID_STATUSES =
%w[active paused done archived].freeze
SCHEMA_VERSION =

Canonical space.yaml shape: registry under project: (renamed from the pre-2.0 architect: key). Bumped from 1 in the release that did the rename, since that release shipped no read-side alias for it.

2

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, data) ⇒ Space

Returns a new instance of Space.



78
79
80
81
# File 'lib/space_core/space.rb', line 78

def initialize(path, data)
  @path = Pathname.new(path)
  @data = data
end

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data.



17
18
19
# File 'lib/space_core/space.rb', line 17

def data
  @data
end

#pathObject (readonly)

Returns the value of attribute path.



17
18
19
# File 'lib/space_core/space.rb', line 17

def path
  @path
end

Class Method Details

.load(path) ⇒ Object

Raises:



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/space_core/space.rb', line 19

def self.load(path)
   = Pathname.new(path).join(METADATA_FILE)
  raise NotFoundError, "No space metadata found at #{}" unless .exist?

  parsed = YAML.safe_load(.read, aliases: false) || {}
  raise Error, "Space metadata must contain a YAML mapping: #{}" unless parsed.is_a?(Hash)

  data = stringify_keys(parsed)
  normalize_schema!(data, )
  new(Pathname.new(path), data)
end

.stringify_keys(hash) ⇒ Object



31
32
33
# File 'lib/space_core/space.rb', line 31

def self.stringify_keys(hash)
  hash.each_with_object({}) { |(key, value), result| result[key.to_s] = value }
end

Instance Method Details

#add_repo(reference, relative_path:, now: Time.now) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/space_core/space.rb', line 143

def add_repo(reference, relative_path:, now: Time.now)
  repo_data = repo_data_for(reference, relative_path:, now:)
  existing = repos.find do |repo|
    repo["full_name"] == repo_data["full_name"] ||
      repo["path"] == repo_data["path"] ||
      repo["name"] == repo_data["name"]
  end
  if existing
    raise RepoExistsError, "Repo '#{repo_data['full_name']}' already exists in #{id}"
  end

  data["repos"] = repos + [repo_data]
  data["updated_at"] = now.iso8601
  save
  repo_data
end

#architectObject



115
116
117
# File 'lib/space_core/space.rb', line 115

def architect
  data["project"]
end

#architect=(val) ⇒ Object



119
120
121
# File 'lib/space_core/space.rb', line 119

def architect=(val)
  data["project"] = val
end

#idObject



83
84
85
# File 'lib/space_core/space.rb', line 83

def id
  data.fetch("id")
end

#metadata_pathObject



123
124
125
# File 'lib/space_core/space.rb', line 123

def 
  path.join(METADATA_FILE)
end

#persist_pathsObject



105
106
107
# File 'lib/space_core/space.rb', line 105

def persist_paths
  Array(data.dig("pack", "persist")).map(&:to_s)
end

#provision_scriptsObject



101
102
103
# File 'lib/space_core/space.rb', line 101

def provision_scripts
  Array(data.dig("pack", "provision")).map(&:to_s)
end

#reposObject



95
96
97
98
99
# File 'lib/space_core/space.rb', line 95

def repos
  Array(data["repos"]).map do |repo|
    repo.is_a?(Hash) ? self.class.stringify_keys(repo) : { "name" => repo.to_s }
  end
end

#run_envObject

Host env vars to forward into the container at space run (bare passthrough). Declared per-space so payloads needing credentials run without a wall of flags.



111
112
113
# File 'lib/space_core/space.rb', line 111

def run_env
  Array(data.dig("run", "env")).map(&:to_s)
end

#saveObject



127
128
129
130
# File 'lib/space_core/space.rb', line 127

def save
  AtomicWrite.write(, YAML.dump(data))
  self
end

#statusObject



91
92
93
# File 'lib/space_core/space.rb', line 91

def status
  data.fetch("status", "active")
end

#titleObject



87
88
89
# File 'lib/space_core/space.rb', line 87

def title
  data.fetch("title")
end

#update_status(status, now: Time.now) ⇒ Object



132
133
134
135
136
137
138
139
140
141
# File 'lib/space_core/space.rb', line 132

def update_status(status, now: Time.now)
  normalized = status.to_s.downcase
  unless VALID_STATUSES.include?(normalized)
    raise InvalidStatusError, "Invalid status '#{status}'. Expected one of: #{VALID_STATUSES.join(', ')}"
  end

  data["status"] = normalized
  data["updated_at"] = now.iso8601
  save
end