Make EvMenu nodes-options' 'exec' callbacks able to return a string for dynamic goto replacement.

This commit is contained in:
Griatch 2017-01-14 18:49:29 +01:00
parent a403cc9576
commit f7936df104

View file

@ -71,10 +71,14 @@ menu is immediately exited and the default "look" command is called.
# fallback when no other option matches the user input. # fallback when no other option matches the user input.
'desc': description, # optional description 'desc': description, # optional description
'goto': nodekey, # node to go to when chosen 'goto': nodekey, # node to go to when chosen
'exec': nodekey}, # node or callback to trigger as callback when chosen. 'exec': nodekey}, # node or callback to trigger as callback when chosen. This
# If a node key is given, the node will be executed once # will execute *before* going to the next node. Both node
# but its return values are ignored. If a callable is # and the explicit callback will be called as normal nodes
# given, it must accept one or two args, like any node. # (with caller and/or raw_string args). If the callable/node
# returns a single string (only), this will replace the current
# goto location string in-place. Note that relying to
# much on letting exec assign the goto location can make it
# hard to debug your menu logic.
{...}, ...) {...}, ...)
If key is not given, the option will automatically be identified by If key is not given, the option will automatically be identified by
@ -99,7 +103,9 @@ Example:
def callback1(caller): def callback1(caller):
# this is called when choosing the "testing" option in node1 # this is called when choosing the "testing" option in node1
# (before going to node2). It needs not have return values. # (before going to node2). If it returned a string, say 'node3',
# then the next node would be node3 instead of node2 as specified
# by the normal 'goto' option key above.
caller.msg("Callback called!") caller.msg("Callback called!")
def node2(caller): def node2(caller):
@ -371,7 +377,7 @@ def null_node_formatter(nodetext, optionstext, caller=None):
def evtable_parse_input(menuobject, raw_string, caller): def evtable_parse_input(menuobject, raw_string, caller):
""" """
Processes the user' node inputs. Processes the user's node inputs.
Args: Args:
menuobject (EvMenu): The EvMenu instance menuobject (EvMenu): The EvMenu instance
@ -710,21 +716,44 @@ class EvMenu(object):
def callback_goto(self, callback, goto, raw_string): def callback_goto(self, callback, goto, raw_string):
"""
Call callback and goto in sequence.
Args:
callback (callable or str): Callback to run before goto. If
the callback returns a string, this is used to replace
the `goto` string before going to the next node.
goto (str): The target node to go to next (unless replaced
by `callable`)..
raw_string (str): The original user input.
"""
if callback: if callback:
self.callback(callback, raw_string) # replace goto only if callback returns
goto = self.callback(callback, raw_string) or goto
if goto: if goto:
self.goto(goto, raw_string) self.goto(goto, raw_string)
def callback(self, nodename, raw_string): def callback(self, nodename, raw_string):
""" """
Run a node as a callback. This makes no use of the return Run a function or node as a callback (with the 'exec' option key).
values from the node.
Args: Args:
nodename (str): Name of node. nodename (callable or str): A callable to run as
`callable(caller, raw_string)`, or the Name of an existing
node to run as a callable. This may or may not return
a string.
raw_string (str): The raw default string entered on the raw_string (str): The raw default string entered on the
previous node (only used if the node accepts it as an previous node (only used if the node accepts it as an
argument) argument)
Returns:
new_goto (str or None): A replacement goto location string or
None (no replacement).
Notes:
Relying on exec callbacks to set the goto location is
very powerful but will easily lead to spaghetti structure and
hard-to-trace paths through the menu logic. So be careful with
relying on this.
""" """
if callable(nodename): if callable(nodename):
@ -732,20 +761,23 @@ class EvMenu(object):
try: try:
if len(getargspec(nodename).args) > 1: if len(getargspec(nodename).args) > 1:
# callable accepting raw_string # callable accepting raw_string
nodename(self.caller, raw_string) ret = nodename(self.caller, raw_string)
else: else:
# normal callable, only the caller as arg # normal callable, only the caller as arg
nodename(self.caller) ret = nodename(self.caller)
except Exception: except Exception:
self.caller.msg(_ERR_GENERAL.format(nodename=nodename), self._session) self.caller.msg(_ERR_GENERAL.format(nodename=nodename), self._session)
raise raise
else: else:
# nodename is a string; lookup as node # nodename is a string; lookup as node
try: try:
# execute the node; we make no use of the return values here. # execute the node
self._execute_node(nodename, raw_string) ret = self._execute_node(nodename, raw_string)
except EvMenuError: except EvMenuError:
return return
if isinstance(basestring, ret):
# only return a value if a string (a goto target), ignore all other returns
return ret
def goto(self, nodename, raw_string): def goto(self, nodename, raw_string):
""" """