Why hello there. I’m attempting to create a page that streams stock quotes in (simulated) real-time. It works, but I’m certain it’s not using the best abstractions for Ember.
The full repo is here, but I’ll post some examples below.
Architecture
Streamer
This class is responsible for providing the real-time updates via websockets or long-polling. It’s currently faked, but it should be abstracted to allow any type of connection.
I don’t feel like this should be an Ember.Object
. Maybe an Adapter?
var Streamer = Ember.Object.extend({
onConnect: function(data) {},
onUpdate: function(updates, date) {}, // Client callback
schedule: function() {
return Ember.run.later(this, function() {
// Call `onUpdate` with new data
this.set('timer', this.schedule());
}, 1000);
},
start: function() {
this.set('timer', this.schedule());
},
// ...
});
Route
The route queries the store for the initial set of stock symbols that will be
passed to the Streamer
. It’s also responsible for instantiating the Streamer
and setting the real-time data on the controller.
var StocksRoute = Ember.Route.extend({
model: function() {
return {
symbols: this.store.find('symbol')
};
},
setupController: function(controller, model) {
controller.set('model', {
lastUpdatedDate: null
});
model.symbols.then(function(obj) {
var streamer = Streamer.create({
onUpdate: function(updates, date) {
controller.set('updates', updates);
controller.set('model.lastUpdatedDate', date);
},
symbols: model.symbols.toArray()
});
streamer.start();
});
},
// ...
});
Controller
Finally, the controller receives the data from the route and translates it into
something that can be rendered in the template. It does this by watching the updates
property that is updated by the route. None of the streamed data will be modified by
the controller, so it never needs to update any models.
It feels like regenerating the entire stocksDisplay
property each update is
inefficient. I could probably refactor this to be cleaner.
var StocksController = Ember.ObjectController.extend({
stocks: [], // Set by route
stocksDisplay: function() {
var updates = this.get('updates');
return _.map(this.get('stocks').toArray(), function(stock) {
var update = updates[stock.symbol] || {};
var display = {
name: stock.name,
symbol: stock.symbol,
values: {
ask: {
value: stock.ask,
updated: false
},
// ...
}
};
// Update the values
var props = _.pick(update, 'ask', 'bid', 'price', 'volume');
_.each(props, function(value, key) {
stock[key] = display.values[key].value = value;
display.values[key].updated = true;
});
return display;
});
}.property('updates'),
updates: {}, // Set by route
// ...
});
Questions, summary, etc
- What kind of object should the
Streamer
be? It feels wrong in its current form, but I don’t know how else it should be structured. - Is it appropriate for the route to control the streaming data?
- Should any of this controller logic be placed into a
Ember.View
? - Does my use of properties vs model look correct on the controller?
- Are there any performance concerns with my approach? The Ember Inspector says updating 10 stocks takes ~4ms.
I know that’s a lot to digest. Please ask any clarifying questions if I’ve left anything too ambiguous.
Thanks.