Rather than try to introduce you to Meteor myself, I'll simply defer to the meteor web site www.meteor.com and let them do the talking. The site includes a high-level description of the framework, detailed documentation, example code and links to videos. The quality of the description and documentation alone merit your attention - and the capability of Meteor deserves your respect. This stuff is brilliant.
Of all the things Meteor brings to the table, Reactivity is the game changer for me. Updates to data sources cause dependent computations to be re-run, and the application shows updates immediately. The burden of carefully choreographing a dance between the server and the client to display pleasing changes in a timely way has been lifted. The dance is already beautiful and the performers are well-rehearsed. I can focus on building great user experience rather than on things like scratching my head over problems with data structures or working the weird bugs out of ajax callbacks.
Recently, when developing a web app I found myself wanting to use this strategy locally as well, building reactivity to be used only within a client. Though Meteor gives you session-level reactivity that propagates changes made to session variables locally, this wasn't quite what I wanted. Session variables are global to the client; I wanted variables that were at least scoped so I could keep them more closely associated with the functions and objects that use them. I've been conditioned to believe that global variables smell bad and I'm not ready to give up that sense yet.
Instead, I opted to adapt a notion that was mentioned in the Meteor documentation. I associated a variable and a dependency in an object and called it an injective. The idea is that I can inject client values into the reactive flow. In Coffeescript,
Deps.injective = (init, options) -> _value: init ? 0 _dep: new Deps.Dependency _force: !!(options && options.force) ? false set: (value) -> if (@_value != value) || @_force @_value = value @changed() @ get: -> @depend() @_value depend: -> @_dep.depend() @ changed: -> @_dep.changed() @ force: (f) -> @_force = !!f @
_depare managed through the
getreturns the value while making the function asking for it dependent on the injective. Updating a value using
setre-computes all functions that depend upon it.
A quick example perhaps, again in Coffeescript.
App.innerWidth = Deps.injective window.innerWidth $(window).resize -> App.innerWidth.set window.innerWidth
This code creates an injective that tracks the inner width of the browser window. When the window resizes, the injective's value is set to the new inner width. While this might not sound like much, anything that has been computed based on the getting the value of
innerwidthwill be automatically recomputed when it changes. In the context of this example, that means that you don't have to know about the 23 layout decisions you made based on this value and how they'll need to propagate, and then manage it all yourself. Instead, you just grab the corner of the window and resize it, and watch the magic happen.
changedfunctions provide access to the dependency mechanism and are used by
dependis called, the injective adds the calling function to its list of dependents. An update to the injective using
setwill re-run these dependent functions. The
changedfunction is what triggers the dependents to be re-run.
forceis a way to force changes to objects and arrays values to be propagated without having to write any special comparitors. When the value being set is a scalar,
==works: if the value is different it is copied and
changedis called. If the value is an object (or an array) and the change is made within the object itself,
==won't report the object has changed; a more complex comparison must be written to do the test. But that really isn't needed if you know about the complexity already. The
forcefunction (or constructor argument) provides a simple shortcut: if
_forcehas been set to
setfunction trusts that the incoming value is different;
setjust goes ahead and updates the value and propagates the change. If something more complex is needed, values in the object can be adjusted and the injective's
changedfunction can be called directly.