textus 0.55.1 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +175 -2768
- data/README.md +17 -18
- data/lib/textus/artifact.rb +8 -0
- data/lib/textus/errors.rb +2 -39
- data/lib/textus/human.rb +7 -0
- data/lib/textus/infra/adapters/concurrency_adapter.rb +15 -0
- data/lib/textus/infra/clock.rb +7 -0
- data/lib/textus/infra/database.rb +127 -0
- data/lib/textus/infra/file_system.rb +54 -0
- data/lib/textus/infra/locks.rb +50 -0
- data/lib/textus/infra/port/clock.rb +18 -0
- data/lib/textus/infra/port/publisher.rb +71 -0
- data/lib/textus/infra/port/sentinel.rb +25 -0
- data/lib/textus/infra/port/sentinel_store.rb +93 -0
- data/lib/textus/infra/store/audit_log.rb +42 -0
- data/lib/textus/infra/store/base.rb +25 -0
- data/lib/textus/infra/store/cursor_store.rb +27 -0
- data/lib/textus/infra/store/entry_index.rb +31 -0
- data/lib/textus/infra/store/envelope/meta.rb +63 -0
- data/lib/textus/infra/store/file_store.rb +23 -0
- data/lib/textus/infra/store/index/builder.rb +28 -0
- data/lib/textus/infra/store/index/lookup.rb +23 -0
- data/lib/textus/infra/store/link_store.rb +43 -0
- data/lib/textus/infra/store/workflow_queue.rb +54 -0
- data/lib/textus/infra/store.rb +29 -0
- data/lib/textus/ingest.rb +8 -0
- data/lib/textus/lanes/artifact/cache.rb +35 -0
- data/lib/textus/lanes/artifact/handlers.rb +29 -0
- data/lib/textus/lanes/artifact/lifecycle.rb +23 -0
- data/lib/textus/lanes/artifact/verbs.rb +18 -0
- data/lib/textus/lanes/ingest/dedup.rb +17 -0
- data/lib/textus/lanes/ingest/entry_types/asset.rb +45 -0
- data/lib/textus/lanes/ingest/entry_types/link.rb +16 -0
- data/lib/textus/lanes/ingest/entry_types/text.rb +17 -0
- data/lib/textus/lanes/ingest/entry_types.rb +25 -0
- data/lib/textus/lanes/ingest/handlers.rb +54 -0
- data/lib/textus/lanes/ingest/index_rebuilder.rb +13 -0
- data/lib/textus/lanes/ingest/key_builder.rb +33 -0
- data/lib/textus/lanes/ingest/resolver.rb +21 -0
- data/lib/textus/lanes/knowledge/freeform.rb +15 -0
- data/lib/textus/lanes/knowledge/handlers.rb +53 -0
- data/lib/textus/lanes/knowledge/runner.rb +45 -0
- data/lib/textus/lanes/knowledge/verbs.rb +16 -0
- data/lib/textus/lanes/scratchpad/handlers.rb +164 -0
- data/lib/textus/lanes/scratchpad/verbs.rb +73 -0
- data/lib/textus/{gate → protocol}/binder.rb +2 -10
- data/lib/textus/protocol/boot/catalog.rb +34 -0
- data/lib/textus/protocol/boot/context.rb +124 -0
- data/lib/textus/protocol/boot/protocol.rb +33 -0
- data/lib/textus/protocol/boot.rb +42 -0
- data/lib/textus/protocol/builder/domain.rb +17 -0
- data/lib/textus/protocol/builder/infrastructure.rb +17 -0
- data/lib/textus/protocol/builder/runtime.rb +46 -0
- data/lib/textus/protocol/builder.rb +11 -0
- data/lib/textus/protocol/container.rb +22 -0
- data/lib/textus/protocol/diff.rb +170 -0
- data/lib/textus/protocol/entry_constraint.rb +54 -0
- data/lib/textus/protocol/envelope.rb +70 -0
- data/lib/textus/protocol/format/base.rb +80 -0
- data/lib/textus/protocol/format/bash.rb +10 -0
- data/lib/textus/protocol/format/javascript.rb +10 -0
- data/lib/textus/protocol/format/json.rb +82 -0
- data/lib/textus/protocol/format/markdown.rb +53 -0
- data/lib/textus/protocol/format/python.rb +10 -0
- data/lib/textus/protocol/format/ruby.rb +10 -0
- data/lib/textus/protocol/format/script.rb +55 -0
- data/lib/textus/protocol/format/text.rb +48 -0
- data/lib/textus/protocol/format/yaml.rb +105 -0
- data/lib/textus/protocol/format.rb +73 -0
- data/lib/textus/protocol/freshness/ttl_evaluator.rb +61 -0
- data/lib/textus/protocol/gate.rb +61 -0
- data/lib/textus/protocol/handlers/command_context.rb +19 -0
- data/lib/textus/protocol/handlers/concern.rb +15 -0
- data/lib/textus/protocol/handlers/ops/audit_filter.rb +37 -0
- data/lib/textus/protocol/handlers/ops/bulk_planner.rb +38 -0
- data/lib/textus/protocol/handlers/ops/data_mv.rb +49 -0
- data/lib/textus/protocol/handlers/ops/git_blame.rb +65 -0
- data/lib/textus/protocol/handlers/ops/pulse_coordinator.rb +37 -0
- data/lib/textus/protocol/handlers/ops/source_expander.rb +41 -0
- data/lib/textus/protocol/handlers/query_context.rb +41 -0
- data/lib/textus/protocol/handlers/read.rb +67 -0
- data/lib/textus/protocol/handlers/system.rb +93 -0
- data/lib/textus/protocol/handlers/write.rb +57 -0
- data/lib/textus/protocol/init.rb +143 -0
- data/lib/textus/protocol/key/grammar.rb +35 -0
- data/lib/textus/protocol/key/matching.rb +23 -0
- data/lib/textus/protocol/key/path.rb +23 -0
- data/lib/textus/protocol/key.rb +25 -0
- data/lib/textus/protocol/lane.rb +12 -0
- data/lib/textus/{store/geometry.rb → protocol/layout.rb} +32 -22
- data/lib/textus/protocol/links/resolver.rb +33 -0
- data/lib/textus/protocol/links/uri_rewriter.rb +30 -0
- data/lib/textus/protocol/links.rb +10 -0
- data/lib/textus/protocol/manifest/data.rb +90 -0
- data/lib/textus/protocol/manifest/entry/base.rb +95 -0
- data/lib/textus/protocol/manifest/entry/leaf.rb +19 -0
- data/lib/textus/protocol/manifest/entry/nested.rb +19 -0
- data/lib/textus/protocol/manifest/entry/parser.rb +71 -0
- data/lib/textus/protocol/manifest/entry/publish.rb +209 -0
- data/lib/textus/protocol/manifest/entry.rb +10 -0
- data/lib/textus/protocol/manifest/policy/publish_target.rb +36 -0
- data/lib/textus/protocol/manifest/policy.rb +69 -0
- data/lib/textus/protocol/manifest/resolver.rb +156 -0
- data/lib/textus/protocol/manifest/schema/contract.rb +53 -0
- data/lib/textus/protocol/manifest/schema/validator.rb +52 -0
- data/lib/textus/protocol/manifest/schema.rb +40 -0
- data/lib/textus/protocol/manifest.rb +62 -0
- data/lib/textus/protocol/produce/render/context.rb +43 -0
- data/lib/textus/protocol/produce/render.rb +44 -0
- data/lib/textus/protocol/schema/registry.rb +44 -0
- data/lib/textus/protocol/schema/tools.rb +104 -0
- data/lib/textus/protocol/schema.rb +117 -0
- data/lib/textus/protocol/session/bridge.rb +11 -0
- data/lib/textus/protocol/session/context.rb +5 -0
- data/lib/textus/protocol/session.rb +87 -0
- data/lib/textus/protocol/store_engine/delete.rb +37 -0
- data/lib/textus/protocol/store_engine/move.rb +109 -0
- data/lib/textus/protocol/store_engine/pipeline.rb +61 -0
- data/lib/textus/protocol/store_engine/put.rb +104 -0
- data/lib/textus/protocol/store_engine.rb +86 -0
- data/lib/textus/protocol/v4.rb +25 -0
- data/lib/textus/protocol/verb_registry/arg_spec.rb +19 -0
- data/lib/textus/protocol/verb_registry/core_verbs.rb +164 -0
- data/lib/textus/protocol/verb_registry/verb_spec.rb +42 -0
- data/lib/textus/protocol/verb_registry.rb +42 -0
- data/lib/textus/surface/cli/group/{rule.rb → web.rb} +2 -2
- data/lib/textus/surface/cli/runner.rb +25 -25
- data/lib/textus/surface/cli/sources.rb +1 -3
- data/lib/textus/surface/cli/verb/get.rb +3 -6
- data/lib/textus/surface/cli/verb/init.rb +1 -1
- data/lib/textus/surface/cli/verb/put.rb +8 -6
- data/lib/textus/surface/cli/verb/web_serve.rb +22 -0
- data/lib/textus/surface/cli.rb +25 -6
- data/lib/textus/surface/dispatch.rb +17 -0
- data/lib/textus/surface/mcp/adapters/mcp_adapter.rb +28 -0
- data/lib/textus/surface/mcp/catalog.rb +26 -62
- data/lib/textus/surface/mcp/cursor_manager.rb +22 -0
- data/lib/textus/surface/mcp/drift_annotation.rb +19 -0
- data/lib/textus/surface/mcp/errors.rb +0 -10
- data/lib/textus/surface/mcp/server.rb +46 -46
- data/lib/textus/surface/watcher.rb +18 -12
- data/lib/textus/surface/web.rb +50 -0
- data/lib/textus/value/call.rb +1 -1
- data/lib/textus/{core → value}/duration.rb +1 -4
- data/lib/textus/value/etag.rb +7 -7
- data/lib/textus/value/payload.rb +7 -0
- data/lib/textus/version.rb +1 -1
- data/lib/textus/workflow/consumer.rb +39 -0
- data/lib/textus/workflow/dsl/lifecycle.rb +32 -0
- data/lib/textus/workflow/dsl/pattern_matcher.rb +20 -0
- data/lib/textus/workflow/dsl/step_builder.rb +56 -0
- data/lib/textus/workflow/dsl.rb +111 -21
- data/lib/textus/workflow/event_emitter.rb +21 -0
- data/lib/textus/workflow/publisher.rb +59 -0
- data/lib/textus/workflow/registry.rb +31 -8
- data/lib/textus/workflow/runner.rb +25 -61
- data/lib/textus/workflow/scheduler.rb +45 -0
- data/lib/textus/workflow/step_executor.rb +78 -0
- data/lib/textus/workflow/step_helpers.rb +102 -0
- data/lib/textus/workflow.rb +2 -5
- data/lib/textus.rb +16 -80
- metadata +167 -183
- data/SPEC.md +0 -908
- data/docs/architecture/README.md +0 -273
- data/docs/reference/conventions.md +0 -138
- data/lib/textus/action/accept.rb +0 -46
- data/lib/textus/action/audit.rb +0 -94
- data/lib/textus/action/base.rb +0 -42
- data/lib/textus/action/blame.rb +0 -79
- data/lib/textus/action/boot.rb +0 -15
- data/lib/textus/action/data_mv.rb +0 -58
- data/lib/textus/action/deps.rb +0 -19
- data/lib/textus/action/doctor.rb +0 -17
- data/lib/textus/action/drain.rb +0 -31
- data/lib/textus/action/enqueue.rb +0 -37
- data/lib/textus/action/get.rb +0 -34
- data/lib/textus/action/ingest.rb +0 -199
- data/lib/textus/action/jobs.rb +0 -27
- data/lib/textus/action/key_delete.rb +0 -26
- data/lib/textus/action/key_delete_prefix.rb +0 -35
- data/lib/textus/action/key_mv.rb +0 -122
- data/lib/textus/action/key_mv_prefix.rb +0 -48
- data/lib/textus/action/list.rb +0 -28
- data/lib/textus/action/propose.rb +0 -42
- data/lib/textus/action/published.rb +0 -22
- data/lib/textus/action/pulse.rb +0 -49
- data/lib/textus/action/put.rb +0 -38
- data/lib/textus/action/rdeps.rb +0 -24
- data/lib/textus/action/reject.rb +0 -28
- data/lib/textus/action/rule_explain.rb +0 -81
- data/lib/textus/action/rule_lint.rb +0 -62
- data/lib/textus/action/rule_list.rb +0 -38
- data/lib/textus/action/schema_envelope.rb +0 -22
- data/lib/textus/action/uid.rb +0 -19
- data/lib/textus/action/where.rb +0 -21
- data/lib/textus/boot.rb +0 -202
- data/lib/textus/contract/arg.rb +0 -10
- data/lib/textus/contract/dsl.rb +0 -88
- data/lib/textus/contract/spec.rb +0 -25
- data/lib/textus/contract.rb +0 -12
- data/lib/textus/core/freshness/evaluator.rb +0 -150
- data/lib/textus/core/freshness/verdict.rb +0 -34
- data/lib/textus/core/freshness.rb +0 -11
- data/lib/textus/core/retention/sweep.rb +0 -57
- data/lib/textus/core/retention.rb +0 -11
- data/lib/textus/core/sentinel.rb +0 -30
- data/lib/textus/doctor/check/audit_log.rb +0 -34
- data/lib/textus/doctor/check/generator_drift.rb +0 -29
- data/lib/textus/doctor/check/illegal_keys.rb +0 -60
- data/lib/textus/doctor/check/manifest_files.rb +0 -25
- data/lib/textus/doctor/check/notebook_sources.rb +0 -50
- data/lib/textus/doctor/check/orphaned_publish_targets.rb +0 -35
- data/lib/textus/doctor/check/proposal_targets.rb +0 -45
- data/lib/textus/doctor/check/protocol_version.rb +0 -47
- data/lib/textus/doctor/check/publish_tree_index_overlap.rb +0 -48
- data/lib/textus/doctor/check/raw_asset_paths.rb +0 -50
- data/lib/textus/doctor/check/rule_ambiguity.rb +0 -49
- data/lib/textus/doctor/check/schema_parse_error.rb +0 -28
- data/lib/textus/doctor/check/schema_violations.rb +0 -28
- data/lib/textus/doctor/check/schemas.rb +0 -26
- data/lib/textus/doctor/check/sentinels.rb +0 -60
- data/lib/textus/doctor/check/stale_reviewed_stamp.rb +0 -54
- data/lib/textus/doctor/check/templates.rb +0 -29
- data/lib/textus/doctor/check/unowned_schema_fields.rb +0 -40
- data/lib/textus/doctor/check.rb +0 -41
- data/lib/textus/doctor/validator.rb +0 -93
- data/lib/textus/doctor.rb +0 -54
- data/lib/textus/format/base.rb +0 -49
- data/lib/textus/format/json.rb +0 -102
- data/lib/textus/format/markdown.rb +0 -80
- data/lib/textus/format/shared.rb +0 -17
- data/lib/textus/format/text.rb +0 -55
- data/lib/textus/format/yaml.rb +0 -125
- data/lib/textus/format.rb +0 -46
- data/lib/textus/gate/auth.rb +0 -212
- data/lib/textus/gate.rb +0 -92
- data/lib/textus/init.rb +0 -157
- data/lib/textus/jobs.rb +0 -9
- data/lib/textus/key/distance.rb +0 -55
- data/lib/textus/key/grammar.rb +0 -33
- data/lib/textus/key/matching.rb +0 -24
- data/lib/textus/key/path.rb +0 -28
- data/lib/textus/manifest/capabilities.rb +0 -29
- data/lib/textus/manifest/data.rb +0 -102
- data/lib/textus/manifest/entry/base.rb +0 -105
- data/lib/textus/manifest/entry/ignore_matcher.rb +0 -46
- data/lib/textus/manifest/entry/leaf.rb +0 -17
- data/lib/textus/manifest/entry/nested.rb +0 -37
- data/lib/textus/manifest/entry/parser.rb +0 -99
- data/lib/textus/manifest/entry/produced.rb +0 -36
- data/lib/textus/manifest/entry/publish/mode.rb +0 -45
- data/lib/textus/manifest/entry/publish/none.rb +0 -14
- data/lib/textus/manifest/entry/publish/subtree_mirror.rb +0 -73
- data/lib/textus/manifest/entry/publish/template.rb +0 -16
- data/lib/textus/manifest/entry/publish/to_paths.rb +0 -76
- data/lib/textus/manifest/entry/publish/tree.rb +0 -50
- data/lib/textus/manifest/entry/publish.rb +0 -45
- data/lib/textus/manifest/entry/validators/format_matrix.rb +0 -21
- data/lib/textus/manifest/entry/validators/ignore.rb +0 -28
- data/lib/textus/manifest/entry/validators/publish.rb +0 -30
- data/lib/textus/manifest/entry/validators.rb +0 -18
- data/lib/textus/manifest/entry.rb +0 -8
- data/lib/textus/manifest/policy/matcher.rb +0 -51
- data/lib/textus/manifest/policy/publish_target.rb +0 -34
- data/lib/textus/manifest/policy/react.rb +0 -30
- data/lib/textus/manifest/policy/retention.rb +0 -29
- data/lib/textus/manifest/policy/source.rb +0 -30
- data/lib/textus/manifest/policy.rb +0 -95
- data/lib/textus/manifest/resolver.rb +0 -118
- data/lib/textus/manifest/rules.rb +0 -86
- data/lib/textus/manifest/schema/contract.rb +0 -61
- data/lib/textus/manifest/schema/keys.rb +0 -90
- data/lib/textus/manifest/schema/semantics/cross_field.rb +0 -53
- data/lib/textus/manifest/schema/semantics/invariants.rb +0 -125
- data/lib/textus/manifest/schema/semantics/migration.rb +0 -83
- data/lib/textus/manifest/schema/semantics.rb +0 -27
- data/lib/textus/manifest/schema/validator.rb +0 -48
- data/lib/textus/manifest/schema/vocabulary.rb +0 -25
- data/lib/textus/manifest/schema.rb +0 -30
- data/lib/textus/manifest.rb +0 -65
- data/lib/textus/meta.rb +0 -54
- data/lib/textus/port/audit_log.rb +0 -268
- data/lib/textus/port/build_lock.rb +0 -64
- data/lib/textus/port/clock.rb +0 -10
- data/lib/textus/port/publisher.rb +0 -60
- data/lib/textus/port/sentinel_store.rb +0 -83
- data/lib/textus/port/storage/file_stat.rb +0 -19
- data/lib/textus/port/storage/file_store.rb +0 -26
- data/lib/textus/port/store.rb +0 -93
- data/lib/textus/port/watcher_lock.rb +0 -48
- data/lib/textus/produce/engine.rb +0 -50
- data/lib/textus/produce/render.rb +0 -20
- data/lib/textus/schema/tools.rb +0 -115
- data/lib/textus/schema.rb +0 -100
- data/lib/textus/schemas.rb +0 -54
- data/lib/textus/store/compositor.rb +0 -34
- data/lib/textus/store/container.rb +0 -43
- data/lib/textus/store/cursor.rb +0 -26
- data/lib/textus/store/envelope/reader.rb +0 -43
- data/lib/textus/store/envelope/writer.rb +0 -195
- data/lib/textus/store/index/builder.rb +0 -74
- data/lib/textus/store/index/lookup.rb +0 -60
- data/lib/textus/store/jobs/base.rb +0 -13
- data/lib/textus/store/jobs/index.rb +0 -15
- data/lib/textus/store/jobs/materialize.rb +0 -15
- data/lib/textus/store/jobs/plan.rb +0 -11
- data/lib/textus/store/jobs/planner.rb +0 -104
- data/lib/textus/store/jobs/queue.rb +0 -154
- data/lib/textus/store/jobs/registry.rb +0 -19
- data/lib/textus/store/jobs/retention.rb +0 -50
- data/lib/textus/store/jobs/sweep.rb +0 -21
- data/lib/textus/store/jobs/worker.rb +0 -64
- data/lib/textus/store/session.rb +0 -37
- data/lib/textus/store.rb +0 -99
- data/lib/textus/surface/cli/verb/doctor.rb +0 -19
- data/lib/textus/surface/cli/verb/schema_diff.rb +0 -17
- data/lib/textus/surface/cli/verb/schema_init.rb +0 -21
- data/lib/textus/surface/cli/verb/schema_migrate.rb +0 -21
- data/lib/textus/surface/mcp.rb +0 -8
- data/lib/textus/surface/projector.rb +0 -27
- data/lib/textus/surface/role_scope.rb +0 -34
- data/lib/textus/value/command.rb +0 -16
- data/lib/textus/value/envelope.rb +0 -89
- data/lib/textus/value/result.rb +0 -26
- data/lib/textus/workflow/collector.rb +0 -27
- data/lib/textus/workflow/context.rb +0 -5
- data/lib/textus/workflow/loader.rb +0 -17
- data/lib/textus/workflow/pattern.rb +0 -18
data/lib/textus/port/store.rb
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "fileutils"
|
|
4
|
-
require "sqlite3"
|
|
5
|
-
|
|
6
|
-
module Textus
|
|
7
|
-
module Port
|
|
8
|
-
# SQLite-backed runtime store for textus state. Owns the connection,
|
|
9
|
-
# schema setup, WAL mode, and transaction boundary for the index and queue.
|
|
10
|
-
class Store
|
|
11
|
-
attr_reader :path, :connection
|
|
12
|
-
|
|
13
|
-
def initialize(root:)
|
|
14
|
-
@root = root
|
|
15
|
-
@path = Textus::Store::Geometry.new(root).store_db_path
|
|
16
|
-
FileUtils.mkdir_p(File.dirname(@path))
|
|
17
|
-
@connection = SQLite3::Database.new(@path)
|
|
18
|
-
@connection.results_as_hash = true
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def execute(sql, params = [])
|
|
22
|
-
@connection.execute(sql, params)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def query_value(sql, params = [])
|
|
26
|
-
@connection.get_first_value(sql, params)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def setup!
|
|
30
|
-
execute("PRAGMA journal_mode=WAL")
|
|
31
|
-
execute("PRAGMA foreign_keys=ON")
|
|
32
|
-
connection.execute_batch(<<~SQL)
|
|
33
|
-
CREATE TABLE IF NOT EXISTS entries (
|
|
34
|
-
key TEXT PRIMARY KEY,
|
|
35
|
-
lane TEXT NOT NULL,
|
|
36
|
-
format TEXT NOT NULL,
|
|
37
|
-
etag TEXT,
|
|
38
|
-
content TEXT,
|
|
39
|
-
extra TEXT,
|
|
40
|
-
indexed_at TEXT NOT NULL
|
|
41
|
-
) STRICT;
|
|
42
|
-
|
|
43
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS entries_fts USING fts5(
|
|
44
|
-
key, lane, content,
|
|
45
|
-
content=entries, content_rowid=rowid
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
CREATE TABLE IF NOT EXISTS jobs (
|
|
49
|
-
id TEXT PRIMARY KEY,
|
|
50
|
-
type TEXT NOT NULL,
|
|
51
|
-
args TEXT NOT NULL,
|
|
52
|
-
state TEXT NOT NULL DEFAULT 'ready',
|
|
53
|
-
role TEXT NOT NULL,
|
|
54
|
-
attempts INTEGER NOT NULL DEFAULT 0,
|
|
55
|
-
max_attempts INTEGER NOT NULL DEFAULT 3,
|
|
56
|
-
errors TEXT,
|
|
57
|
-
lease TEXT,
|
|
58
|
-
created_at TEXT NOT NULL,
|
|
59
|
-
updated_at TEXT NOT NULL
|
|
60
|
-
) STRICT;
|
|
61
|
-
|
|
62
|
-
CREATE INDEX IF NOT EXISTS idx_jobs_state ON jobs(state);
|
|
63
|
-
CREATE INDEX IF NOT EXISTS idx_entries_lane ON entries(lane);
|
|
64
|
-
SQL
|
|
65
|
-
self
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def transaction
|
|
69
|
-
connection.transaction
|
|
70
|
-
yield
|
|
71
|
-
connection.commit
|
|
72
|
-
rescue StandardError
|
|
73
|
-
connection.rollback if connection.transaction_active?
|
|
74
|
-
raise
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def close
|
|
78
|
-
connection.close unless connection.closed?
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def self.open(root)
|
|
82
|
-
store = new(root: root)
|
|
83
|
-
store.setup!
|
|
84
|
-
return store unless block_given?
|
|
85
|
-
|
|
86
|
-
yield store
|
|
87
|
-
ensure
|
|
88
|
-
store&.close
|
|
89
|
-
end
|
|
90
|
-
private :connection
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "socket"
|
|
4
|
-
|
|
5
|
-
module Textus
|
|
6
|
-
module Port
|
|
7
|
-
# Flock-based watcher presence lock. Held for the watcher's lifetime.
|
|
8
|
-
# Process death releases the flock automatically.
|
|
9
|
-
class WatcherLock
|
|
10
|
-
def initialize(root)
|
|
11
|
-
@path = Textus::Store::Geometry.new(root).lock_path("watcher")
|
|
12
|
-
@file = nil
|
|
13
|
-
FileUtils.mkdir_p(File.dirname(@path))
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self.running?(root)
|
|
17
|
-
path = Textus::Store::Geometry.new(root).lock_path("watcher")
|
|
18
|
-
return false unless File.exist?(path)
|
|
19
|
-
|
|
20
|
-
File.open(path, "r+") do |file|
|
|
21
|
-
got = file.flock(File::LOCK_EX | File::LOCK_NB)
|
|
22
|
-
file.flock(File::LOCK_UN) if got
|
|
23
|
-
return !got
|
|
24
|
-
end
|
|
25
|
-
rescue Errno::ENOENT
|
|
26
|
-
false
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def acquire
|
|
30
|
-
@file = File.open(@path, File::RDWR | File::CREAT, 0o644)
|
|
31
|
-
raise "watcher already running" unless @file.flock(File::LOCK_EX | File::LOCK_NB)
|
|
32
|
-
|
|
33
|
-
@file.write("pid=#{Process.pid} host=#{Socket.gethostname}\n")
|
|
34
|
-
@file.flush
|
|
35
|
-
self
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def release
|
|
39
|
-
return unless @file
|
|
40
|
-
|
|
41
|
-
@file.flock(File::LOCK_UN)
|
|
42
|
-
@file.close
|
|
43
|
-
FileUtils.rm_f(@path)
|
|
44
|
-
@file = nil
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
module Produce
|
|
3
|
-
class Engine
|
|
4
|
-
def self.converge(container:, call:, keys:)
|
|
5
|
-
new(container:, call:).run(keys)
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
def initialize(container:, call:)
|
|
9
|
-
@container = container
|
|
10
|
-
@call = call
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def run(keys)
|
|
14
|
-
results = { completed: [], failed: [] }
|
|
15
|
-
Array(keys).each { |key| produce_one(key, results) }
|
|
16
|
-
results
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
alias call run
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def produce_one(key, results)
|
|
24
|
-
workflow = @container.workflows.for(key)
|
|
25
|
-
if workflow
|
|
26
|
-
Workflow::Runner.new(workflow, container: @container, call: @call).run(key)
|
|
27
|
-
else
|
|
28
|
-
publish_only(key)
|
|
29
|
-
end
|
|
30
|
-
results[:completed] << key
|
|
31
|
-
rescue StandardError => e
|
|
32
|
-
results[:failed] << { key: key, error: e.message }
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def publish_only(key)
|
|
36
|
-
entry = @container.manifest.resolver.resolve(key).entry
|
|
37
|
-
return unless entry.publish_tree || !Array(entry.publish_to).empty?
|
|
38
|
-
|
|
39
|
-
reader = Textus::Store::Envelope::Reader.from(container: @container)
|
|
40
|
-
entry_path = @container.manifest.resolver.resolve(key).path
|
|
41
|
-
return unless entry.publish_tree || File.exist?(entry_path)
|
|
42
|
-
|
|
43
|
-
pctx = Textus::Manifest::Entry::Base::PublishContext.new(
|
|
44
|
-
container: @container, call: @call, reader: reader.method(:read),
|
|
45
|
-
)
|
|
46
|
-
entry.publish_via(pctx)
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "erb"
|
|
4
|
-
|
|
5
|
-
module Textus
|
|
6
|
-
module Produce
|
|
7
|
-
class Render
|
|
8
|
-
def initialize(template_loader:)
|
|
9
|
-
@template_loader = template_loader
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def bytes_for(target:, data:, boot:)
|
|
13
|
-
raise ArgumentError.new("Produce::Render called for a verbatim target #{target.to.inspect}") unless target.renders?
|
|
14
|
-
|
|
15
|
-
ctx = target.inject_boot ? data.merge("boot" => boot) : data
|
|
16
|
-
ERB.new(@template_loader.call(target.template), trim_mode: "-").result_with_hash(ctx)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
data/lib/textus/schema/tools.rb
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
require "yaml"
|
|
2
|
-
require "fileutils"
|
|
3
|
-
|
|
4
|
-
module Textus
|
|
5
|
-
class Schema
|
|
6
|
-
module Tools
|
|
7
|
-
# textus schema init NAME --from=KEY → infer YAML schema from an entry's frontmatter
|
|
8
|
-
def self.init(store, name:, from:)
|
|
9
|
-
env = pure_get(store, Textus::Value::Role::DEFAULT, from)
|
|
10
|
-
meta = env.meta
|
|
11
|
-
schema = {
|
|
12
|
-
"name" => name,
|
|
13
|
-
"required" => meta.keys,
|
|
14
|
-
"optional" => [],
|
|
15
|
-
"fields" => meta.each_with_object({}) { |(k, v), h| h[k] = { "type" => infer_type(v) } },
|
|
16
|
-
}
|
|
17
|
-
geom = Textus::Store::Geometry.new(store.root)
|
|
18
|
-
FileUtils.mkdir_p(geom.schemas_dir)
|
|
19
|
-
target = geom.schema_path(name)
|
|
20
|
-
File.write(target, YAML.dump(schema))
|
|
21
|
-
{ "protocol" => PROTOCOL, "schema_name" => name, "path" => target }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# textus schema diff NAME → list keys whose frontmatter violates the schema
|
|
25
|
-
def self.diff(store, name:)
|
|
26
|
-
schema = load_schema(store, name)
|
|
27
|
-
drift = []
|
|
28
|
-
store.manifest.resolver.enumerate.each do |row|
|
|
29
|
-
env = pure_get(store, Textus::Value::Role::DEFAULT, row[:key])
|
|
30
|
-
begin
|
|
31
|
-
schema.validate!(env.meta)
|
|
32
|
-
rescue SchemaViolation => e
|
|
33
|
-
drift << { "key" => row[:key], "details" => e.details }
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
{ "protocol" => PROTOCOL, "schema_name" => name, "drift" => drift }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# textus schema migrate NAME --rename=OLD:NEW → rewrites frontmatter across affected entries
|
|
40
|
-
# If --rename is omitted, falls back to schema.evolution.migrate_from.
|
|
41
|
-
def self.migrate(store, name:, rename: nil)
|
|
42
|
-
renames =
|
|
43
|
-
if rename
|
|
44
|
-
old_field, new_field = rename.split(":", 2)
|
|
45
|
-
raise UsageError.new("--rename=OLD:NEW") unless old_field && new_field && !new_field.empty?
|
|
46
|
-
|
|
47
|
-
{ old_field => new_field }
|
|
48
|
-
else
|
|
49
|
-
load_schema(store, name).evolution["migrate_from"] || {}
|
|
50
|
-
end
|
|
51
|
-
raise UsageError.new("schema migrate needs --rename=OLD:NEW or schema.evolution.migrate_from") if renames.empty?
|
|
52
|
-
|
|
53
|
-
authority = accept_role_for(store)
|
|
54
|
-
ops = store.as(authority)
|
|
55
|
-
touched = []
|
|
56
|
-
store.manifest.resolver.enumerate.each do |row|
|
|
57
|
-
env = pure_get(store, authority, row[:key])
|
|
58
|
-
meta = env.meta.dup
|
|
59
|
-
changed = false
|
|
60
|
-
renames.each do |old, new|
|
|
61
|
-
if meta.key?(old)
|
|
62
|
-
meta[new] = meta.delete(old)
|
|
63
|
-
changed = true
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
next unless changed
|
|
67
|
-
|
|
68
|
-
ops.put(row[:key], meta: meta, body: env.body)
|
|
69
|
-
touched << row[:key]
|
|
70
|
-
end
|
|
71
|
-
{ "protocol" => PROTOCOL, "migrated" => touched, "renames" => renames }
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def self.infer_type(value)
|
|
75
|
-
case value
|
|
76
|
-
when String then "string"
|
|
77
|
-
when Numeric then "number"
|
|
78
|
-
when true, false then "boolean"
|
|
79
|
-
when Array then "array"
|
|
80
|
-
when Hash then "object"
|
|
81
|
-
else "string"
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Orchestrator-free read: schema tooling must never trigger a fetch
|
|
86
|
-
# while inspecting/migrating entries (ADR 0062).
|
|
87
|
-
def self.pure_get(store, role, key)
|
|
88
|
-
scope = store.as(role)
|
|
89
|
-
Value::Result.unwrap(
|
|
90
|
-
Textus::Action::Get.call(
|
|
91
|
-
container: scope.container,
|
|
92
|
-
call: Textus::Value::Call.build(role: role),
|
|
93
|
-
key: key,
|
|
94
|
-
),
|
|
95
|
-
)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def self.load_schema(store, name)
|
|
99
|
-
store.schemas.fetch(name)
|
|
100
|
-
rescue IoError
|
|
101
|
-
raise UsageError.new("schema not found: #{name}")
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def self.accept_role_for(store)
|
|
105
|
-
authority = store.manifest.policy.roles_with_capability("author").first
|
|
106
|
-
return authority if authority
|
|
107
|
-
|
|
108
|
-
raise UsageError.new(
|
|
109
|
-
"schema migrate requires a role holding the 'author' capability; " \
|
|
110
|
-
"none declared (add e.g. `- { name: owner, can: [author] }` to roles:)",
|
|
111
|
-
)
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
data/lib/textus/schema.rb
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
require "yaml"
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
class Schema
|
|
5
|
-
attr_reader :name, :required, :optional, :fields, :raw
|
|
6
|
-
|
|
7
|
-
def self.load(path)
|
|
8
|
-
raw = YAML.safe_load_file(path, permitted_classes: [Symbol, Date, Time], aliases: false)
|
|
9
|
-
new(raw)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def initialize(raw)
|
|
13
|
-
@raw = raw || {}
|
|
14
|
-
@name = @raw["name"]
|
|
15
|
-
@required = Array(@raw["required"])
|
|
16
|
-
@optional = Array(@raw["optional"])
|
|
17
|
-
@fields = @raw["fields"] || {}
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def to_h
|
|
21
|
-
@raw
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def maintained_by(field)
|
|
25
|
-
meta = @fields[field] or return nil
|
|
26
|
-
meta["maintained_by"]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Returns the list of field names whose spec is a Hash but lacks the
|
|
30
|
-
# 'maintained_by' key. Used by Doctor::Check::UnownedSchemaFields.
|
|
31
|
-
def unowned_fields
|
|
32
|
-
@fields.each_with_object([]) do |(name, spec), acc|
|
|
33
|
-
next unless spec.is_a?(Hash)
|
|
34
|
-
|
|
35
|
-
acc << name if spec["maintained_by"].nil?
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def evolution
|
|
40
|
-
raw = @raw["evolution"] || {}
|
|
41
|
-
raw.each_with_object({}) do |(k, v), h|
|
|
42
|
-
h[k] = v.is_a?(Date) || v.is_a?(Time) ? v.to_s : v
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Returns nil on success; raises SchemaViolation on hard failure.
|
|
47
|
-
# Unknown fields produce warnings, returned as a String[] alongside.
|
|
48
|
-
def validate!(frontmatter)
|
|
49
|
-
missing = @required - frontmatter.keys
|
|
50
|
-
raise SchemaViolation.new("missing" => missing) unless missing.empty?
|
|
51
|
-
|
|
52
|
-
known = (@required + @optional).uniq
|
|
53
|
-
frontmatter.each do |k, v|
|
|
54
|
-
next unless @fields.key?(k)
|
|
55
|
-
|
|
56
|
-
check_type!(k, v, @fields[k])
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
warnings = frontmatter.keys - known
|
|
60
|
-
warnings.map { |w| "unknown field: #{w}" }
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
private
|
|
64
|
-
|
|
65
|
-
def check_type!(field, value, spec) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
66
|
-
type = spec["type"]
|
|
67
|
-
case type
|
|
68
|
-
when "string"
|
|
69
|
-
bad!(field, "expected string") unless value.is_a?(String)
|
|
70
|
-
if (max = spec["max"]) && value.bytesize > max
|
|
71
|
-
bad!(field, "exceeds max #{max}")
|
|
72
|
-
end
|
|
73
|
-
when "number"
|
|
74
|
-
bad!(field, "expected number") unless value.is_a?(Numeric)
|
|
75
|
-
when "boolean"
|
|
76
|
-
bad!(field, "expected boolean") unless [true, false].include?(value)
|
|
77
|
-
when "enum"
|
|
78
|
-
values = Array(spec["values"])
|
|
79
|
-
bad!(field, "not in enum #{values.inspect}") unless values.include?(value)
|
|
80
|
-
when "array"
|
|
81
|
-
bad!(field, "expected array") unless value.is_a?(Array)
|
|
82
|
-
if (items = spec["items"])
|
|
83
|
-
value.each_with_index { |v, i| check_type!("#{field}[#{i}]", v, items) }
|
|
84
|
-
end
|
|
85
|
-
when "object"
|
|
86
|
-
bad!(field, "expected object") unless value.is_a?(Hash)
|
|
87
|
-
if (sub = spec["fields"])
|
|
88
|
-
sub.each { |fk, fspec| check_type!("#{field}.#{fk}", value[fk], fspec) if value.key?(fk) }
|
|
89
|
-
end
|
|
90
|
-
when nil
|
|
91
|
-
# untyped — no check
|
|
92
|
-
# unknown type spec — vendor extension; ignore
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def bad!(field, reason)
|
|
97
|
-
raise SchemaViolation.new("field" => field, "reason" => reason)
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
data/lib/textus/schemas.rb
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
# Eager-loading schema cache. Loads every *.yaml under +dir+ at construction.
|
|
3
|
-
# A missing directory is treated as "no schemas" (does not raise) to mirror
|
|
4
|
-
# the lazy behavior previously embedded in Store#schema_for.
|
|
5
|
-
class Schemas
|
|
6
|
-
def initialize(dir)
|
|
7
|
-
@dir = dir
|
|
8
|
-
@schemas = {}
|
|
9
|
-
load_all
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def fetch(name)
|
|
13
|
-
@schemas[name] || raise(IoError.new("schema not found: #{File.join(@dir, "#{name}.yaml")}"))
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Only nil short-circuits. A missing-but-named schema still raises IoError.
|
|
17
|
-
def fetch_or_nil(name)
|
|
18
|
-
return nil if name.nil?
|
|
19
|
-
|
|
20
|
-
fetch(name)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def all
|
|
24
|
-
@schemas.values
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Name-keyed view: { canonical_name => Schema }. The key is the schema's
|
|
28
|
-
# file stem, which is authoritative even when a schema file carries no
|
|
29
|
-
# top-level `name:` (Schema#name reads the body and may be nil). Symmetric
|
|
30
|
-
# with #all (values); use this when you need the names too.
|
|
31
|
-
def by_name
|
|
32
|
-
@schemas.dup
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def load_all
|
|
38
|
-
return unless File.directory?(@dir)
|
|
39
|
-
|
|
40
|
-
Dir.glob(File.join(@dir, "*.yaml")).each do |path|
|
|
41
|
-
name = File.basename(path, ".yaml")
|
|
42
|
-
begin
|
|
43
|
-
@schemas[name] = Schema.load(path)
|
|
44
|
-
rescue StandardError
|
|
45
|
-
# Tolerate broken schema files at construction time so the rest of
|
|
46
|
-
# the store remains loadable. Surfacing the failure is the job of
|
|
47
|
-
# Doctor::Check::SchemaParseError. Lookups via #fetch still raise
|
|
48
|
-
# IoError for the missing-but-named schema.
|
|
49
|
-
next
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
class Store
|
|
5
|
-
class Compositor
|
|
6
|
-
def initialize(container)
|
|
7
|
-
@container = container
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def write(key, mentry:, payload:, call:, if_etag: nil)
|
|
11
|
-
Textus::Store::Envelope::Writer.from(container: @container, call: call)
|
|
12
|
-
.put(key, mentry: mentry, payload: payload, if_etag: if_etag)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def read(key)
|
|
16
|
-
Textus::Store::Envelope::Reader.from(container: @container).read(key)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def delete(key, call:, mentry: nil, if_etag: nil)
|
|
20
|
-
Textus::Store::Envelope::Writer.from(container: @container, call: call)
|
|
21
|
-
.delete(key, mentry: mentry, if_etag: if_etag)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def move(from_key:, to_key:, new_mentry:, call:, if_etag: nil)
|
|
25
|
-
Textus::Store::Envelope::Writer.from(container: @container, call: call)
|
|
26
|
-
.move(from_key: from_key, to_key: to_key, new_mentry: new_mentry, if_etag: if_etag)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def exists?(key)
|
|
30
|
-
Textus::Store::Envelope::Reader.from(container: @container).exists?(key)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
class Store
|
|
5
|
-
class Container
|
|
6
|
-
Infrastructure = Data.define(:file_store, :schemas, :audit_log, :job_store, :geometry)
|
|
7
|
-
Coordination = Data.define(:manifest, :workflows, :gate, :compositor)
|
|
8
|
-
|
|
9
|
-
def self.attribute_names
|
|
10
|
-
@attribute_names ||= [:root] + Infrastructure.members + Coordination.members
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def initialize(infra, coord)
|
|
14
|
-
@infra = infra
|
|
15
|
-
@coord = coord
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
attr_reader :infra, :coord
|
|
19
|
-
|
|
20
|
-
def root
|
|
21
|
-
@infra.geometry.root
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
Infrastructure.members.each do |name|
|
|
25
|
-
define_method(name) { @infra.public_send(name) }
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
Coordination.members.each do |name|
|
|
29
|
-
define_method(name) { @coord.public_send(name) }
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def wire_gate!(gate, compositor)
|
|
33
|
-
@coord = Coordination.new(
|
|
34
|
-
manifest: @coord.manifest,
|
|
35
|
-
workflows: @coord.workflows,
|
|
36
|
-
gate:,
|
|
37
|
-
compositor:,
|
|
38
|
-
)
|
|
39
|
-
self
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
data/lib/textus/store/cursor.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
require "fileutils"
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
class Store
|
|
5
|
-
# Per-role cursor cache under <root>/.state/cursors/<role>. A convenience so
|
|
6
|
-
# `textus pulse` (no --since) means "since I last looked". Gitignored;
|
|
7
|
-
# losing it just re-emits recent deltas, never corrupts the store. ADR 0036/0038.
|
|
8
|
-
class Cursor
|
|
9
|
-
def initialize(root:, role:)
|
|
10
|
-
@path = Store::Geometry.new(root).cursor_path(role)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def read
|
|
14
|
-
Integer(File.read(@path).strip)
|
|
15
|
-
rescue Errno::ENOENT, ArgumentError
|
|
16
|
-
0
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def write(seq)
|
|
20
|
-
FileUtils.mkdir_p(File.dirname(@path))
|
|
21
|
-
File.write(@path, seq.to_s)
|
|
22
|
-
seq
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
module Textus
|
|
2
|
-
class Store
|
|
3
|
-
module Envelope
|
|
4
|
-
# Read-only counterpart to EnvelopeWriter. Resolves a key, reads the
|
|
5
|
-
# bytes, parses them via the format strategy, and hands back an
|
|
6
|
-
# Envelope. Used by Mv (pre-move inspection) and by EnvelopeWriter
|
|
7
|
-
# (existing-meta lookup for the uid/sources preservation step in #put).
|
|
8
|
-
#
|
|
9
|
-
# No audit, no events, no permission checks — those live one layer up.
|
|
10
|
-
class Reader
|
|
11
|
-
def self.from(container:)
|
|
12
|
-
new(file_store: container.file_store, manifest: container.manifest,
|
|
13
|
-
geometry: container.geometry)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def initialize(file_store:, manifest:, geometry:)
|
|
17
|
-
@file_store = file_store
|
|
18
|
-
@manifest = manifest
|
|
19
|
-
@geometry = geometry
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def read(key)
|
|
23
|
-
res = @manifest.resolver.resolve(key)
|
|
24
|
-
path = res.path
|
|
25
|
-
return nil unless @file_store.exists?(path)
|
|
26
|
-
|
|
27
|
-
mentry = res.entry
|
|
28
|
-
raw = @file_store.read(path)
|
|
29
|
-
parsed = Format.for(mentry.format).parse(raw, path: path)
|
|
30
|
-
Textus::Value::Envelope.build(
|
|
31
|
-
key: key, mentry: mentry, path: path,
|
|
32
|
-
meta: parsed["_meta"], body: parsed["body"],
|
|
33
|
-
etag: Value::Etag.for_bytes(raw), content: parsed["content"]
|
|
34
|
-
)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def exists?(key)
|
|
38
|
-
@file_store.exists?(@manifest.resolver.resolve(key).path)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|