Skip to content

Instantly share code, notes, and snippets.

@tamaramalysh5991
Created June 3, 2020 07:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tamaramalysh5991/db7dee5d0abdc655919247dcf8d3b2cc to your computer and use it in GitHub Desktop.
Save tamaramalysh5991/db7dee5d0abdc655919247dcf8d3b2cc to your computer and use it in GitHub Desktop.
Command Pattern (Abstract handlers to execute commands)
import re
from abc import abstractmethod
from ..registry import register_handler
__all__ = (
'AbstractCommandHandler',
'CommandPayload',
'AbstractRegexpCommandHandler'
)
class CommandPayload(object):
"""Simple Data Transfer Object for encapsulate command's data
Is simply an object with attributes corresponding to the names of
the data you want to pass around or return
Example:
>>> cmd = CommandPayload(yumber_name="some info", target="some info")
>>> cmd.yumber_name
>>> "some info"
"""
def __init__(self, **kwargs):
"""Define attributes form kwargs"""
self.__dict__.update(kwargs)
class CommandHandlerMetaClass(type):
"""Register command handler classes in a registry"""
def __new__(cls, clsname, bases, attrs):
handler_class = super(CommandHandlerMetaClass, cls).\
__new__(cls, clsname, bases, attrs)
if not clsname.startswith('Abstract'):
register_handler(handler_class)
return handler_class
class AbstractCommandHandler(object, metaclass=CommandHandlerMetaClass):
"""Abstract class for representation command handler
Each handler can process command in message and send answer if
message matches the condition of current handler
"""
def __init__(
self,
phone: str,
msg: str,
media_files=None,
):
"""Define constructor
Args:
phone (str): user phone from sms
msg (str): body of sms -> the command to
be executed if it matches the pattern
media_files: list of media urls from MMS
"""
self.phone = phone
self.msg = msg or ''
self.media_files = media_files or []
@abstractmethod
def check_condition(self, *args, **kwargs):
"""Check the message match command's condition"""
@abstractmethod
def prefetch_required_data(self) -> CommandPayload:
"""Get data needed to process command from message or cache and
encapsulate data to `Command` object
"""
@abstractmethod
def execute_command(self, command: CommandPayload = None, *args, **kwargs):
"""Execute command from message
Main method for handler where command should be processed.
The main method of Handler remains abstract, so that each
specific Handler will define it in its own way.
"""
@classmethod
def handler_name(cls):
"""Get handler name to identify handler in string mode"""
return cls.__name__
def __call__(self, do_check: bool = True, *args, **kwargs):
"""Override __call_ method for execute handler
If Handler correspond its requirements,
the command processing is started.
Args:
do_check: The flag determines whether to perform a conditions check
Used in cases where we directly call the handler and we don't
need to additionally check its conditions
"""
if not do_check or self.check_condition():
return self.execute_command(command=self.prefetch_required_data())
class AbstractRegexpCommandHandler(AbstractCommandHandler):
"""Abstract class of simple regexp command handler
The handler process plain commands the conditions of which can be
described by regexp pattern
Attributes:
* pattern (str): regex pattern to which the command should correspond
"""
pattern = None
def check_condition(self):
"""Check the message match command's pattern"""
return re.search(self.pattern, self.msg, re.IGNORECASE)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment