canjs / can-connect

Model layer utilities for every JavaScript framework! Assemble real-time, high performance, restful data connections.
https://canjs.com/doc/can-connect.html
MIT License
29 stars 16 forks source link

new can-connect interface #304

Open nlundquist opened 7 years ago

nlundquist commented 7 years ago

With the rework currently being done to can-connect, we have an opportunity to redesign the interface to make usage with ES2015 classes easy as well as other ease of use improvements. This issue is intended to capture feedback on what users & other Bitovians would like to see in this new interface.

To kick off discussion we have several examples of potential redesigns:

1. Explicit Usage

let connection = baseConnection({ url: "/todos" }) connection = dataUrl(connection) connection = combineRequests(connection) connection.init();


**2.  Class-based Usage**
- design behaviors to be usable as mixins on ES2015 classes
 - the behaviors could still be implemented to be usable as a plain function (rather than a mixin) on an instance of baseConnection (rather than a connection class constructor)
- connection.init() is called implicitly by the TodoConnection constructor
```js
@combineRequests
@dataUrl
class TodoConnection extends BaseConnection {
}

Todo.connection = new TodoConnection({
  url: "/todos"
})

3. Constructor Extend Usage

Todo.connection = new TodoConnection({ url: "/todos" })



Any comments you might have on the above or any developer experience enhancements you'd like to see in the next interface are welcome!
justinbmeyer commented 7 years ago

To be clear, we'd provide #3 for folks where ES6 trasnpiling isn't available right?

matthewp commented 7 years ago

I'm guessing that combineRequests is a different thing in 2 and 3, right? Because I'm pretty sure the APIs are different. So those would be imported from different places.

Anyways, if you don't want to transpile or use babel plugins you could do it this way and it works in all modern browsers:

class TodoConnection extends combineRequests(dataUrl(BaseConnection)) {

}

Todo.connection = new TodoConnection({
  url: "/todos"
})

presumably this works with no extra effort, as extending classes and function constructors is the same.

nlundquist commented 7 years ago

@justinbmeyer that's right, no. 3 above would be the easier way of making the interface in no. 2 work in situations where transpiling is unavailable. we could plausibly make no. 1 the alternative interface where transpiling is unavailable but that would be harder.

I'd prefer the interface that Matthew suggest above, with classes, but without mixins, though it wouldn't work with any IE prior to Edge. I personally don't have a problem with making that restriction however and requiring ES6 transpiration for older IE.

@matthewp I believe a single behavior module would be able to support the usages in 2 & 3, though perhaps with a slightly different codepath between the usages, but I'd have to experiment to confirm that.

nlundquist commented 7 years ago

In talking to a few Bitovians and external users, I've received unanimous support for approach no. 2.

I've also confirmed that approach no.3 and the approach suggested by Matthew could also be supported by the same behaviors with very little effort.

Going to move ahead with adding support for this API next week, updating our docs to show it as the first class usage, add an easily discoverable explanation of approach no.3 for those not using an ES6 transpiler and creating a guide.

justinbmeyer commented 6 years ago

@nlundquist How would #2 accept the Todo type? As follows?

@combineRequests
@dataUrl
class TodoConnection extends BaseConnection {
}

Todo.connection = new TodoConnection({
  url: "/todos",
  Map: Todo
})
justinbmeyer commented 6 years ago

It feels odd to extend a type without providing methods and then always create a singleton instance with it:

@realtime
class TodoConnection extends CanConnection {

}
new TodoConnection({
    service: todosService,
    Instance: Todo,
    Instances: TodoList
})

I wonder if there's a way to make these connections more useful for testing purpose. Maybe easier to mock-up behaviors?

@realtime
class TodoConnection extends CanConnection {
  Instance= Todo; // Using https://github.com/tc39/proposal-class-fields
  Instances = TodoList;
  Service: TodosService
}

new TodoConnection({
    service: todosService,
    Instance: Todo,
    Instances: TodoList
})
test("you can get data", function(){
  var connection = new TodoConnection({
    service: new FixtureService()
  });

  Todo.getList() //-> uses fixture service 

  connection.break() //-> removes all the `Todo` methods .. maybe restores them to what was there before?
})

An even crazier idea might be for new TodoConnection to actually return proxied versions of the Todo and TodoList constructor functions.

{Instance: Todo, Instances: TodoList} = new TodoConnection( {Instance: Todo, Instances: TodoList, service: mockService} );