ActiveAndroid icon indicating copy to clipboard operation
ActiveAndroid copied to clipboard

Added hooks for save and delete, allow simple change tracking

Open markuspfeiffer opened this issue 11 years ago • 8 comments

Added two hooks onSaved() and onDeleted(), to allow derived classes to do something useful after being either saved or deleted (e.g. trigger an update of the UI), since save() and delete() methods are final.

Also added methods that allow derived classes to do a simple form of change tracking. A derived class can use markAsChanged() to track if it was changed, the flag is reset once the model is saved. For example, this can be used to only invoke save() on a model if isChanged() returns true.

markuspfeiffer avatar Aug 08 '13 20:08 markuspfeiffer

This was discussed on another issue here, I realy not know why the save and delete methods are final, but if your case is get when the models are edited just go and change the final modifier of the save and/or delete method.

You always can perform a check on the Model.getId() if the model is saved this will not return null and will return the correct ID, I think this merge is just a work around and merge is not required.

leonardoxh avatar Aug 10 '13 01:08 leonardoxh

I think you misunderstand the intention of this change. I do not want to detect whether a model was previously saved or not. I want to perform an action whenever a model is saved (i.e. more than once).

Removing 'final' from save() would be an option, but I think the intention is to prevent implementers from accidentally forgetting to call super.save() in the override. So adding onSaved() which in turn is overridable is imho a better option.

As far as I see it, using "getId() != null" (potentially in combination with polling) is the workaround (and does not work with instances that were loaded from the DB). Providing an override that lets derived classes do 'whatever' whenever a model is saved to the DB is the clean solution imho.

In my case, I trigger an update of the UI by sending a 'changed'-Message from onSaved(), which guarantees that what the UI sees in-memory is consistent with what has been written to the database and allows me to update the UI without having to read from the DB again. I'm sure there are other use cases where such an override would be useful.

markuspfeiffer avatar Aug 10 '13 14:08 markuspfeiffer

I proposed a similar change in #28, but we seem to be the minority.

vonloxley avatar Aug 11 '13 12:08 vonloxley

Just thinking, but if we want hooks for specific actions (insert, update, delete), would it be more elegant to introduce some sort of a callback mechanism? You could then just implement a listener that would perform the desired action and just register it with the library without the need to hardcode your actions into your models.

vsigler avatar Aug 11 '13 15:08 vsigler

Callbacks wouldn't work for my case. Models are created/modified on a background thread and need to notify the UI of changes (via messages). Hooking up the UI to a model's callback doesn't work since you don't know it has been created until you receive a message that it has. Also, the callback might then be invoked from a background thread. However, hooks would allow you to implement callbacks very easily. As far as I see it hooks are more flexible.

However, after further testing I've noticed that callbacks still aren't flexible enough to serve all my needs. I'll remove the final modifiers, as leonardoxh suggested, from save() and delete() to give me full control. For example, a call to delete() can then decide whether to actually delete a row or set a model's "deleted" column to true based on its internal state.

So I guess I'll add my voice to removing the final modifiers instead (sorry vonloxley).

markuspfeiffer avatar Aug 12 '13 07:08 markuspfeiffer

My idea was that this would not be on the model at all. You would have an external callback repository, so you would do something like this:

IModelCallback callback = new IModelCallback<YourModel>() {

  @Override
  public Class<YourModel> getModelClass() { //to know which class should trigger this callback
    return YourModel.class;
  }

  @Override
  public void onBeforeAction(ActionType action, YourModel model) throws InterruptModelOperationException {
     //do whatever before model action happens
     if (ActionType.Save.equals(action) && preventSave) {
        throw new InterruptModelOperationException("YOU SHALL NOT SAVE!");
     }
  }

  @Override
  public void onAfterAction(ActionType action, YourModel model) {
     //do something
  }
};

ModelCallbacks.registerCallback(callback);

Then, upon the operations (save, update, delete, you could even add the "change" operation although you'd have to mark the model "changed" yourself).

Like this, you can also very easily put a Handler into the callbacks, much as you would probably inject one into the model when you are creating it in the background. You could inject and remove the callbacks along with Activity lifecycle.

As for removing final modifiers, I'm also all the way for it, also for the getId() method. Final methods cripple the possibilities to extend quite a bit.

vsigler avatar Aug 12 '13 07:08 vsigler

No need to apologize @markuspfeiffer . But I’d really like to see this resolved, one way or another. @vsigler ’s solution has it’s merits too.

vonloxley avatar Aug 12 '13 14:08 vonloxley

My idea was that this would not be on the model at all. You would have an external callback repository, so you would do something like this:

IModelCallback callback = new IModelCallback<YourModel>() {

  @Override
  public Class<YourModel> getModelClass() { //to know which class should trigger this callback
    return YourModel.class;
  }

  @Override
  public void onBeforeAction(ActionType action, YourModel model) throws InterruptModelOperationException {
     //do whatever before model action happens
     if (ActionType.Save.equals(action) && preventSave) {
        throw new InterruptModelOperationException("YOU SHALL NOT SAVE!");
     }
  }

  @Override
  public void onAfterAction(ActionType action, YourModel model) {
     //do something
  }
};

ModelCallbacks.registerCallback(callback);

Then, upon the operations (save, update, delete, you could even add the "change" operation although you'd have to mark the model "changed" yourself).

Like this, you can also very easily put a Handler into the callbacks, much as you would probably inject one into the model when you are creating it in the background. You could inject and remove the callbacks along with Activity lifecycle.

As for removing final modifiers, I'm also all the way for it, also for the getId() method. Final methods cripple the possibilities to extend quite a bit.

@vsigler in which class we should write your code and it can show/log the error, whether the record is saved or not?

hasnain-ahmad avatar Apr 09 '20 10:04 hasnain-ahmad