textus 0.5.0 → 0.8.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.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +83 -1
  3. data/README.md +29 -21
  4. data/SPEC.md +75 -142
  5. data/docs/architecture.md +42 -23
  6. data/lib/textus/builder/pipeline.rb +56 -0
  7. data/lib/textus/builder/renderer/json.rb +42 -0
  8. data/lib/textus/builder/renderer/markdown.rb +22 -0
  9. data/lib/textus/builder/renderer/text.rb +14 -0
  10. data/lib/textus/builder/renderer/yaml.rb +42 -0
  11. data/lib/textus/builder/renderer.rb +17 -0
  12. data/lib/textus/builder.rb +9 -114
  13. data/lib/textus/cli/group/hook.rb +11 -0
  14. data/lib/textus/cli/group/key.rb +12 -0
  15. data/lib/textus/cli/group/schema.rb +13 -0
  16. data/lib/textus/cli/verb/accept.rb +15 -0
  17. data/lib/textus/cli/verb/build.rb +13 -0
  18. data/lib/textus/cli/verb/delete.rb +16 -0
  19. data/lib/textus/cli/verb/deps.rb +12 -0
  20. data/lib/textus/cli/verb/doctor.rb +15 -0
  21. data/lib/textus/cli/verb/get.rb +12 -0
  22. data/lib/textus/cli/verb/hook_run.rb +48 -0
  23. data/lib/textus/cli/verb/hooks.rb +50 -0
  24. data/lib/textus/cli/verb/init.rb +14 -0
  25. data/lib/textus/cli/verb/intro.rb +11 -0
  26. data/lib/textus/cli/verb/list.rb +14 -0
  27. data/lib/textus/cli/verb/migrate_keys.rb +16 -0
  28. data/lib/textus/cli/verb/mv.rb +17 -0
  29. data/lib/textus/cli/verb/published.rb +11 -0
  30. data/lib/textus/cli/verb/put.rb +50 -0
  31. data/lib/textus/cli/verb/rdeps.rb +12 -0
  32. data/lib/textus/cli/verb/refresh.rb +15 -0
  33. data/lib/textus/cli/verb/schema.rb +12 -0
  34. data/lib/textus/cli/verb/schema_diff.rb +12 -0
  35. data/lib/textus/cli/verb/schema_init.rb +16 -0
  36. data/lib/textus/cli/verb/schema_migrate.rb +16 -0
  37. data/lib/textus/cli/verb/stale.rb +14 -0
  38. data/lib/textus/cli/verb/uid.rb +12 -0
  39. data/lib/textus/cli/verb/where.rb +12 -0
  40. data/lib/textus/cli.rb +23 -42
  41. data/lib/textus/doctor/check/audit_log.rb +50 -0
  42. data/lib/textus/doctor/check/hooks.rb +29 -0
  43. data/lib/textus/doctor/check/illegal_keys.rb +49 -0
  44. data/lib/textus/doctor/check/manifest_files.rb +38 -0
  45. data/lib/textus/doctor/check/schema_violations.rb +22 -0
  46. data/lib/textus/doctor/check/schemas.rb +26 -0
  47. data/lib/textus/doctor/check/sentinels.rb +57 -0
  48. data/lib/textus/doctor/check/templates.rb +26 -0
  49. data/lib/textus/doctor/check/unowned_schema_fields.rb +34 -0
  50. data/lib/textus/doctor/check.rb +30 -0
  51. data/lib/textus/doctor.rb +22 -288
  52. data/lib/textus/entry/base.rb +30 -0
  53. data/lib/textus/entry/json.rb +5 -1
  54. data/lib/textus/entry/markdown.rb +1 -1
  55. data/lib/textus/entry/text.rb +1 -1
  56. data/lib/textus/entry/yaml.rb +5 -1
  57. data/lib/textus/entry.rb +0 -5
  58. data/lib/textus/envelope.rb +30 -0
  59. data/lib/textus/hooks/builtin.rb +70 -0
  60. data/lib/textus/hooks/dispatcher.rb +49 -0
  61. data/lib/textus/hooks/loader.rb +26 -0
  62. data/lib/textus/hooks/registry.rb +73 -0
  63. data/lib/textus/init.rb +13 -10
  64. data/lib/textus/intro.rb +14 -16
  65. data/lib/textus/key/distance.rb +55 -0
  66. data/lib/textus/key/grammar.rb +33 -0
  67. data/lib/textus/key/path.rb +17 -0
  68. data/lib/textus/manifest/entry.rb +199 -0
  69. data/lib/textus/manifest.rb +10 -34
  70. data/lib/textus/migrate_keys.rb +1 -1
  71. data/lib/textus/projection.rb +5 -4
  72. data/lib/textus/proposal.rb +1 -1
  73. data/lib/textus/refresh.rb +11 -11
  74. data/lib/textus/schema/tools.rb +89 -0
  75. data/lib/textus/store/audit_log.rb +71 -0
  76. data/lib/textus/store/mover.rb +19 -16
  77. data/lib/textus/store/reader.rb +67 -0
  78. data/lib/textus/store/staleness.rb +10 -19
  79. data/lib/textus/store/validator.rb +11 -8
  80. data/lib/textus/store/view.rb +29 -0
  81. data/lib/textus/store/writer.rb +132 -0
  82. data/lib/textus/store.rb +25 -221
  83. data/lib/textus/version.rb +1 -1
  84. data/lib/textus.rb +14 -67
  85. metadata +73 -40
  86. data/lib/textus/audit_log.rb +0 -67
  87. data/lib/textus/builtin_actions.rb +0 -68
  88. data/lib/textus/cli/accept.rb +0 -13
  89. data/lib/textus/cli/action.rb +0 -51
  90. data/lib/textus/cli/build.rb +0 -11
  91. data/lib/textus/cli/delete.rb +0 -14
  92. data/lib/textus/cli/deprecated_alias.rb +0 -31
  93. data/lib/textus/cli/deps.rb +0 -10
  94. data/lib/textus/cli/doctor.rb +0 -13
  95. data/lib/textus/cli/extension_group.rb +0 -9
  96. data/lib/textus/cli/extensions.rb +0 -49
  97. data/lib/textus/cli/get.rb +0 -10
  98. data/lib/textus/cli/init.rb +0 -12
  99. data/lib/textus/cli/intro.rb +0 -9
  100. data/lib/textus/cli/key_group.rb +0 -10
  101. data/lib/textus/cli/list.rb +0 -12
  102. data/lib/textus/cli/migrate.rb +0 -41
  103. data/lib/textus/cli/migrate_keys.rb +0 -19
  104. data/lib/textus/cli/mv.rb +0 -20
  105. data/lib/textus/cli/published.rb +0 -9
  106. data/lib/textus/cli/put.rb +0 -48
  107. data/lib/textus/cli/rdeps.rb +0 -10
  108. data/lib/textus/cli/refresh.rb +0 -13
  109. data/lib/textus/cli/schema.rb +0 -10
  110. data/lib/textus/cli/schema_diff.rb +0 -15
  111. data/lib/textus/cli/schema_group.rb +0 -33
  112. data/lib/textus/cli/schema_init.rb +0 -19
  113. data/lib/textus/cli/schema_migrate.rb +0 -19
  114. data/lib/textus/cli/stale.rb +0 -12
  115. data/lib/textus/cli/uid.rb +0 -15
  116. data/lib/textus/cli/where.rb +0 -10
  117. data/lib/textus/extension_registry.rb +0 -61
  118. data/lib/textus/extensions.rb +0 -33
  119. data/lib/textus/key_distance.rb +0 -53
  120. data/lib/textus/manifest_entry.rb +0 -185
  121. data/lib/textus/migrate_v2.rb +0 -27
  122. data/lib/textus/schema_tools.rb +0 -87
  123. data/lib/textus/store/events.rb +0 -31
  124. data/lib/textus/store_view.rb +0 -27
