Skip to content

add closure observer #916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: 2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/masoniteorm/models/Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,18 @@ def boot(self):
f"{type(self).__name__} must specify either __fillable__ or __guarded__ properties, but not both."
)

self.booted()
self._booted = True
self.observe_events(self, "booted")

self.append_passthrough(list(self.get_builder()._macros.keys()))

def booted(self):
"""
Perform any actions required after the model boots
"""
pass

def append_passthrough(self, passthrough):
self.__passthrough__.update(passthrough)
return self
Expand Down
49 changes: 49 additions & 0 deletions src/masoniteorm/observers/ObservesEvents.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from types import SimpleNamespace


class ObservesEvents:
def observe_events(self, model, event):
if model.__has_events__ == True:
Expand Down Expand Up @@ -25,3 +28,49 @@ def with_events(cls):
"""Sets __has_events__ attribute on model to True."""
cls.__has_events__ = True
return cls

@classmethod
def creating(cls, callback):
cls._register_model_event('creating', callback)

@classmethod
def created(cls, callback):
cls._register_model_event('created', callback)

@classmethod
def deleting(cls, callback):
cls._register_model_event('deleting', callback)

@classmethod
def deleted(cls, callback):
cls._register_model_event('deleted', callback)

@classmethod
def hydrating(cls, callback):
cls._register_model_event('hydrating', callback)

@classmethod
def hydrated(cls, callback):
cls._register_model_event('hydrated', callback)

@classmethod
def saving(cls, callback):
cls._register_model_event('saving', callback)

@classmethod
def saved(cls, callback):
cls._register_model_event('saved', callback)

@classmethod
def updating(cls, callback):
cls._register_model_event('updating', callback)

@classmethod
def updated(cls, callback):
cls._register_model_event('updated', callback)

@classmethod
def _register_model_event(cls, event, callback):
anon_observer = SimpleNamespace()
anon_observer.__setattr__(event, callback)
cls.observe(anon_observer)
49 changes: 49 additions & 0 deletions tests/sqlite/models/test_observers.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,52 @@ def test_hydrating_is_observed(self):
self.assertEqual(TestM.observed_hydrating, 1)
self.assertEqual(TestM.observed_hydrated, 1)
DB.rollback("dev")

def test_model_can_observe_callback(self):
events = {
"creating": False,
"created": False,
"deleting": False,
"deleted": False,
"hydrating": False,
"hydrated": False,
"saving": False,
"saved": False,
"updating": False,
"updated": False,
}
class ModelWithCallbacksObserver(Model):
def booted(cls):
cls.creating(lambda m: events.update({"creating": True}))
cls.created(lambda m: events.update({"created": True}))
cls.deleting(lambda m: events.update({"deleting": True}))
cls.deleted(lambda m: events.update({"deleted": True}))
cls.hydrating(lambda m: events.update({"hydrating": True}))
cls.hydrated(lambda m: events.update({"hydrated": True}))
cls.saving(lambda m: events.update({"saving": True}))
cls.saved(lambda m: events.update({"saved": True}))
cls.updating(lambda m: events.update({"updating": True}))
cls.updated(lambda m: events.update({"updated": True}))

model = ModelWithCallbacksObserver()
for event in events:
model.observe_events(model, event)

for event, is_called in events.items():
self.assertTrue(is_called)

def test_model_can_observe_two_callbacks_on_same_event(self):
creating_called_num = 0
def callback(_):
nonlocal creating_called_num
creating_called_num += 1

class ModelWithCallbacksObserver(Model):
def booted(cls):
cls.creating(callback)
cls.creating(callback)

model = ModelWithCallbacksObserver()
model.observe_events(model, 'creating')

self.assertEqual(2, creating_called_num)