Class: SignalWire::Utils::SchemaUtils

Inherits:
Object
  • Object
show all
Defined in:
lib/signalwire/utils/schema_utils.rb

Overview

SchemaUtils — Ruby port of signalwire.utils.schema_utils.SchemaUtils.

Loads the SWML JSON Schema, extracts verb metadata, and validates either a single verb config or a complete SWML document.

Construction rules mirror Python:

- Pass schema_path: nil to use the bundled schema.json.
- schema_validation: false disables validation (validate_verb returns
  true for every call).
- The env var SWML_SKIP_SCHEMA_VALIDATION=1/true/yes also disables
  validation regardless of the constructor argument.

The Ruby port currently ships only the lightweight validator (verb existence + required-property check). Full JSON Schema validation can be wired in via the ‘json_schemer` gem by extending init_full_validator. The lightweight contract matches Python’s _validate_verb_lightweight() exactly.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schema_path = nil, schema_validation = true) ⇒ SchemaUtils

Construct a SchemaUtils.

Parameters:

  • schema_path (String, nil) (defaults to: nil)

    path to a schema.json file; nil for the bundled copy at lib/signalwire/swml/schema.json.

  • schema_validation (Boolean) (defaults to: true)

    enable/disable schema validation.



58
59
60
61
62
63
64
65
66
# File 'lib/signalwire/utils/schema_utils.rb', line 58

def initialize(schema_path = nil, schema_validation = true)
  env_skip = env_boolish(ENV.fetch('SWML_SKIP_SCHEMA_VALIDATION', ''))
  @schema_path = schema_path
  @validation_enabled = schema_validation && !env_skip
  @schema = load_schema
  @verbs = {}
  extract_verbs
  init_full_validator if @validation_enabled && !@schema.empty?
end

Instance Attribute Details

#schemaHash{String=>Object} (readonly)

Returns parsed JSON Schema document.

Returns:

  • (Hash{String=>Object})

    parsed JSON Schema document



48
49
50
# File 'lib/signalwire/utils/schema_utils.rb', line 48

def schema
  @schema
end

#schema_pathString? (readonly)

Returns resolved schema path (nil = embedded default).

Returns:

  • (String, nil)

    resolved schema path (nil = embedded default)



51
52
53
# File 'lib/signalwire/utils/schema_utils.rb', line 51

def schema_path
  @schema_path
end

Instance Method Details

#full_validation_available?Boolean

Whether full JSON Schema validation is wired up. Mirrors Python’s full_validation_available property.

Returns:

  • (Boolean)


70
71
72
# File 'lib/signalwire/utils/schema_utils.rb', line 70

def full_validation_available?
  !@full_validator.nil?
end

#generate_method_body(verb_name) ⇒ Object

Generate a Python-style method body string for a verb. Mirrors Python’s generate_method_body(verb_name).



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/signalwire/utils/schema_utils.rb', line 188

def generate_method_body(verb_name)
  params = get_verb_parameters(verb_name)
  keys = params.keys.sort
  lines = [
    '        # Prepare the configuration',
    '        config = {}'
  ]
  keys.each do |name|
    lines << "        if #{name} is not None:"
    lines << "            config['#{name}'] = #{name}"
  end
  lines << '        # Add any additional parameters from kwargs'
  lines << '        for key, value in kwargs.items():'
  lines << '            if value is not None:'
  lines << '                config[key] = value'
  lines << ''
  lines << "        # Add the #{verb_name} verb"
  lines << "        return self.add_verb('#{verb_name}', config)"
  lines.join("\n")
end

#generate_method_signature(verb_name) ⇒ Object

Generate a Python-style method signature string for a verb. Mirrors Python’s generate_method_signature(verb_name).



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/signalwire/utils/schema_utils.rb', line 159

def generate_method_signature(verb_name)
  params = get_verb_parameters(verb_name)
  required = get_verb_required_properties(verb_name).to_set
  parts = ['self']
  keys = params.keys.sort
  keys.each do |name|
    t = python_type_annotation(params[name])
    parts << if required.include?(name)
               "#{name}: #{t}"
             else
               "#{name}: Optional[#{t}] = None"
             end
  end
  parts << '**kwargs'
  doc = +"\"\"\"\n        Add the #{verb_name} verb to the current document\n        \n"
  keys.each do |name|
    desc = ''
    d = params[name]
    if d.is_a?(Hash) && d['description']
      desc = d['description'].to_s.gsub("\n", ' ').strip
    end
    doc << "        Args:\n            #{name}: #{desc}\n"
  end
  doc << "        \n        Returns:\n            True if the verb was added successfully, False otherwise\n        \"\"\"\n"
  "def #{verb_name}(#{parts.join(', ')}) -> bool:\n#{doc}"
end

#get_all_verb_namesObject

Sorted list of all known verb names. Mirrors Python’s get_all_verb_names().



87
88
89
# File 'lib/signalwire/utils/schema_utils.rb', line 87

def get_all_verb_names
  @verbs.keys.sort
end

#get_verb_parameters(verb_name) ⇒ Object

Parameter-definition block used by code-gen tooling. Mirrors Python’s get_verb_parameters(verb_name).



118
119
120
121
122
123
124
# File 'lib/signalwire/utils/schema_utils.rb', line 118

def get_verb_parameters(verb_name)
  inner = get_verb_properties(verb_name)
  props = inner['properties']
  return {} unless props.is_a?(Hash)

  props
end

#get_verb_properties(verb_name) ⇒ Object

The properties block for a verb, or {} when unknown. Mirrors Python’s get_verb_properties(verb_name).



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/signalwire/utils/schema_utils.rb', line 93

def get_verb_properties(verb_name)
  v = @verbs[verb_name]
  return {} if v.nil?

  outer_props = v['definition']['properties'] rescue nil
  return {} unless outer_props.is_a?(Hash)

  inner = outer_props[verb_name]
  return {} unless inner.is_a?(Hash)

  inner
end

#get_verb_required_properties(verb_name) ⇒ Object

The required list for a verb, or [] when unknown / no required. Mirrors Python’s get_verb_required_properties(verb_name).



108
109
110
111
112
113
114
# File 'lib/signalwire/utils/schema_utils.rb', line 108

def get_verb_required_properties(verb_name)
  inner = get_verb_properties(verb_name)
  req = inner['required']
  return [] unless req.is_a?(Array)

  req.select { |x| x.is_a?(String) }
end

#load_schemaObject

Read and parse the JSON Schema. Mirrors Python’s load_schema().



75
76
77
78
79
80
81
82
83
# File 'lib/signalwire/utils/schema_utils.rb', line 75

def load_schema
  path = @schema_path || default_schema_path
  return {} if path.nil? || !File.exist?(path)

  raw = File.read(path, encoding: 'UTF-8')
  JSON.parse(raw)
rescue JSON::ParserError, Errno::ENOENT
  {}
end

#validate_document(document) ⇒ Object

Validate a complete SWML document. Mirrors Python’s validate_document(document). Returns (false, [‘Schema validator not initialized’]) when no full validator is wired in.



148
149
150
151
152
153
154
155
# File 'lib/signalwire/utils/schema_utils.rb', line 148

def validate_document(document)
  if @full_validator.nil?
    return [false, ['Schema validator not initialized']]
  end

  # Reserved for full-validator wiring.
  [true, []]
end

#validate_verb(verb_name, verb_config) ⇒ Array(Boolean, Array<String>)

Validate a verb config against the schema. Mirrors Python’s validate_verb(verb_name, verb_config).

Returns:

  • (Array(Boolean, Array<String>))

    (valid, errors) tuple.



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/signalwire/utils/schema_utils.rb', line 130

def validate_verb(verb_name, verb_config)
  return [true, []] unless @validation_enabled

  unless @verbs.key?(verb_name)
    return [false, ["Unknown verb: #{verb_name}"]]
  end

  if @full_validator
    validate_verb_full(verb_name, verb_config)
  else
    validate_verb_lightweight(verb_name, verb_config)
  end
end