class OpenUI::Rails::Actions::Schema
Validates the ‘args` of an action submission against a small, explicit contract. It is intentionally not a general-purpose schema language: it only ensures the shape a host handler relies on, so a malformed payload is rejected before any side effect.
Attributes
optional[R]
required[R]
Public Class Methods
any()
click to toggle source
A schema that accepts any JSON object.
# File lib/openui/rails/actions/schema.rb, line 26 def self.any new(required: {}, optional: {}, allow_extra: true) end
define(required: {}, optional: {}, allow_extra: false)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 30 def self.define(required: {}, optional: {}, allow_extra: false) new(required: required || {}, optional: optional || {}, allow_extra: allow_extra) end
from(value)
click to toggle source
Build a schema from a Hash of required types, an existing Schema or a callable predicate.
# File lib/openui/rails/actions/schema.rb, line 36 def self.from(value) case value when nil then any when Schema, Callable then value when Hash then new(required: value, allow_extra: false) else unless value.respond_to?(:call) raise ConfigurationError, "schema must be a Hash, Schema, or respond to #call" end Callable.new(value) end end
new(required: {}, optional: {}, allow_extra: false)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 52 def initialize(required: {}, optional: {}, allow_extra: false) @required = required @optional = optional @allow_extra = allow_extra end
Public Instance Methods
allow_extra?(= @allow_extra)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 58 def allow_extra? = @allow_extra def validate!(args) raise InvalidActionError, "action arguments must be an object" unless args.is_a?(Hash) values = normalize(args) validate_required(values) validate_optional(values) validate_extras(values.keys) unless allow_extra? args end private def normalize(args) args.each_with_object({}) { |(key, value), memo| memo[key.to_s] = value } end def validate_required(values) required.each do |key, expected| name = key.to_s raise InvalidActionError, "missing required argument #{name}" unless values.key?(name) next if type_matches?(values[name], expected) raise InvalidActionError, "argument #{name} has an invalid value" end end def validate_optional(values) optional.each do |key, expected| name = key.to_s next unless values.key?(name) next if type_matches?(values[name], expected) raise InvalidActionError, "argument #{name} has an invalid value" end end def validate_extras(keys) allowed = (required.keys + optional.keys).map(&:to_s) extra = keys - allowed return if extra.empty? raise InvalidActionError, "unexpected arguments: #{extra.join(", ")}" end def type_matches?(value, expected) case expected when Array then expected.any? { |candidate| type_matches?(value, candidate) } when Regexp then value.is_a?(String) && expected.match?(value) when Module then value.is_a?(expected) else expected.respond_to?(:call) ? expected.call(value) == true : false end end end
normalize(args)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 72 def normalize(args) args.each_with_object({}) { |(key, value), memo| memo[key.to_s] = value } end
type_matches?(value, expected)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 104 def type_matches?(value, expected) case expected when Array then expected.any? { |candidate| type_matches?(value, candidate) } when Regexp then value.is_a?(String) && expected.match?(value) when Module then value.is_a?(expected) else expected.respond_to?(:call) ? expected.call(value) == true : false end end
validate!(args)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 60 def validate!(args) raise InvalidActionError, "action arguments must be an object" unless args.is_a?(Hash) values = normalize(args) validate_required(values) validate_optional(values) validate_extras(values.keys) unless allow_extra? args end
validate_extras(keys)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 96 def validate_extras(keys) allowed = (required.keys + optional.keys).map(&:to_s) extra = keys - allowed return if extra.empty? raise InvalidActionError, "unexpected arguments: #{extra.join(", ")}" end
validate_optional(values)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 86 def validate_optional(values) optional.each do |key, expected| name = key.to_s next unless values.key?(name) next if type_matches?(values[name], expected) raise InvalidActionError, "argument #{name} has an invalid value" end end
validate_required(values)
click to toggle source
# File lib/openui/rails/actions/schema.rb, line 76 def validate_required(values) required.each do |key, expected| name = key.to_s raise InvalidActionError, "missing required argument #{name}" unless values.key?(name) next if type_matches?(values[name], expected) raise InvalidActionError, "argument #{name} has an invalid value" end end