@@ -1,13 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Accept < Verb
4
- option :as_flag, "--as=ROLE"
5
-
6
- def call(store)
7
- key = positional.shift or raise UsageError.new("accept requires a key")
8
- role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
9
- emit(store.accept(key, as: role))
10
- end
11
- end
12
- end
13
- end
@@ -1,51 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Action < Verb
4
- prepend DeprecatedAliasMixin
5
-
6
- def self.deprecated_name = "action"
7
- def self.replacement_path = "extension run"
8
-
9
- def parse(argv)
10
- @raw_argv = argv
11
- end
12
-
13
- def call(store)
14
- name = @raw_argv.shift
15
- raise UsageError.new("action requires a name") if name.nil?
16
-
17
- as_flag = nil
18
- args = {}
19
- @raw_argv.each do |tok|
20
- case tok
21
- when /\A--as=(.+)\z/ then as_flag = ::Regexp.last_match(1)
22
- when /\A--format=/ then next
23
- when /\A--([\w-]+)=(.*)\z/ then args[::Regexp.last_match(1)] = ::Regexp.last_match(2)
24
- else
25
- raise UsageError.new("unknown arg to 'action #{name}': #{tok}")
26
- end
27
- end
28
-
29
- role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
30
- callable = store.registry.action(name)
31
- view = StoreView.new(store, writable: true, as: role)
32
-
33
- begin
34
- Timeout.timeout(Textus::Refresh::ACTION_TIMEOUT_SECONDS) do
35
- callable.call(config: {}, store: view, args: args)
36
- end
37
- rescue Timeout::Error
38
- raise UsageError.new(
39
- "action '#{name}' exceeded #{Textus::Refresh::ACTION_TIMEOUT_SECONDS}s timeout",
40
- )
41
- rescue Textus::Error
42
- raise
43
- rescue StandardError => e
44
- raise UsageError.new("action '#{name}' raised: #{e.class}: #{e.message}")
45
- end
46
-
47
- emit({ "action" => name, "ok" => true })
48
- end
49
- end
50
- end
51
- end
@@ -1,11 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Build < Verb
4
- option :prefix, "--prefix=K"
5
-
6
- def call(store)
7
- emit(Textus::Builder.new(store).build(prefix: prefix))
8
- end
9
- end
10
- end
11
- end
@@ -1,14 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Delete < Verb
4
- option :as_flag, "--as=ROLE"
5
- option :if_etag, "--if-etag=E"
6
-
7
- def call(store)
8
- key = positional.shift or raise UsageError.new("delete requires a key")
9
- role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
10
- emit(store.delete(key, if_etag: if_etag, as: role))
11
- end
12
- end
13
- end
14
- end
@@ -1,31 +0,0 @@
1
- module Textus
2
- class CLI
3
- module DeprecatedAliasMixin
4
- def self.prepended(base)
5
- base.extend(ClassMethods)
6
- end
7
-
8
- module ClassMethods
9
- def deprecated_name
10
- raise NotImplementedError.new("#{self}.deprecated_name must be defined")
11
- end
12
-
13
- def replacement_path
14
- raise NotImplementedError.new("#{self}.replacement_path must be defined")
15
- end
16
- end
17
-
18
- attr_writer :deprecated_alias
19
-
20
- def call(store)
21
- if @deprecated_alias
22
- @stderr.puts(
23
- "textus: '#{self.class.deprecated_name}' is deprecated; " \
24
- "use 'textus #{self.class.replacement_path}' instead. Removed in 0.6.",
25
- )
26
- end
27
- super
28
- end
29
- end
30
- end
31
- end
@@ -1,10 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Deps < Verb
4
- def call(store)
5
- key = positional.shift or raise UsageError.new("deps requires a key")
6
- emit({ "key" => key, "deps" => store.deps(key) })
7
- end
8
- end
9
- end
10
- end
@@ -1,13 +0,0 @@
1
- module Textus
2
- class CLI
3
- class DoctorVerb < Verb
4
- option :checks, "--check=NAME"
5
-
6
- def call(store)
7
- check_list = checks&.split(",")&.map(&:strip)
8
- res = Textus::Doctor.run(store, checks: check_list)
9
- emit(res, exit_code: res["ok"] ? 0 : 1)
10
- end
11
- end
12
- end
13
- end
@@ -1,9 +0,0 @@
1
- module Textus
2
- class CLI
3
- class ExtensionGroup < Group
4
- self.cli_name = "extension"
5
- subcommands["list"] = Extensions
6
- subcommands["run"] = Action
7
- end
8
- end
9
- end
@@ -1,49 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Extensions < Verb
4
- prepend DeprecatedAliasMixin
5
-
6
- def self.deprecated_name = "extensions"
7
- def self.replacement_path = "extension list"
8
-
9
- option :kind, "--kind=K"
10
-
11
- def call(store) # rubocop:disable Metrics/AbcSize
12
- # When invoked as the flat alias `textus extensions list`, the positional "list"
13
- # is still present. When invoked via `textus extension list` (group), it has been
14
- # consumed by the group dispatcher — both are valid.
15
- subcommand = positional.first
16
- if subcommand
17
- raise UsageError.new("extensions requires 'list'") unless subcommand == "list"
18
-
19
- positional.shift
20
- end
21
-
22
- rows = []
23
- rows += store.registry.action_names.map { |n| { "kind" => "action", "name" => n.to_s } }
24
- rows += store.registry.doctor_check_names.map { |n| { "kind" => "doctor_check", "name" => n.to_s } }
25
- rows += store.registry.reducer_names.map { |n| { "kind" => "reducer", "name" => n.to_s } }
26
- store.registry.hook_events.each do |evt|
27
- store.registry.hooks(evt).each do |h|
28
- rows << { "kind" => "hook", "event" => evt.to_s, "name" => h[:name].to_s }
29
- end
30
- end
31
- store.manifest.entries.each do |e|
32
- e.events.each do |evt, defs|
33
- Array(defs).each do |defn|
34
- next unless defn["exec"]
35
-
36
- rows << {
37
- "kind" => "hook", "event" => evt.to_s, "exec" => defn["exec"],
38
- "key" => e.key, "as" => defn["as"] || "script"
39
- }
40
- end
41
- end
42
- end
43
- rows.select! { |r| r["kind"] == kind } if kind
44
-
45
- emit({ "extensions" => rows })
46
- end
47
- end
48
- end
49
- end
@@ -1,10 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Get < Verb
4
- def call(store)
5
- key = positional.shift or raise UsageError.new("get requires a key")
6
- emit(store.get(key))
7
- end
8
- end
9
- end
10
- end
@@ -1,12 +0,0 @@
1
- module Textus
2
- class CLI
3
- class InitVerb < Verb
4
- def self.needs_store? = false
5
-
6
- def call(_store)
7
- target = File.join(@cwd, ".textus")
8
- emit(Textus::Init.run(target))
9
- end
10
- end
11
- end
12
- end
@@ -1,9 +0,0 @@
1
- module Textus
2
- class CLI
3
- class IntroVerb < Verb
4
- def call(store)
5
- emit(Textus::Intro.run(store))
6
- end
7
- end
8
- end
9
- end
@@ -1,10 +0,0 @@
1
- module Textus
2
- class CLI
3
- class KeyGroup < Group
4
- self.cli_name = "key"
5
- subcommands["mv"] = Mv
6
- subcommands["uid"] = Uid
7
- subcommands["migrate"] = MigrateKeysVerb
8
- end
9
- end
10
- end
@@ -1,12 +0,0 @@
1
- module Textus
2
- class CLI
3
- class List < Verb
4
- option :prefix, "--prefix=KEY"
5
- option :zone, "--zone=Z"
6
-
7
- def call(store)
8
- emit({ "entries" => store.list(prefix: prefix, zone: zone) })
9
- end
10
- end
11
- end
12
- end
@@ -1,41 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Migrate < Verb
4
- # Does not need a store — the target manifest may still be textus/1.
5
- def self.needs_store? = false
6
-
7
- def call(_store)
8
- target = positional.shift or raise UsageError.new("migrate requires a target (e.g. 'v2')")
9
- raise UsageError.new("unknown migration target: #{target.inspect}; supported: v2") unless target == "v2"
10
-
11
- # Locate the .textus directory the same way Store.discover does.
12
- root = find_textus_root
13
- emit(Textus::MigrateV2.run(root))
14
- end
15
-
16
- private
17
-
18
- def find_textus_root
19
- explicit = ENV.fetch("TEXTUS_ROOT", nil)
20
- if explicit
21
- abs = File.expand_path(explicit)
22
- return abs if File.directory?(abs)
23
-
24
- raise IoError.new("no textus store at #{abs}")
25
- end
26
-
27
- dir = File.expand_path(@cwd)
28
- loop do
29
- candidate = File.join(dir, ".textus")
30
- return candidate if File.directory?(candidate) && File.exist?(File.join(candidate, "manifest.yaml"))
31
-
32
- parent = File.dirname(dir)
33
- break if parent == dir
34
-
35
- dir = parent
36
- end
37
- raise IoError.new("no .textus directory found from #{@cwd}")
38
- end
39
- end
40
- end
41
- end
@@ -1,19 +0,0 @@
1
- module Textus
2
- class CLI
3
- class MigrateKeysVerb < Verb
4
- prepend DeprecatedAliasMixin
5
-
6
- def self.deprecated_name = "migrate-keys"
7
- def self.replacement_path = "key migrate"
8
-
9
- option :write, "--write"
10
- option :dry_run, "--dry-run"
11
-
12
- def call(store)
13
- effective_write = write && !dry_run
14
- res = Textus::MigrateKeys.run(store, write: effective_write || false)
15
- emit(res, exit_code: res["ok"] ? 0 : 1)
16
- end
17
- end
18
- end
19
- end
data/lib/textus/cli/mv.rb DELETED
@@ -1,20 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Mv < Verb
4
- prepend DeprecatedAliasMixin
5
-
6
- def self.deprecated_name = "mv"
7
- def self.replacement_path = "key mv"
8
-
9
- option :as_flag, "--as=ROLE"
10
- option :dry_run, "--dry-run"
11
-
12
- def call(store)
13
- old_key = positional.shift or raise UsageError.new("mv requires <old-key> <new-key>")
14
- new_key = positional.shift or raise UsageError.new("mv requires <old-key> <new-key>")
15
- role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
16
- emit(store.mv(old_key, new_key, as: role, dry_run: dry_run || false))
17
- end
18
- end
19
- end
20
- end
@@ -1,9 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Published < Verb
4
- def call(store)
5
- emit({ "published" => store.published })
6
- end
7
- end
8
- end
9
- end
@@ -1,48 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Put < Verb
4
- option :as_flag, "--as=ROLE"
5
- option :use_stdin, "--stdin"
6
- option :action_name, "--action=NAME"
7
-
8
- def call(store) # rubocop:disable Metrics/AbcSize
9
- key = positional.shift or raise UsageError.new("put requires a key")
10
- raise UsageError.new("put requires --stdin in v1") unless use_stdin
11
-
12
- role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
13
-
14
- raw = @stdin.read
15
- payload =
16
- if action_name
17
- callable = store.registry.action(action_name)
18
- result =
19
- begin
20
- Timeout.timeout(Textus::Refresh::ACTION_TIMEOUT_SECONDS) do
21
- callable.call(config: { "bytes" => raw }, store: Textus::StoreView.new(store), args: {})
22
- end
23
- rescue Timeout::Error
24
- raise UsageError.new(
25
- "action '#{action_name}' exceeded #{Textus::Refresh::ACTION_TIMEOUT_SECONDS}s timeout",
26
- )
27
- end
28
- basename = key.split(".").last
29
- {
30
- "_meta" => {
31
- "name" => basename,
32
- "last_refreshed_at" => Time.now.utc.iso8601,
33
- "actioned_with" => action_name,
34
- }.merge(result[:_meta] || result["_meta"] || result[:frontmatter] || result["frontmatter"] || {}),
35
- "body" => result[:body] || result["body"] || "",
36
- }
37
- else
38
- JSON.parse(raw)
39
- end
40
-
41
- meta = payload["_meta"] || payload["frontmatter"] || {}
42
- body = payload["body"] || ""
43
- if_etag = payload["if_etag"]
44
- emit(store.put(key, meta: meta, body: body, if_etag: if_etag, as: role))
45
- end
46
- end
47
- end
48
- end
@@ -1,10 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Rdeps < Verb
4
- def call(store)
5
- key = positional.shift or raise UsageError.new("rdeps requires a key")
6
- emit({ "key" => key, "rdeps" => store.rdeps(key) })
7
- end
8
- end
9
- end
10
- end
@@ -1,13 +0,0 @@
1
- module Textus
2
- class CLI
3
- class RefreshVerb < Verb
4
- option :as_flag, "--as=ROLE"
5
-
6
- def call(store)
7
- key = positional.shift or raise UsageError.new("refresh requires a key")
8
- role = Role.resolve(flag: as_flag, env: ENV, root: store.root)
9
- emit(Textus::Refresh.call(store, key, as: role))
10
- end
11
- end
12
- end
13
- end
@@ -1,10 +0,0 @@
1
- module Textus
2
- class CLI
3
- class SchemaVerb < Verb
4
- def call(store)
5
- key = positional.shift or raise UsageError.new("schema requires a key")
6
- emit(store.schema_envelope(key))
7
- end
8
- end
9
- end
10
- end
@@ -1,15 +0,0 @@
1
- module Textus
2
- class CLI
3
- class SchemaDiff < Verb
4
- prepend DeprecatedAliasMixin
5
-
6
- def self.deprecated_name = "schema-diff"
7
- def self.replacement_path = "schema diff"
8
-
9
- def call(store)
10
- name = positional.shift or raise UsageError.new("schema-diff NAME")
11
- emit(Textus::SchemaTools.diff(store, name: name))
12
- end
13
- end
14
- end
15
- end
@@ -1,33 +0,0 @@
1
- module Textus
2
- class CLI
3
- class SchemaGroup < Group
4
- self.cli_name = "schema"
5
- subcommands["show"] = SchemaVerb
6
- subcommands["init"] = SchemaInit
7
- subcommands["diff"] = SchemaDiff
8
- subcommands["migrate"] = SchemaMigrate
9
-
10
- # Back-compat: `textus schema KEY` (dotted key, no subcommand word).
11
- # If the first positional looks like a dotted key, treat it as `schema show KEY`.
12
- def parse(argv)
13
- first = argv.first
14
- if first && dotted_key?(first)
15
- @stderr.puts(
16
- "textus: 'schema KEY' is deprecated; use 'textus schema show KEY' instead. Removed in 0.6.",
17
- )
18
- argv.unshift("show")
19
- end
20
- super
21
- end
22
-
23
- private
24
-
25
- def dotted_key?(token)
26
- return false if token.start_with?("-")
27
- return false unless token.include?(".")
28
-
29
- token.split(".").all? { |seg| seg.match?(Manifest::KEY_SEGMENT) }
30
- end
31
- end
32
- end
33
- end
@@ -1,19 +0,0 @@
1
- module Textus
2
- class CLI
3
- class SchemaInit < Verb
4
- prepend DeprecatedAliasMixin
5
-
6
- def self.deprecated_name = "schema-init"
7
- def self.replacement_path = "schema init"
8
-
9
- option :from_key, "--from=KEY"
10
-
11
- def call(store)
12
- name = positional.shift or raise UsageError.new("schema-init NAME")
13
- raise UsageError.new("schema-init requires --from=KEY") unless from_key
14
-
15
- emit(Textus::SchemaTools.init(store, name: name, from: from_key))
16
- end
17
- end
18
- end
19
- end
@@ -1,19 +0,0 @@
1
- module Textus
2
- class CLI
3
- class SchemaMigrate < Verb
4
- prepend DeprecatedAliasMixin
5
-
6
- def self.deprecated_name = "schema-migrate"
7
- def self.replacement_path = "schema migrate"
8
-
9
- option :rename, "--rename=O:N"
10
-
11
- def call(store)
12
- name = positional.shift or raise UsageError.new("schema-migrate NAME")
13
- raise UsageError.new("schema-migrate requires --rename=OLD:NEW") unless rename
14
-
15
- emit(Textus::SchemaTools.migrate(store, name: name, rename: rename))
16
- end
17
- end
18
- end
19
- end
@@ -1,12 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Stale < Verb
4
- option :prefix, "--prefix=KEY"
5
- option :zone, "--zone=Z"
6
-
7
- def call(store)
8
- emit(store.stale(prefix: prefix, zone: zone))
9
- end
10
- end
11
- end
12
- end
@@ -1,15 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Uid < Verb
4
- prepend DeprecatedAliasMixin
5
-
6
- def self.deprecated_name = "uid"
7
- def self.replacement_path = "key uid"
8
-
9
- def call(store)
10
- key = positional.shift or raise UsageError.new("uid requires a key")
11
- emit({ "key" => key, "uid" => store.uid(key) })
12
- end
13
- end
14
- end
15
- end
@@ -1,10 +0,0 @@
1
- module Textus
2
- class CLI
3
- class Where < Verb
4
- def call(store)
5
- key = positional.shift or raise UsageError.new("where requires a key")
6
- emit(store.where(key))
7
- end
8
- end
9
- end
10
- end
@@ -1,61 +0,0 @@
1
- module Textus
2
- class ExtensionRegistry
3
- EVENTS = %i[put delete refresh build accept].freeze
4
-
5
- def initialize
6
- @actions = {}
7
- @reducers = {}
8
- @hooks = {}
9
- @doctor_checks = {}
10
- end
11
-
12
- def register_action(name, &blk)
13
- name = name.to_sym
14
- raise UsageError.new("action '#{name}' already registered") if @actions.key?(name)
15
-
16
- @actions[name] = blk
17
- end
18
-
19
- def register_reducer(name, &blk)
20
- name = name.to_sym
21
- raise UsageError.new("reducer '#{name}' already registered") if @reducers.key?(name)
22
-
23
- @reducers[name] = blk
24
- end
25
-
26
- def register_hook(event, name, &blk)
27
- event = event.to_sym
28
- raise UsageError.new("unknown event: #{event}") unless EVENTS.include?(event)
29
-
30
- (@hooks[event] ||= []) << { name: name.to_sym, callable: blk }
31
- end
32
-
33
- def register_doctor_check(name, &blk)
34
- name = name.to_sym
35
- raise UsageError.new("doctor_check '#{name}' already registered") if @doctor_checks.key?(name)
36
-
37
- @doctor_checks[name] = blk
38
- end
39
-
40
- def action(name)
41
- @actions[name.to_sym] or raise UsageError.new("unknown action: #{name}")
42
- end
43
-
44
- def reducer(name)
45
- @reducers[name.to_sym] or raise UsageError.new("unknown reducer: #{name}")
46
- end
47
-
48
- def hooks(event)
49
- @hooks[event.to_sym] || []
50
- end
51
-
52
- def doctor_check(name)
53
- @doctor_checks[name.to_sym] or raise UsageError.new("unknown doctor_check: #{name}")
54
- end
55
-
56
- def action_names = @actions.keys
57
- def reducer_names = @reducers.keys
58
- def hook_events = @hooks.keys
59
- def doctor_check_names = @doctor_checks.keys
60
- end
61
- end