Skip to content
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

Fluent interface #73

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Fluent interface #73

wants to merge 1 commit into from

Conversation

creynders
Copy link
Contributor

I'm starting to sway towards the idea of having a fluent interface. I didn't deem it necessary at first, but with Context#configure and a few of the new things I'd like to add, a FI seems to make sense.

Something like:

context.wire(Foo)
  .asSingleton('foo')
  .withWiring({
    baz: 'qux'
  })
  .withContextEvents({
    "system:somethingHappened": "doBar"
  });

The thing is, I notice I'm pulling more and more of the Geppetto stuff out of the ordinary actors and into the wiring phase, which means you need more possibilities, which in turn means you want to apply more methods to the same wiring configurations.

@geekdave what do you think?

@creynders creynders changed the title DSL-syntax Fluent interface Sep 24, 2014
@creynders
Copy link
Contributor Author

So, I sketched out a first try with some tests ported from the original API. Amazingly enough I succeeded in making it even backwards compatible, i.e. we have 2 API's now: the "old" one and the fluent one.
However, the code's not that elegant anymore.

We need to ask ourselves whether we want to present users with two API's or one? And if the latter, which one?

@creynders
Copy link
Contributor Author

Ah yes, I didn't include commands yet. Since I didn't quite know how to call the mapping method.
toEvent seems a very weird breaking of symmetry. But on the other hand, commands ARE quite something else, so maybe it's warranted.

E.g.

context.wire(FooCommand)
  .toEvent("system:somethingHappened")

Also, there's some technical difficulties, so I didn't want to start stirring that hornet's nest until I know what you think about the fluent API idea.

@creynders
Copy link
Contributor Author

I do love the legibility though:

context.wire(clazz)
  .asClass(key)
  .withWiring({
    foo : "foo"
  })
  .withContextEvents({
     "event:foo" : "doSomething"
  })
  .withParameters(payload, a, b);

@creynders
Copy link
Contributor Author

After thinking some more about this I'd like to propose the following API:

Level 1

  • wire(subject)

Level 2

  • asSingleton(keys [String|Array<String>])
  • asValue(keys [String|Array<String>])
  • asClass(keys [String|Array<String>])
  • asView(keys [String|Array<String>])
  • asCommand(once [Boolean]) default false. When true the command is released after a single execution.
  • asFactory(keys [String|Array<String>])

Level 3

  • withWiring(wirings [Object|Array]) (all, except values)
  • withListeners(eventListenersMap [Object]) (all, except commands and values)
  • withConfiguration(... [arguments]) (all, except commands, values and views)
  • toContext(keyOrObject [String|Object]) (all, except commands and values)
  • toContextEvents(eventNames [String|Array<String>]) (only commands)

If we want to maintain backwards compatibility we can simply replace the current methods with sugary ones:

function wireSingleton(key, clazz, wiring){
    this.wire(clazz).asSingleton(key).withWiring(wiring);
}

Or release a separate geppetto.migrate module which mixes those methods in.

@creynders
Copy link
Contributor Author

Or we can go the full road:

context.wire(Foo)
  .as.singleton('foo')
  .using.wiring(wiring)
    .and.listeners(listenersMap)
    .and.configuration(a,b,c);

//edit:

Nope, don't like this one.

@creynders creynders mentioned this pull request Oct 8, 2014
@creynders
Copy link
Contributor Author

So, thinking about this some more, if we stick to the idea of having the methods reflect what is returned from the context, we really need to think about/rename:

  • asClass
  • asView

The full explanation of asClass is: "take this object and create a new instance every time it's key is asked for".

On the other hand, maybe we should rethink the entire naming scheme. ATM it looks like the object that is wired itself is used as a singleton instance, which is not the case. I'm having a hard time to come up with an API for that though.

@creynders
Copy link
Contributor Author

Also, some food for thought: in SwiftSuspenders (which both the old API and the new API are based on) there's the notion of providers, i.e. a SingletonProvider, a InstanceProvider et cetera. It allows people to create their own providers and register them with the context. Will need to think on how this could fit into the whole.

@creynders creynders mentioned this pull request Jan 27, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant