ember-data-actions
ember-data-actions copied to clipboard
actions supporting HTTP method types
Example
export default DS.Model.extend(ResourceActionsMixin, {
// attrs here
revoke: action('revoke', {
method: 'DELETE'
})
});
Used with user.revoke() and firing DELETE /api/users/21/revoke.
Hmm, interesting. I like the options hash, but this makes me think that maybe actions should be their own class perhaps. You can pass arguments to an action at invocation (I'll call these "params" for now):
book.publish({ foo: 'bar' })
But this is different, it's arguments for the action definition itself ("options"). In this case, it's options for the default action, but you could imagine the default action as just a default class (i.e. RESTAction) and the options here are overriding the default settings.
I could even see us auto-discovering actions on the container (i.e. for CLI projects, a folder under app/actions). Not sure if that's overkill though.
I guess the question is how much reuse would there be - would enough actions have enough code in common to make it worthwhile it consolidate into base classes that are extended? Or is each action different enough that the overlap would be relatively small?
Sorry for the braindump, just sorting all this stuff out in my head. Comments welcome :wink:
I think that actions are usually too heavily tied to their models. There might be some exceptions, but those could be defined in utils and reused. Although, at the same time, it seems like it might be a hassle to create custom actions since you'd have to define an action on the resource and on the adapter, to handle methods besides POST.
It'd want to define a couple things on the adapter, if they aren't handled by the defaults, and from there only have to define actions on the individual items, etc.
Maybe a middle road approach would work, something like how ember-validations does custom validators. You can define it inline if you want, or as a separate class.
I'm not sure about exposing adapter options on the model though, now that I think about it. While the convenience is nice, it feels like a leaky abstraction. I'm not sure of a clean way to do this though, off the top of my head.
Maybe have different types of action functions that are importable?
getActionoraction.getpostActionoraction.postputActionoraction.putdeleteActionoraction.delete
But then there are so many methods, that you can't possibly have one for all of them. Maybe from there you have to define your own.
But that still feels like it's coupling to the adapter too much. What if you have a non-RESTful adapter, or even localStorage adapter, where a postAction might not make sense? I could see supplying RESTful actions out-of-the-box, at the adapter layer, but I'm not sure about defining them like that on the model itself.
@davewasmer Yeah, agreed. This is a tough problem, maybe we abstract out the method, and just create actions that use a "verb" from the adapter.
action('revoke', { verb: 'delete' }) where 'delete' is a function (or action) on the adapter, and can do a DELETE ajax request, or it could do localStorage.removeItem(..).
In some sense, that was my goal with having an adapter layer and a model layer definitions for actions. Your adapter action would fire the appropriate ajax request or whatever is needed, and your model action would perform any logic against the in-memory model.
I think perhaps what makes more sense is to provide a base RESTAction definition that is used as the default action, and can be tweaked like this:
DS.Model({
revoke: action('revoke')
});
DS.RESTAdapter.extend({
actions: {
revoke: RESTAction({ verb: 'DELETE' })
}
});
Or something like that.
That looks reasonable.