Bind any collection to SignalR with a single call.

This commit is contained in:
kay.one 2013-05-05 17:33:43 -07:00
parent 87a5dc7869
commit a6aba16902
13 changed files with 106 additions and 26 deletions

View File

@ -121,6 +121,7 @@
<Compile Include="REST\RestResource.cs" /> <Compile Include="REST\RestResource.cs" />
<Compile Include="RootFolders\RootFolderModule.cs" /> <Compile Include="RootFolders\RootFolderModule.cs" />
<Compile Include="RootFolders\RootFolderResource.cs" /> <Compile Include="RootFolders\RootFolderResource.cs" />
<Compile Include="RootFolders\RootFolderConnection.cs" />
<Compile Include="Seasons\SeasonModule.cs" /> <Compile Include="Seasons\SeasonModule.cs" />
<Compile Include="Series\SeriesConnection.cs" /> <Compile Include="Series\SeriesConnection.cs" />
<Compile Include="Series\SeriesResource.cs" /> <Compile Include="Series\SeriesResource.cs" />
@ -144,6 +145,7 @@
<Compile Include="Resolvers\QualityTypesToIntResolver.cs" /> <Compile Include="Resolvers\QualityTypesToIntResolver.cs" />
<Compile Include="Config\SettingsModule.cs" /> <Compile Include="Config\SettingsModule.cs" />
<Compile Include="SignalR\BasicResourceConnection.cs" /> <Compile Include="SignalR\BasicResourceConnection.cs" />
<Compile Include="SignalR\Serializer.cs" />
<Compile Include="SignalR\SignalrDependencyResolver.cs" /> <Compile Include="SignalR\SignalrDependencyResolver.cs" />
<Compile Include="SignalR\NzbDronePersistentConnection.cs" /> <Compile Include="SignalR\NzbDronePersistentConnection.cs" />
<Compile Include="TinyIoCNancyBootstrapper.cs" /> <Compile Include="TinyIoCNancyBootstrapper.cs" />

View File

@ -0,0 +1,13 @@
using NzbDrone.Api.SignalR;
using NzbDrone.Core.RootFolders;
namespace NzbDrone.Api.RootFolders
{
public class RootFolderConnection : BasicResourceConnection<RootFolder>
{
public override string Resource
{
get { return "RootFolder"; }
}
}
}

View File

@ -1,5 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Infrastructure;
using NLog; using NLog;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
@ -14,6 +15,7 @@ namespace NzbDrone.Api.SignalR
{ {
private readonly Logger _logger; private readonly Logger _logger;
public BasicResourceConnection() public BasicResourceConnection()
{ {
_logger = LogManager.GetCurrentClassLogger(); _logger = LogManager.GetCurrentClassLogger();
@ -33,7 +35,8 @@ namespace NzbDrone.Api.SignalR
public void HandleAsync(ModelEvent<T> message) public void HandleAsync(ModelEvent<T> message)
{ {
Connection.Broadcast(message); var context =((ConnectionManager)GlobalHost.ConnectionManager).GetConnection(GetType());
context.Connection.Broadcast(message);
} }
} }
} }

View File

@ -0,0 +1,42 @@
using System;
using System.IO;
using Microsoft.AspNet.SignalR.Json;
namespace NzbDrone.Api.SignalR
{
public class Serializer : IJsonSerializer
{
private readonly Common.IJsonSerializer _nzbDroneSerializer;
private JsonNetSerializer _signalRSerializer;
public Serializer(Common.IJsonSerializer nzbDroneSerializer)
{
_signalRSerializer = new JsonNetSerializer();
_nzbDroneSerializer = nzbDroneSerializer;
}
public void Serialize(object value, TextWriter writer)
{
if (value.GetType().FullName.StartsWith("NzbDrone"))
{
_nzbDroneSerializer.Serialize(value, writer);
}
else
{
_signalRSerializer.Serialize(value, writer);
}
}
public object Parse(string json, Type targetType)
{
if (targetType.FullName.StartsWith("NzbDrone"))
{
return _nzbDroneSerializer.Deserialize(json, targetType);
}
return _signalRSerializer.Parse(json, targetType);
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Json;
using TinyIoC; using TinyIoC;
namespace NzbDrone.Api.SignalR namespace NzbDrone.Api.SignalR
@ -13,6 +14,8 @@ namespace NzbDrone.Api.SignalR
public static void Register(TinyIoCContainer container) public static void Register(TinyIoCContainer container)
{ {
GlobalHost.DependencyResolver = new SignalrDependencyResolver(container); GlobalHost.DependencyResolver = new SignalrDependencyResolver(container);
container.Register<IJsonSerializer, Serializer>().AsSingleton();
} }
private SignalrDependencyResolver(TinyIoCContainer container) private SignalrDependencyResolver(TinyIoCContainer container)

View File

@ -9,6 +9,7 @@ namespace NzbDrone.Common
{ {
T Deserialize<T>(string json) where T : class, new(); T Deserialize<T>(string json) where T : class, new();
string Serialize(object obj); string Serialize(object obj);
void Serialize<TModel>(TModel model, TextWriter textWriter);
void Serialize<TModel>(TModel model, Stream outputStream); void Serialize<TModel>(TModel model, Stream outputStream);
object Deserialize(string json, Type type); object Deserialize(string json, Type type);
} }
@ -55,13 +56,18 @@ namespace NzbDrone.Common
} }
public void Serialize<TModel>(TModel model, Stream outputStream) public void Serialize<TModel>(TModel model, TextWriter outputStream)
{ {
var jsonTextWriter = new JsonTextWriter(new StreamWriter(outputStream)); var jsonTextWriter = new JsonTextWriter(outputStream);
_jsonNetSerializer.Serialize(jsonTextWriter, model); _jsonNetSerializer.Serialize(jsonTextWriter, model);
jsonTextWriter.Flush(); jsonTextWriter.Flush();
} }
public void Serialize<TModel>(TModel model, Stream outputStream)
{
Serialize(model, new StreamWriter(outputStream));
}
} }
} }

