Flotype / now

NowJS makes it easy to build real-time web apps using JavaScript
http://www.nowjs.com
MIT License
1.91k stars 175 forks source link

Feature request: everyone.now.exclude([clientId]).someMethod functionality #50

Closed tommedema closed 13 years ago

tommedema commented 13 years ago

A very common scenario is where you'd want to broadcast to all clients in a group except for the client that sent the initial message.

For example, in a chat application, when a user sends a message he does not need to know from the server that he actually sent this message. Thus, the client can add this message locally. However, the server does have to send this message to all clients except for the originating client. This is just an example.

It would make no sense to copy all clients to a new group except for that originating client, thus I propose the following exclude style:

everyone.now.exclude([clientId1, clientId2]).someMethod

which would work exactly the same as:

everyone.now.someMethod

with the exception that clientId1 and clientId2 are excluded.

Thoughts?

tommedema commented 13 years ago

After further discussion a better syntax might be:

everyone.exclude([clientId]).now.someMethod

or of course the same should work for a sub-group:

someGroup.exclude([clientId]).now.someMethod

Also, please understand that the group functionality is no solution to this problems. Groups are like "persistent selectors", where expensive operations need to be made to move the required users to a new group. The suggested exclude functionality would be meant for selectors that occur for a different id on every call, for example to exclude the client that called the remote method. It would obviousely not be efficient to create a whole new group just for one method and store all those references of all clients except one for every call that is made by every client.

dvv commented 13 years ago

Personally, I see a flaw in the logic in processing a message post locally in the example. A client posting something is one procedure, and the message arrival is another. Matter of taste purely, though.

Still, filtering group members is very fruitful pattern. I only do not understand why only exclude is considered. I'd love to have full set of Array.prototype methods able to act upon groups. Say, someGroup.filter(function(x){return x.now.name.charAt(0) === 'A'}).now.someMethod and alike. I believe internally calling a method for a group does iterate over an array, so honoring array methods could be kinda low-hanging fruit.

tommedema commented 13 years ago

@dvv, I know what you're talking about regarding the chat example. It was merely an example though.

I will give you a new example that does not have this flaw. For example, say client A has a certain resource. The goal of the application is to send this resource to all clients. Because client A already has this resource, it should only be send to client B, C, .... Thus, it should not be resend to client A.

I completely agree with your suggestion that a whole set of selectors would be preferable. I merely suggested the most useful one in my case. I do not know whether it'd be much more challenging to also implement these other selectors though.

ericz commented 13 years ago

This is a good feature. Definitely a common pattern to need to exclude a certain user. We will find a good way to implement this in a feature soon. I'm not set on the syntax yet but I do understand the issue and how groups do not solve this particular issue.

@dvv I'll look into that. Indeed internally it does iterate over an array.

samyakbhuta commented 13 years ago

@ericz,

Would definitely like to see bunch of methods like @dvv said and not just exclude. Thumbs Up for feature request.

A proposal

But, I would go with the nomenclature we all are already familiar with, and now much popularized by jQuery selection API. e.g.

everyone.now(".ferrariFans").someMethod(); // This is same as group calling. Infact .ferrariFans could be name of the group already created.
everyone.now("#anIndividualClient").someMethod(); // to pin down a single client and invoke something on it.
everyone.now("someRegularExpression").someMethod(); // whoooo ! this couldd be icing on the cake. Needless to say how powerful one could be.
everyone.now(":pair").someMethod(); // A pseudo class. Client is assumed to be already paired with another client and method is just meant for that
everyone.now(":flashsocket").someMethod(); // A pseudo class. to all the client who are connect with flashsocket as transport.
... many more

Ofcourse, then it will be necessary to name clients in certain cases. Don't know if we can directly use parts of jQuery selection API and plug it into the now.js code or that would be seperate module written in C :p.

Justification

As now.js gains popularity, these patterns of various ways of managing clients would emerge and be in demand. I guess having a mechanism to address this requirement at this moment would be really great. There are many scenarios where this kind of filtering would be required. I could see many game development use cases here !!

tommedema commented 13 years ago

@samyakbhuta, personally I'd see much more use in an exclusion from all than either of the inclusion patterns you have specified.

I reckon that the exclude pattern is simple and highly efficient, allowing the team to work on more important issues rather than delaying such feature and cause even longer implementation pauses as such extensive selection API would be a considerable time investment.

dvv commented 13 years ago

Given everyone.now is exposed as a list (an array-like object such that one can iterate over its item), one can use the full power of underscore library to transform it.

tommedema commented 13 years ago

@dvv, you're right -- never mind my remarks about time investment.

The filter option is especially interesting.

dvv commented 13 years ago

I just found myself including _ solves many problem of both sides. I haven't digged much in the deep of now.js function call dispatcher, but if it reinvents some logic which can be relayed to _, the latter might worth inclusion as both server and client side dependency (at client chances high that one includes underscore for other purposes). To sum my opinion on the subj: depending on good written and proven libraries is one of virtues of a good project

samyakbhuta commented 13 years ago

@tommedema, my stress is not if we would include or exclude, but have JQuery style selection. One can also use negate operator to decide if they want to exclude or include. e.g ! or ^.

dvv commented 13 years ago

@samyakbhuta: am sorry, but what is the point of borrowing jQuery syntax in now.js deep? Given an arbitrary filtration is supported, writing a shim consisting of a couple of filtering functions which would honor jQuery style would be an easy task.

steveWang commented 13 years ago

Implemented in v0.7. You can do the following, if you really want:

everyone.constructor.prototype.filter = function (func) {
  var toExclude = [];
  for (var i in this.users) {
    if (!func.call(this.users[i])) {
      toExclude.push(i);
    }
  }
  return this.exclude(toExclude);
}
everyone.now.distribute = function (msg) {
  var self = this;
  everyone.filter(function () {
    return this.user.clientId !== self.user.clientId;
  }).now.receive(this.now.name + ": " + msg);
}

That'll essentially just exclude the calling user. A bit contrived, but there are more useful applications of .filter(), of course, such as excluding folks who are additionally members of group X.