Module: Textus::Protocol::Schema::Tools

Defined in:
lib/textus/protocol/schema/tools.rb

Class Method Summary collapse

Class Method Details

.accept_role_for(store) ⇒ Object

Raises:



92
93
94
95
96
97
98
99
100
# File 'lib/textus/protocol/schema/tools.rb', line 92

def self.accept_role_for(store)
  authority = store.manifest.policy.writers_for("knowledge").first
  return authority if authority

  raise UsageError.new(
    "schema migrate requires a role that can write knowledge; " \
    "none found in the fixed lane table",
  )
end

.diff(store, name:) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/textus/protocol/schema/tools.rb', line 24

def self.diff(store, name:)
  schema = load_schema(store, name)
  drift = []
  store.manifest.resolver.enumerate.each do |row|
    env = pure_get(store, Textus::Value::Role::DEFAULT, row[:key])
    begin
      schema.validate!(env["_meta"])
    rescue SchemaViolation => e
      drift << { "key" => row[:key], "details" => e.details }
    end
  end
  { "protocol" => PROTOCOL, "schema_name" => name, "drift" => drift }
end

.infer_type(value) ⇒ Object



71
72
73
74
75
76
77
78
79
80
# File 'lib/textus/protocol/schema/tools.rb', line 71

def self.infer_type(value)
  case value
  when String  then "string"
  when Numeric then "number"
  when true, false then "boolean"
  when Array   then "array"
  when Hash    then "object"
  else "string"
  end
end

.init(store, name:, from:) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/textus/protocol/schema/tools.rb', line 8

def self.init(store, name:, from:)
  env = pure_get(store, Textus::Value::Role::DEFAULT, from)
  meta = env["_meta"]
  schema = {
    "name" => name,
    "required" => meta.keys,
    "optional" => [],
    "fields" => meta.each_with_object({}) { |(k, v), h| h[k] = { "type" => infer_type(v) } },
  }
  geom = Textus::Protocol::Layout.new(store.root)
  FileUtils.mkdir_p(geom.schemas_dir)
  target = geom.schema_path(name)
  File.write(target, YAML.dump(schema))
  { "protocol" => PROTOCOL, "schema_name" => name, "path" => target }
end

.load_schema(store, name) ⇒ Object



86
87
88
89
90
# File 'lib/textus/protocol/schema/tools.rb', line 86

def self.load_schema(store, name)
  store.schemas.fetch(name)
rescue IoError
  raise UsageError.new("schema not found: #{name}")
end

.migrate(store, name:, rename: nil) ⇒ Object

Raises:



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/textus/protocol/schema/tools.rb', line 38

def self.migrate(store, name:, rename: nil)
  renames =
    if rename
      old_field, new_field = rename.split(":", 2)
      raise UsageError.new("--rename=OLD:NEW") unless old_field && new_field && !new_field.empty?

      { old_field => new_field }
    else
      load_schema(store, name).evolution["migrate_from"] || {}
    end
  raise UsageError.new("schema migrate needs --rename=OLD:NEW or schema.evolution.migrate_from") if renames.empty?

  authority = accept_role_for(store)
  ops = store.with_role(authority)
  touched = []
  store.manifest.resolver.enumerate.each do |row|
    env = pure_get(store, authority, row[:key])
    meta = (env["_meta"] || {}).dup
    changed = false
    renames.each do |old, new|
      if meta.key?(old)
        meta[new] = meta.delete(old)
        changed = true
      end
    end
    next unless changed

    ops.put(key: row[:key], meta: meta, body: env["body"])
    touched << row[:key]
  end
  { "protocol" => PROTOCOL, "migrated" => touched, "renames" => renames }
end

.pure_get(store, role, key) ⇒ Object



82
83
84
# File 'lib/textus/protocol/schema/tools.rb', line 82

def self.pure_get(store, role, key)
  store.with_role(role).get(key: key)
end