View File

@ -23,7 +23,8 @@ namespace NzbDrone.Owin.MiddleWare
{ {
foreach (var nzbDronePersistentConnection in _persistentConnections) foreach (var nzbDronePersistentConnection in _persistentConnections)
{ {
appBuilder.MapConnection("signalr/series", nzbDronePersistentConnection.GetType(), new ConnectionConfiguration { EnableCrossDomain = true }); var url = string.Format("signalr/{0}", nzbDronePersistentConnection.Resource.Trim('/'));
appBuilder.MapConnection(url, nzbDronePersistentConnection.GetType(), new ConnectionConfiguration { EnableCrossDomain = true });
} }
} }

View File

@ -7,6 +7,7 @@
<w>rootdir</w> <w>rootdir</w>
<w>rootfolder</w> <w>rootfolder</w>
<w>rootfolders</w> <w>rootfolders</w>
<w>signalr</w>
<w>thetvdb</w> <w>thetvdb</w>
<w>trakt</w> <w>trakt</w>
<w>tvdb</w> <w>tvdb</w>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="JavaScriptLibraryMappings"> <component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{libraries}" /> <file url="PROJECT" libraries="{jQuery-1.9.1, libraries}" />
</component> </component>
</project> </project>

View File

@ -61,7 +61,7 @@
<option passfail="false" /> <option passfail="false" />
<option white="false" /> <option white="false" />
<option maxerr="50" /> <option maxerr="50" />
<option predef="NzbDrone, define, Backbone, _, window,Handlebars, console,require,$,Marionette, Backgrid, jQuery" /> <option predef="NzbDrone, define, Backbone, _, window,Handlebars, console,require,$,Marionette, Backgrid, jQuery, signalR" />
</component> </component>
</project> </project>

View File

@ -10,14 +10,12 @@
<item url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.pager.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.pager.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/handlebars.runtime.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/handlebars.runtime.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.bootstrap.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.bootstrap.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/backbone.modelbinder.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/backbone.modelbinder.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/backbone.collectionbinder.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/fullcalendar.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/fullcalendar.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/backbone.collectionbinder.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/underscore.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/underscore.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/backbone.marionette.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/backbone.marionette.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/bootstrapSwitch.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/toastr-1.1.5.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/bootstrap.slider.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/bootstrap.slider.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/backbone.marionette.viewswapper.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/backbone.marionette.viewswapper.js" />
<item url="file://$PROJECT_DIR$/JsLibraries/backbone.js" /> <item url="file://$PROJECT_DIR$/JsLibraries/backbone.js" />
@ -26,6 +24,8 @@
</sourceFilesUrls> </sourceFilesUrls>
</properties> </properties>
<CLASSES> <CLASSES>
<root url="file://$PROJECT_DIR$/JsLibraries/bootstrapSwitch.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/toastr-1.1.5.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/bootstrap.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/bootstrap.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/backbone.debug.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/backbone.debug.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/jquery.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/jquery.js" />
@ -34,15 +34,13 @@
<root url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.pager.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.pager.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/handlebars.runtime.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/handlebars.runtime.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.bootstrap.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.bootstrap.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/backbone.modelbinder.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/jquery.tablesorter.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/fullcalendar.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/backbone.modelbinder.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/backbone.collectionbinder.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/backbone.collectionbinder.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/fullcalendar.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/underscore.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/underscore.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/bootstrapSwitch.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/backbone.marionette.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/backbone.marionette.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/bootstrap.slider.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/bootstrap.slider.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/toastr-1.1.5.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/backbone.marionette.viewswapper.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/backbone.marionette.viewswapper.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/backbone.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/backbone.js" />
<root url="file://$PROJECT_DIR$/JsLibraries/backbone.mutators.js" /> <root url="file://$PROJECT_DIR$/JsLibraries/backbone.mutators.js" />

View File

@ -6,5 +6,5 @@ define(['app', 'AddSeries/RootFolders/RootFolderModel'], function () {
model: NzbDrone.AddSeries.RootFolders.RootFolderModel model: NzbDrone.AddSeries.RootFolders.RootFolderModel
}); });
return new rootFolderCollection(); return new rootFolderCollection().BindSignalR();
}); });

View File

@ -1,7 +1,15 @@
"use strict"; "use strict";
(function ($) {
var connection = $.connection('/signalr/series'); Backbone.Collection.prototype.BindSignalR = function (options) {
if (!options || !options.url) {
console.assert(this.url, 'url must be provided or collection must have url');
options = {
url: this.url.replace('api', 'signalr')
};
}
var self = this;
var _getStatus = function (status) { var _getStatus = function (status) {
switch (status) { switch (status) {
@ -19,18 +27,21 @@
}; };
var connection = $.connection(options.url);
connection.stateChanged(function (change) { connection.stateChanged(function (change) {
console.debug('{0} [{1}]'.format(options.url, _getStatus(change.newState)));
console.log('signalR [{0}]'.format(_getStatus(change.newState)));
}); });
connection.received(function (data) { connection.received(function () {
console.log(data); self.fetch();
}); });
connection.error(function (error) { connection.start({ transport: ['longPolling'] });
console.warn(error);
}); return this;
};
connection.start();
})(jQuery);