Extracted from http://livsey.org/blog/2013/02/10/integrating-pusher-with-ember/
The Ember Router takes events from user actions and hands them off to the appropriate Route depending on where the user is within the app.
Pusher receives events from your server which your app then handles, but you might want to do different things depending on where your user is within your app at the time the message is received.
Wouldn’t it be great if we could hook these two things up together?
Add this line to your application's Gemfile:
gem 'ember-pusher'
And then execute:
$ bundle
Or install it yourself as:
$ gem install ember-pusher
Here’s what we’re going to end up with in a route:
App.MyRoute = Ember.Route.extend(App.EventPusherActivation, {
pusher_channel: "my-pusher-channel",
// handle event from pusher just like normal actions
events: {
aMessageFromPusher: function(data) {
// do something here
}
}
});
Default channel name is 'event_channel'. Override this via App.EventPusher.pusher_channel = 'my-default-channel
Now in your app.js or wherever you kick-off your app, we can re-open App.Pusher to set the API key:
App.Pusher.reopen({
key: "your-pusher-key"
});
Job done, now any messages received from Pusher will trigger events on your routes and you can handle them just like normal user actions.
Simply require ember-pusher.js
in your Assets manifest:
//= require ember_pusher
To have Rails trigger pusher events when models are created, updated or deleted:
See pusherable or my fork with Mongoid integration at pusherable
We will extend an Ember Store` to auto-update on pusher events sent from the server. We want to execute the following:
didUpdateRecord()
when the record has been updated on the server.didCreateRecord()
, when a new record has been created on the server.didDeleteRecord()
when the record has been deleted on the server
With pusherable
, pusher should send a message of the form (User
record example):
- create:
"User.create", self.to_json
- destroy:
"User.destroy", self.to_json
- update:
"User.update", self.to_json
In the future, we might want to provide bulk operations/events as well for improved performance :)
To achieve this funcitonality, ember-pusher extends Basic store like this, so that we can signal to the Adapter that a record was modified in the given way, using did<Event>Record
without doing a real server sync for that record.
DS.Adapter.reopen({
hasCreatedRecord: function(store, type, record) {
var data = this.mockJSON(type, record);
this.didCreateRecord(store, type, record, data);
},
// ...
});
The store then get some was<Event>
methods mixed in, that eah call the equivalent has<Event>
method on the store adapter.
wasCreated: function(type, id, data) {
var record = this.findTheRecord(type, id);
if (this.isRecord(record)) {
this.get('adapter').hasCreatedRecord(this, type, data);
}
},
See store_pusher.js
for the Ember pusherable client code ;)
App.StorePusher
is automatically registered with every store, similar to how the EventPusher
is connected to all controllers and routes.
For StorePusher, two mixins are provided
- App.StorePusherActivation (activate/deactivate pusher subscription)
- App.StorePusherEventHandler (handle store pusher events on store)
To customize the Pusher channel name:
App.Store.reopen(StorePusherEventHandler, StorePusherActivation, {
pusher_channel: "my-rest-channel"
});
Note: You should be able to override the default (main) store used by the StorePusher like so.
App.MyOtherStorePusher = App.StorePusher.extend({
store: function() {
return this.get("container").lookup("store:other");
}
})
Note that this code has not yet been tested, but I hope the architecture is close to something that would work! Please help out testing, debugging and improving the StorePusher code :) Would be awesome, similar to what we see with the MeteorJS framework!!!
Been tweaking more with the code, looking into ember-data for some guidance... this is way to hardcore/advanced that I can figure out how it needs to be tweaked to work correctly with ember-data. The main problem as I see it, is how to load a record from the store, without having it make a request to the server.
Looks like it is better to do it on the model!
reload()
for updatecreateRecord(json)
for createdeleteRecord
for delete
My issue is here
@darthdeus responded:
For loading records into the store you can just use store.load
or store.loadMany
Hmm.. maybe that would solve it!?
You can retrieve DS.Model instances from the store in several ways. To retrieve
a record for a specific id, use the find()
method:
var record = MyApp.store.find(MyApp.Contact, 123);
By default, the store will talk to your backend using a standard REST mechanism. You can customize how the store talks to your backend by specifying a custom adapter:
MyApp.store = DS.Store.create({
adapter: 'MyApp.CustomAdapter'
});
Looks like this local storage adapter might be useful ;)
Would be nice perhaps with a BrideAdapter, one that saves to localstorage adapter and then to REST adapter. In this bridge adapter, you could then have the option to shut off the REST adapter temporarily, fx using a serverSync: false
flag on key adapter methods?
I have started implementing ember-bridge-adapter as a better solution to handle this.
The key model methods should now take an optional options hash, which is passed around to the most essential functions of adapter, store, transaction etc.
Currently two options are supported syncServer: true
and serverFirst: true
.
The syncServer
option is used to disable syncing with the server.
serverFirst can be set in order to first try finding the record(s) on the server, before trying in the local storage.
Important: BridgeAdapter is still very experimental and needs further testing and improvement!!!
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request