Class: RepoTender::State::Store
- Inherits:
-
Object
- Object
- RepoTender::State::Store
show all
- Defined in:
- lib/repo_tender/state/store.rb
Overview
Machine-managed state at $XDG_STATE_HOME/repo-tender/state.yaml. Never hand-edited (per PRD ยง3.2). Per-repo + per-org records with a fixed status enum; the store validates the enum and timestamp format on write.
Defined Under Namespace
Classes: Org, Repo, State
Constant Summary
collapse
- STATUSES =
%w[clean dirty diverged detached wrong_branch missing error].freeze
Class Method Summary
collapse
Class Method Details
.build_state(raw) ⇒ Object
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/repo_tender/state/store.rb', line 104
def self.build_state(raw)
repos = (raw["repos"] || {}).each_with_object({}) do |(key, attrs), acc|
acc[key] = Repo.new(
default_branch: attrs["default_branch"],
last_fetch_at: attrs["last_fetch_at"],
last_synced_at: attrs["last_synced_at"],
status: attrs["status"],
last_error: attrs["last_error"]
)
end
orgs = (raw["orgs"] || {}).each_with_object({}) do |(key, attrs), acc|
acc[key] = Org.new(
last_listed_at: attrs["last_listed_at"],
repo_count: attrs["repo_count"] || 0,
last_error: attrs["last_error"]
)
end
State.new(repos: repos, orgs: orgs)
end
|
.emit(state) ⇒ Object
131
132
133
134
135
136
137
|
# File 'lib/repo_tender/state/store.rb', line 131
def self.emit(state)
payload = {
"repos" => state.repos.each_with_object({}) { |(k, v), acc| acc[k] = v.to_h_compact },
"orgs" => state.orgs.each_with_object({}) { |(k, v), acc| acc[k] = v.to_h_compact }
}
YAML.dump(payload, line_width: -1)
end
|
.load(path) ⇒ Object
67
68
69
70
|
# File 'lib/repo_tender/state/store.rb', line 67
def self.load(path)
raw = read_yaml(path)
Success(build_state(raw))
end
|
.read_yaml(path) ⇒ Object
96
97
98
99
100
101
102
|
# File 'lib/repo_tender/state/store.rb', line 96
def self.read_yaml(path)
return {} unless File.exist?(path)
YAML.safe_load_file(path, permitted_classes: [Symbol, Time], aliases: false) || {}
end
|
.validate(state) ⇒ Object
87
88
89
90
91
92
93
94
|
# File 'lib/repo_tender/state/store.rb', line 87
def self.validate(state)
state.repos.each do |key, repo|
unless STATUSES.include?(repo.status)
return Failure({repos: {key => {status: ["must be one of: #{STATUSES.join(", ")}"]}}})
end
end
Success(state)
end
|
.write(path, state) ⇒ Object
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
# File 'lib/repo_tender/state/store.rb', line 72
def self.write(path, state)
validation = validate(state)
return validation if validation.failure?
FileUtils.mkdir_p(File.dirname(path))
tmp = "#{path}.tmp.#{Process.pid}"
begin
File.write(tmp, emit(state))
File.rename(tmp, path)
ensure
File.delete(tmp) if File.exist?(tmp)
end
Success(state)
end
|