Last active
December 11, 2015 22:58
-
-
Save EGreg/4673641 to your computer and use it in GitHub Desktop.
Examples of how to use Q
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SERVER SIDE: | |
========================= classes/Chess.php ===================== | |
// MODEL ON SERVER | |
class Chess extends Base_Chess // here's one particular table | |
{ | |
function makeMove($e) | |
{ | |
$this->doSomething(); | |
$this->save(); | |
} | |
} | |
========================= handlers/Chess/move/validate =========== | |
========================= handlers/Chess/move/put ================ | |
========================= handlers/Chess/move/post =============== | |
========================= handlers/Chess/move/delete ============= | |
========================= handlers/Chess/move/response =========== | |
========================= handlers/Chess/move/response/$slotName = | |
// CONTROLLER ON SERVER - STRONGLY ENCOURAGES HTTP REST INTERFACE | |
function Chess_move_post() // here's one particular action & http verb | |
{ | |
// assume validate.php already did what it has to do | |
// so usually we don't have to pollute this with checks | |
$chess->makeMove(); // call model | |
} | |
function Chess_move_response_stream() // here's one particular slot | |
{ | |
return Chess::$cache['stream']; // or any other way to cache useful objects in script | |
} | |
function Chess_game_response_column1() // for a SmartApp app | |
{ | |
$options = array( | |
'something' => 'something we need to send urgently', | |
'why' => 'to avoid more requests' | |
); | |
return Q::tool('Chess/game', $options); | |
} | |
========================= views/Chess/$slotName/foo.mustache ==== | |
<div id="$slotName"> | |
enjoy writing your mustache templates here | |
</div> | |
Client API: | |
Q.req("Module/action", slotNames, callback, options); | |
Server API: | |
request: Server should access $_REQUEST and Q_Request::special($field, $default) | |
response: Fill appropriate slots. In column1, column2 slots, etc. put most dynamic data into tool options. | |
Transport: | |
Typical response would look like this: | |
{ | |
"slots": { | |
"column1": "<div id='Chess_game_tool' class='Q_tool Chess_game_tool' tool-options='{\"foo\": \"bar\"}' />" | |
}, | |
"timestamp": 192380329912.1232 | |
} | |
********************************************************************************** | |
MVC WITH Q SERVER: | |
Client -> Request to PHP -> Dispatch to Controller -> Model | |
-> internal post to node.js -> socket.io -> Client | |
The Request is either AJAX or from Location Bar. | |
HTTP interface is usually REST! | |
********************************************************************************** | |
CLIENT SIDE: | |
========================= web/js/Chess.js ======================= | |
MODULE ON CLIENT: | |
var Chess = Q.plugins.Chess = {}; | |
Chess.staticMethod = function () { | |
// whatever you can think of | |
}; | |
Chess.getMyGame = function (token, callback) { | |
if (!Q.Users.loggedInUser) { | |
return callback(Q.Users.Exception.NotLoggedIn); | |
} | |
Q.Streams.Stream.get(Q.Users.loggedInUser.id, "Chess/Game/"+token, callback); | |
// we automatically use the Q.Cache.local("Streams.Stream.getter") object | |
// as the "central storage" for all the streams we are using on the client | |
// and what's more ... this cache is updated as soon as messages arrive! | |
// so it is almost always up to date, and reflects latest server state. | |
// Most caches become out of date until sync, but thanks to socket.io, | |
// we can update ours when relevant messages are pushed to streams. | |
} | |
// Now let's define some MODELS!!!!! | |
Q.Streams.constructors["Chess/Game"] = "js/Chess/models/Game.js" | |
// Now let's define a TOOL!!!!!! | |
Q.Tool.constructors["Chess/Game"] = "js/Chess/tools/Game.js" | |
return Chess; | |
========================= web/js/Chess/models/Game.js ==================== | |
CLIENT SIDE MODEL!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! :-) | |
Streams.constructors["Chess/Game"] = function () { | |
// In JavascriptMVC, canJS and Backbone, models inherit from can.Model, etc. | |
// In our framework, they inherit from Q.Streams.Stream | |
console.log(this.fields); // the fields of the stream | |
}; | |
// CLIENT SIDE CONTROLLER IS IMPLEMENTED AS METHODS ON TOP OF CLIENT SIDE MODEL, | |
// BECAUSE WE DO NOT PLAN TO CHANGE MODEL UNDERNEATH CONTROLLER LAYER: | |
Streams.constructors["Chess/Game"].move(from, to) { | |
// validate move on client side <---- here is where you would do it | |
this.post({ | |
"type": "Chess/move", | |
"instructions": {from: from, to: to} | |
}); | |
// all access checks, real-time updates to participants, | |
// and subscriptions+notifications are done out of the box by stream | |
}; | |
Streams.constructors["Chess/Game"].onMove = new Q.Event(); | |
Streams.onMessage("Chess/Game", "Chess/move").set(function (err, message) { | |
var data = extractDataFromMessage(message); // whatever it does | |
this.onMove(data); // tell whoever wants to know | |
// NOTICE ... THE MODEL CAN LISTEN FOR MESSAGES FROM ITS STREAM | |
// AND DIGESTS THEM AND TELL WHOEVER WANTS TO KNOW ABOUT "updates" | |
// In MVC, who listens to updates? VIEWS!!! | |
// Tools are VIEWS in that diagram -- so now let's define some tools. | |
}); | |
========================= web/js/Chess/tool/Game.js ==================== | |
CLIENT SIDE VIEWS!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
Q.Tool.constructors["Chess/Game"] = function (prefix, options) { | |
var tool = this; | |
var $template = jQuerySpaghettiCodeToMakeDOM(); | |
var $template2 = niceMustacheTemplate(name, options); | |
Q.addStylesheet("css/Chess.css"); // we need this css for this tool | |
Chess.getMyGame(options.token, function renderTheGame(err, game) { | |
// you see, tool is a view | |
game.onMove.set(function () { | |
// show the move happening on the board | |
}, tool); // AUTOMATICALLY REMOVED WHEN TOOL IS REMOVED!!!! | |
}); | |
// let others know when something happens in this tool | |
this.onClickButton = new Q.Event(); | |
this.onResign = new Q.Event(); | |
// attach events | |
$('button', $template2).click(this.onClickButton.handle); | |
$('.resign', $template2).click(function () { | |
if (!confirm("Really resign?")) return; | |
this.onResign.handle(); | |
}); | |
}; | |
SIMPLE! CLEAN! EVERYTHING IS IN ITS OWN FILE!!!!! | |
IT FITS NOT JUST MVC ON SERVER BUT MVC ON CLIENT. | |
WHAT IS THE PROBLEM??????? | |
FIRST, LOOK IT OVER AGAIN AND REALLY PUT ASIDE YOUR PREJUDICES, | |
REALLY LOOK AT IT AND REALIZE WHAT THE DESIGN IS NOW. | |
THEN WE CAN DISCUSS. :) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
model:
controller:
each visual class contains render, animation etc. methods AND DOM node. F.E. animation means calling jQuery method on figure DOM node - whatever it means. You can think about DOM properties as extra "visualization" model attached to visual element. Controller changes property and some shape on the screen changes.
views:
Logic:
User picks a figure from e2
User drops the figure on e4
As user interraction is local sending a "move" message to other player trigger "move" event. Figure controller knows that
it is other's turn and triggers figure view animation to new cell
ready