✔️ Custom (block based) validator for `PrimaryNode`s

pull/677/head
Rick Carlino 2018-02-21 16:30:02 -06:00
parent fbdc6469bc
commit 7c3ef8f57f
5 changed files with 50 additions and 25 deletions

View File

@ -68,6 +68,7 @@ module CeleryScript
def validate_node(node)
check_arity(node)
node.args.map { |array| check_arg_validity(*array) }
corpus.validate_node(node)
end
def check_arity(node)
@ -149,7 +150,7 @@ module CeleryScript
end
def run_additional_validations(node, expectation)
corpus.validator(expectation).call(node, TypeCheckError, corpus)
corpus.arg_validator(expectation).call(node, TypeCheckError, corpus)
end
# Calling this method with only one paramter

View File

@ -4,33 +4,31 @@ module CeleryScript
class Corpus
BAD_NODE_NAME = "Can't find validation rules for node "
NO_ARG_SPEC = "CANT FIND ARG SPEC"
NO_NODE_SPEC =
def initialize
@arg_def_list = {}
@node_def_list = {}
@arg_def_list = HashWithIndifferentAccess.new
@node_def_list = HashWithIndifferentAccess.new
end
def fetchArg(name)
@arg_def_list[name.to_sym] or raise NO_ARG_SPEC
end
def arg(arg_name, allowed_values, &blk)
@arg_def_list[arg_name.to_sym] = ArgumentSpecification.new(arg_name,
allowed_values,
blk)
self
@arg_def_list[name] or raise NO_ARG_SPEC
end
def fetchNode(name)
n = @node_def_list[name.to_sym]
n = @node_def_list[name]
n ? n : raise(TypeCheckError, BAD_NODE_NAME + name.to_s)
end
def node(kind, allowed_args, allowed_body_nodes = [], &blk = nil)
@node_def_list[kind.to_sym] = NodeSpecification.new(kind,
allowed_args,
allowed_body_nodes,
blk)
def arg(arg_name, allowed_values, &blk)
@arg_def_list[arg_name] = \
ArgumentSpecification.new(arg_name, allowed_values, blk)
self
end
def node(kind, allowed_args, allowed_body_nodes = [], &blk)
@node_def_list[kind] = \
NodeSpecification.new(kind, allowed_args, allowed_body_nodes, blk)
self
end
@ -49,7 +47,15 @@ module CeleryScript
Array(fetchNode(node.kind).allowed_body_types).map(&:to_sym)
end
def validator(name)
# Grab validator for a fully formed node.
def validate_node(node)
defn = @node_def_list[node.kind] or raise(TypeCheckError,
BAD_NODE_NAME + name.to_s)
defn.additional_validation&.call(node)
end
# Grabs validator for an __ARG__ type.
def arg_validator(name)
fetchArg(name).additional_validation || CeleryScript::NOOP
end

View File

@ -2,12 +2,26 @@
# Eg: Which arguments does it take? Which nodes can be placed in the body field?
module CeleryScript
class NodeSpecification
attr_reader :name, :allowed_args, :allowed_body_types
NOOP = ->(*_) { }
def initialize(name, allowed_args, allowed_body_types)
attr_reader :name,
:allowed_args,
:allowed_body_types,
:additional_validation
def initialize(name, allowed_args, allowed_body_types, additional_validation = NOOP)
@name = name
@allowed_args = allowed_args
@allowed_body_types = allowed_body_types
@additional_validation = additional_validation
end
def as_json(*)
{
"allowed_args" => allowed_args,
"allowed_body_types" => allowed_body_types,
"name" => name,
}
end
end
end

View File

@ -51,10 +51,11 @@ module CeleryScriptSettingsBag
BAD_PACKAGE = '"%s" is not a valid package. Allowed values: %s'
BAD_AXIS = '"%s" is not a valid axis. Allowed values: %s'
BAD_POINTER_ID = "Bad point ID: %s"
BAD_PIN_ID = "Can't find %s with id of %s"
BAD_POINTER_TYPE = '"%s" is not a type of point. Allowed values: %s'
BAD_PIN_TYPE = '"%s" is not a type of pin. Allowed values: %s'
BAD_SPEED = "Speed must be a percentage between 1-100"
PIN_TYPE_MAP = { "Peripheral" => Peripheral, "Sensor" => Sensor }
Corpus = CeleryScript::Corpus
.new
.arg(:_else, [:execute, :nothing])
@ -166,7 +167,11 @@ module CeleryScriptSettingsBag
end
end
.node(:named_pin, [:pin_type, :pin_id]) do |node|
binding.pry
klass = \
PIN_TYPE_MAP[node.args[:pin_type].value] or raise "IMPOSSIBLE"
id = node.args[:pin_id].value
bad_node = !klass.exists?(id)
node.invalidate!(BAD_PIN_ID % [klass, id]) if bad_node
end
.node(:read_peripheral, [:peripheral_id, :pin_mode])
.node(:nothing, [])

View File

@ -116,7 +116,6 @@ describe CeleryScript::Checker do
end
it "Catches bad `pin_type`s in `read_pin`" do
p = FactoryBot.create(:peripheral)
hash[:body] = [
{
kind: "read_pin",
@ -125,13 +124,13 @@ describe CeleryScript::Checker do
label: "pin",
pin_number: {
kind: "named_pin",
args: { pin_type: p.class.name, pin_id: p.id }
args: { pin_type: "Peripheral", pin_id: 900 }
}
}
}
]
chk = CeleryScript::Checker.new(tree, corpus)
expect(chk.valid?).to be false
expect(chk.error.message).to include("not a type of pin")
expect(chk.error.message).to include("Can't find Peripheral with id of 900")
end
end