Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Starting the conversation around Geddy's answer to 'real time'. #164

Merged
merged 31 commits into from

7 participants

@Techwraith
Owner

Real Time MVC with GeddyJS

The goal of this project is to implement a system that integrates Socket.io into Geddy's resources. Developers should have these things available to them on the front-end and the backend:

Front-End

  • Easy way to create, read, update, and delete resources through a real-time pipe
  • Subscribe to resource events for a changes feed: 'create', 'update', 'remove'
  • Subscribe to a specific changes feed for a resource: 'update', 'remove'

Back-End

  • Easily push events from your controllers/models to subscribed clients
  • Automatically route messages from the real-time pipe to the associated actions in controllers
  • Automatically push events from crud actions to subscribed clients

Examples

Back-End

// Custom event pushed to all subscribed clients
user.emit('event', {data: 'goes all clients subscribed to this user'});
geddy.model.User.emit('event', {data: 'goes to all clients subscribed to the resource feed'});

// Save sends an 'update' event to all subscribed clients if it's an update
user.save();

// Save sends a 'create' event to all subscribed clients if it's new
var user = geddy.model.User.create(params);
user.save();

// Remove sends a 'remove' event to all subscribed clients
geddy.model.User.remove(id);

Front-End

// Subscribe to a specific model item's update feed
user.on('update', function (user) {
  // do stuff with the user
});

// Subscribe to a resource's changes feed
geddy.model.User.on('change', function (event, user) {
  // do stuff with event or user
  // event could be 'updated', 'created', or 'deleted'
});

// Subscribe to a resources updated feed
geddy.model.User.on('update', function (user) {
  // do stuff with user
});

// Save automatically pushes up an update or create message
// will be routed to controller#create, or controller#update
user.save(callback);

// Remove automatically pushes up a remove message
// will be routed to controller#remove
user.remove(callback);
@larzconwell

On the front-end the "User:123" is it's ID right?

What do you think about these?

// Subscribe to a specific model item's changes feed
geddy.io.on('User', '123', function(event, user) {
  // Do things with user from events "updated" and "deleted"
});

// Subscribe to a specific model item's update feed
geddy.io.on('User', '123', 'updated', function(user) {
  // Do things with user when updated event occurs
});

// Subscribe to a resource's events feed
geddy.io.on('Users', function(event, user) {
  // Do things with users from any of the following events "updated" "created" "deleted"
});

// Subscribe to a resources updated feed
geddy.io.on('Users', 'updated', function (user) {
  // do stuff with user
});

But we could also use the single string with colons as well, I think both would be awesome!

@Techwraith
Owner
@larzconwell

Okay that's fine with me!

@polotek

Interesting. One question. Are people expected to use geddy.io.on() directly or can they listen to events via the model itself? e.g. geddy.model.User.on('update').

@Techwraith
Owner

@polotek We can definitely implement events on the model itself. That shouldn't be an issue.

@mde
Owner

Okay, after thinking about this a little, I've got a couple of questions:

  1. If you can listen for events on the models themselves (e.g., geddy.model.User), and specific instances (e.g,. myUser), do we really need a global pub/sub message bus with a special DSL for the channel IDs (e.g., `geddy.io.on('User:123:updated', function () {});1)? Do you care about updates to an instance that you don't have a reference to already? If we eliminate the global pub/sub, we wouldn't need to make up this DSL for subscribing to it.

  2. I can understand the CRUD events, but it seems weird that they're not named the same as the methods you call. There are reasons we can't use some of them (e.g., "delete") -- so should we emit both ("remove" and "delete"), or just stick to the method names?

@Techwraith
Owner

@mde We should just stick with the same methods, that was my mistake.

For (1) though, you're right, perhaps we should just open up a new channel for each model you have a reference to. Sending all the data all the time might not be the best choice ;)

@Techwraith
Owner

@mde also, geddy.io == socket.io, so we're not making up a dsl so much as using the one that socket.io provides us.

@mscdex

I'm interested in testing out Geddy for use in a project of mine, but I am also interested in using something like backbone.io. Is it possible to (easily) integrate the two?

@Techwraith
Owner

@mscdex That's pretty close to what this spec is trying to achieve. Hooking it up to a system like backbone should be pretty easy once this is implemented. I'll keep you posted.

@mscdex

It'd also be neat to have some kind of automatic (Backbone) code generation for the client side for models defined in Geddy, since Backbone and Geddy models aren't compatible.

@Techwraith
Owner

I'll think about implementing Backbone models on the front end. Might be fairly easy.

@Techwraith
Owner

I've updated the API a bit to reflect some talks we've had. I'll be starting in on this soon.

@Techwraith
Owner

@mde Does model emit an event when new instances are saved (but not when an previously saved instance is saved)?

@Techwraith
Owner

Alright, I've got the events working on the server side, next up is getting Model to work on the front end and figuring out a way to get the defined properties of the models to be be used on the front end.

@Techwraith
Owner

Alright, I've got the events working on the server side, next up is getting Model to work on the front end and figuring out a way to get the defined properties of the models to be be used on the front end.

@Techwraith
Owner

Alright, I've got the events working on the server side, next up is getting Model to work on the front end and figuring out a way to get the defined properties of the models to be be used on the front end.

@Techwraith
Owner

Alright, I've got the events working on the server side, next up is getting Model to work on the front end and figuring out a way to get the defined properties of the models to be be used on the front end.

@mde, @larzconwell, @MiguelMadero I could use a second pair of eyes on this stuff, can you test it out and see if it looks good to you?

Steps to test:

  1. create an app geddy app test
  2. create a model geddy scaffold item name description
  3. install socket.io npm install socket.io
  4. edit the environment.js file to include socketIo = true;
  5. add socket.io to application.html.ejs and connect:
<script src="/socket.io/socket.io.js"></script>
var socket = io.connect('http://localhost');
  1. start up the app and create, update, and delete some items.
  2. verify that the events are getting sent to the front end

  • {{model}}:save
  • {{model}}:save:{{id}}
  • {{model}}:update
  • {{model}}:update:{{id}}
  • {{model}}:remove
  • {{model}}:remove:{{id}}
@larzconwell

@Techwraith I'll test this when I get done with the utility docs, almost done now.

@Techwraith
Owner
@larzconwell

Okay I'm testing this now, how do I test the events on the front end? {{model}}:save this part, haha

@Techwraith
Owner
socket.on('Item:save', function(data) {
  console.log(data);
});

Then pull up a page (like /items) - then see if it logs it in the console.

@larzconwell

Ah duh... lol thanks.

@larzconwell

Yep it works perfectly fine for me! :D

@Techwraith
Owner

Note to self:

make sure to use "save" and "update" instead of "before", but continue to use "beforeRemove"

@Techwraith
Owner

Looks like I'm going to need to get this figured out before I can go any further here: geddy/model#10

@Techwraith
Owner

Come to think of it, we need two helper functions:

  • makePublic where you can pass a directory name, and a path and it will make it public for you:
makePublic('app/views/partials', 'partials');
// make anything in 'app/views/partials' public on http://localhost:4000/partials/*
  • concatDir where you can pass a directory name and a file template, and it will concat all the file contents together:
var content = concatDir("app/views/partials", 
        "(function(){geddy.template = geddy.template || {}; geddy.template.{{name}} = '{{content}}';}())");

The first would be useful for this case, and the second would be useful for the model case (and perhaps this partials case too).

@Techwraith Techwraith merged commit 602ecf1 into master
@mscdex

I'm still a bit confused. These commits seem to add the plumbing for the server-side portion, but don't seem to really address the client side? Is there no way to take advantage of a nice, well-tested client-side framework like backbone.js with these latest changes?

@Techwraith
Owner

@mscdex It's possible to hook this stuff up to backbone, but you'll have to write your own backbone.sync function.

This definitely gets your foot into the door though. As it stands, it'll allow you to do things like:

<script>
geddy.model.Thing.on('update', function (thing) {
  // do things with the updated `thing`
});
</script>

I'm still working on docs for this, but if you want to get started, pull down the latest master, install it, and do this:

geddy app -rt test_realtime
cd test_realtime
geddy scaffold -rt thing title description

This will generate an app with realtime enabled and set up a scaffold for things. It'll also set up a realtime pipe for the server to talk to the client with.

@Techwraith
Owner

@mscdex Also, if you'd rather use backbone and hook it up yourself, you can use geddy.io

geddy.io.on('Things:update', function (thing) {
  // do backbone stuff here
});
@Techwraith
Owner

@mscdex Sorry for all the responses, but I keep wanting to add things-

We're definitely going to be working on backbone support in the future. I'm imagining a hybrid approach where you get to use geddy models (so you only have to write your models once), backbone views and router, with geddy's templates (so you don't have to write those twice either).

@mscdex

@Techwraith Yes, the re-use feature is a huge plus for me and is something I've been struggling with in general with node and backbone. What makes it especially difficult for me is that I also want to share some common validation rules with backbone on the client, but then have additional validation rules just for the server when saving to persistent storage like MySQL.

@Techwraith
Owner

@mscdex We haven't solved the server-side only part yet, but we have solved the model sharing part - it works exactly the same on both sides.

We just need to make these models play nicely with backbone.

We'll get there, but if you'd like to see it go faster, I wouldn't mind merging a pull request ;)

@UzEE

Sorry for commenting on an issue which was closed months ago (I'm new to GitHub, and open source in general), but even after going through the whole process, I haven't been able to understand how to the Real-Time features.

The Real-time events generated through scaffolds work perfectly, but I can't figure out how to write custom events. I'm trying to achieve something like this (controller Main.index method):

geddy.io.sockets.on('frontend:mainPageLoaded', function(data) {
    console.log('Caught main page loaded!');
    auctions.forEach(function(auction, index) {
        auction.getProduct(function(err, product) {
            console.log('Emitting ' + product.name + ' loaded!');
            geddy.io.sockets.emit('auction:productLoaded', product);
        });
    });
});

Events are being generated and I can see them on Chrome's web inspector console but I'm unable to handle them. Here's what I'm doing on the frontend page:

<script type="text/javascript">
$(function () {

  var updateProduct = function (product) {
    console.log("Received " + product.name);
  }

  console.log("starting events");
  geddy.socket.on('auction:productLoad', updateProduct);

  console.log('Emitting page loaded!');
  geddy.socket.emit('frontend:mainPageLoaded', {page: 'main'});
  console.log('Emited page loaded!');
});
</script>

The console.log() calls run and I can even see the event handler registered in the geddy.socket.$events collection, but the handler function updateProduct isn't invoked when the events are received on the front end.

I'm obviously doing something wrong and I've actually been trying to figure this out for a couple of days now (I actually found out about geddy.io by studying the source), but I can't figure out what I'm doing wrong. Can someone here point me in the right direction or maybe point me towards a documentation as the one on the website is pretty thin.

Thanks!

@nounch

@UzEE As you have already indicated, your problem is not directly issue-related, but it's great that you are using Geddy and your concerns are welcome wherever you post them.

Looks like you are looking for this Wiki page (read the section "Realtime for existing projects").

Your particular piece of code may not work as you emit the 'auction:productLoaded' event on the server, but react on the 'auction:productLoad' event on the client.

Including socket.io on the client (<script src="/socket.io/socket.io.js"></script>) wouldn't hurt either.

The documentation is not `thin', it's elegant - it covers all the relevant MVC topics/functions/.... Anyway, the Wiki is the place to look for Geddy + realtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 18, 2012
  1. @Techwraith
Commits on Aug 11, 2012
  1. @Techwraith
Commits on Oct 6, 2012
  1. @Techwraith
Commits on Oct 17, 2012
  1. @Techwraith
  2. @Techwraith

    REALTIME: added event listeners for each model, for each event, and p…

    Techwraith authored
    …roxy them down to the front end
  3. @Techwraith
Commits on Oct 19, 2012
  1. @Techwraith
Commits on Oct 23, 2012
  1. @Techwraith
  2. @Techwraith
Commits on Oct 25, 2012
  1. @Techwraith
  2. @Techwraith
  3. @Techwraith
Commits on Oct 27, 2012
  1. @Techwraith
  2. @Techwraith
Commits on Oct 30, 2012
  1. @Techwraith
  2. @Techwraith

    Added socket.io as a dependency, perhaps a bit arrogant, but I'd like…

    Techwraith authored
    … to believe that people will want realtime more often than not.
  3. @Techwraith

    RT-MVC: added build init step, copied socket.io into all realtime app…

    Techwraith authored
    …s on app creation, got the demo in the bag now
  4. @Techwraith
  5. @Techwraith
  6. @Techwraith

    built core.js into public

    Techwraith authored
Commits on Oct 31, 2012
  1. @larzconwell
  2. @Techwraith
Commits on Nov 3, 2012
  1. @Techwraith
  2. @Techwraith
  3. @Techwraith
  4. @Techwraith
  5. @Techwraith
  6. @Techwraith
  7. @Techwraith
  8. @Techwraith

    removed the generated .gitignore because git didn't like it, switched…

    Techwraith authored
    … -r to be -rt in the cli tool
  9. @Techwraith

    Merge branch 'master' into feature/socket.io-mvc

    Techwraith authored
    Conflicts:
    	lib/app.js
    	templates/base/development.js
    	templates/base/environment.js
    	templates/base/production.js
Something went wrong with that request. Please try again.