Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 5aac980
Showing
24 changed files
with
26,073 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[Oo]bj/ | ||
[Bb]in/ | ||
*.user | ||
/TestResults | ||
*.vspscc | ||
*.vssscc | ||
deploy | ||
deploy/* | ||
*.suo | ||
*.cache | ||
*.docstates | ||
_ReSharper.* | ||
*.csproj.user | ||
*[Rr]e[Ss]harper.user | ||
_ReSharper.*/ | ||
packages/* | ||
artifacts/* | ||
msbuild.log | ||
PublishProfiles/ | ||
*.psess | ||
*.vsp | ||
*.csv |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 11.00 | ||
# Visual Studio 2010 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalR.Reactive", "SignalR.Reactive\SignalR.Reactive.csproj", "{2F4D1483-3A2E-4983-8EA4-75243C6759E4}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{2F4D1483-3A2E-4983-8EA4-75243C6759E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{2F4D1483-3A2E-4983-8EA4-75243C6759E4}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{2F4D1483-3A2E-4983-8EA4-75243C6759E4}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{2F4D1483-3A2E-4983-8EA4-75243C6759E4}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
EndGlobal |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using System; | ||
using System.Linq.Expressions; | ||
using SignalR.Hosting.AspNet; | ||
using SignalR.Hubs; | ||
using SignalR.Infrastructure; | ||
|
||
namespace SignalR.Reactive | ||
{ | ||
public class Clientside<T> | ||
{ | ||
private readonly IObservable<T> _observable; | ||
|
||
internal Clientside(IObservable<T> observable) | ||
{ | ||
_observable = observable; | ||
} | ||
|
||
public IDisposable Observable<THub>(Expression<Func<THub, dynamic>> expression) where THub : Hub, new() | ||
{ | ||
return Observable(expression, null); | ||
} | ||
|
||
public IDisposable Observable<THub>(Expression<Func<THub, dynamic>> expression, string clientName) where THub : Hub, new() | ||
{ | ||
var memberExpression = expression.Body as MemberExpression; | ||
if (memberExpression == null) | ||
{ | ||
throw new ArgumentException("'expression' should be a member expression"); | ||
} | ||
|
||
return Observable<THub>(memberExpression.Member.Name, clientName); | ||
} | ||
|
||
public IDisposable Observable<THub>(string eventName) where THub : Hub, new() | ||
{ | ||
return Observable<THub>(eventName, null); | ||
} | ||
|
||
public IDisposable Observable<THub>(string eventName, string clientName) where THub : Hub, new() | ||
{ | ||
var connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>(); | ||
|
||
dynamic clients = connectionManager.GetClients<THub>(); | ||
clients = string.IsNullOrEmpty(clientName) ? clients : clients[clientName]; | ||
|
||
return _observable.Subscribe( | ||
x => clients.Invoke("subjectOnNext", new { Data = x, EventName = eventName, Type = "onNext" }), | ||
x => clients.Invoke("subjectOnNext", new { Data = x, EventName = eventName, Type = "onError" }), | ||
() => clients.Invoke("subjectOnNext", new { EventName = eventName, Type = "onCompleted" }) | ||
); | ||
} | ||
|
||
} | ||
|
||
public static class SignalRObservableExtensions | ||
{ | ||
public static Clientside<T> ToClientside<T>(this IObservable<T> observable) | ||
{ | ||
return new Clientside<T>(observable); | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
SignalR.Reactive/SignalR.Reactive/DependencyResolverExtensions.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using SignalR.Hubs; | ||
using SignalR.Infrastructure; | ||
|
||
namespace SignalR.Reactive | ||
{ | ||
public static class DependencyResolverExtensions | ||
{ | ||
public static IDependencyResolver EnableRxSupport(this IDependencyResolver dependencyResolver) | ||
{ | ||
var proxyGenerator = new Lazy<RxJsProxyGenerator>(() => new RxJsProxyGenerator(dependencyResolver)); | ||
dependencyResolver.Register(typeof(IJavaScriptProxyGenerator), () => proxyGenerator.Value); | ||
|
||
return dependencyResolver; | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
SignalR.Reactive/SignalR.Reactive/Properties/AssemblyInfo.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
// Allgemeine Informationen über eine Assembly werden über die folgenden | ||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, | ||
// die mit einer Assembly verknüpft sind. | ||
[assembly: AssemblyTitle("SignalR.Reactive")] | ||
[assembly: AssemblyDescription("")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("")] | ||
[assembly: AssemblyProduct("SignalR.Reactive")] | ||
[assembly: AssemblyCopyright("Copyright © 2012")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar | ||
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von | ||
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. | ||
[assembly: ComVisible(false)] | ||
|
||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird | ||
[assembly: Guid("126d9d94-4de9-4550-bea6-f63ecb245569")] | ||
|
||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: | ||
// | ||
// Hauptversion | ||
// Nebenversion | ||
// Buildnummer | ||
// Revision | ||
// | ||
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern | ||
// übernehmen, indem Sie "*" eingeben: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: AssemblyFileVersion("1.0.0.0")] |
211 changes: 211 additions & 0 deletions
211
SignalR.Reactive/SignalR.Reactive/RxJsProxyGenerator.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using SignalR.Hubs; | ||
using SignalR.Infrastructure; | ||
|
||
namespace SignalR.Reactive | ||
{ | ||
class RxJsProxyGenerator | ||
{ | ||
private static readonly Lazy<string> _template = new Lazy<string>(GetTemplate); | ||
private static readonly ConcurrentDictionary<string, string> _scriptCache = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase); | ||
|
||
private const string ScriptResource = "SignalR.Scripts.hubs.js"; | ||
|
||
private readonly IHubLocator _hubLocator; | ||
private readonly IJavaScriptMinifier _javascriptMinifier; | ||
|
||
public RxJsProxyGenerator(IDependencyResolver resolver) : | ||
this(resolver.Resolve<IHubLocator>(), | ||
resolver.Resolve<IJavaScriptMinifier>()) | ||
{ | ||
} | ||
|
||
public RxJsProxyGenerator(IHubLocator hubLocator, IJavaScriptMinifier javascriptMinifier) | ||
{ | ||
_hubLocator = hubLocator; | ||
_javascriptMinifier = javascriptMinifier ?? NullJavaScriptMinifier.Instance; | ||
} | ||
|
||
public bool IsDebuggingEnabled { get; set; } | ||
|
||
public string GenerateProxy(string serviceUrl) | ||
{ | ||
string script; | ||
if (_scriptCache.TryGetValue(serviceUrl, out script)) | ||
{ | ||
return script; | ||
} | ||
|
||
var template = _template.Value; | ||
|
||
script = template.Replace("{serviceUrl}", serviceUrl); | ||
|
||
var hubs = new StringBuilder(); | ||
var first = true; | ||
foreach (var type in _hubLocator.GetHubs()) | ||
{ | ||
if (!first) | ||
{ | ||
hubs.AppendLine(","); | ||
hubs.Append(" "); | ||
} | ||
GenerateType(serviceUrl, hubs, type); | ||
first = false; | ||
} | ||
|
||
script = script.Replace("/*hubs*/", hubs.ToString()); | ||
|
||
if (!IsDebuggingEnabled) | ||
{ | ||
script = _javascriptMinifier.Minify(script); | ||
} | ||
|
||
_scriptCache.TryAdd(serviceUrl, script); | ||
|
||
return script; | ||
} | ||
|
||
private void GenerateType(string serviceUrl, StringBuilder sb, Type type) | ||
{ | ||
// Get public instance methods declared on this type only | ||
var methods = GetMethods(type); | ||
var members = methods.Select(m => m.Name).ToList(); | ||
|
||
// Get observable properties declared on this type | ||
var observableProperties = ReflectionHelper.GetExportedHubObservables(type); | ||
|
||
members.Add("namespace"); | ||
members.Add("ignoreMembers"); | ||
members.Add("callbacks"); | ||
|
||
sb.AppendFormat("{0}: {{", GetHubName(type)).AppendLine(); | ||
sb.AppendFormat(" _: {{").AppendLine(); | ||
sb.AppendFormat(" hubName: '{0}',", type.FullName ?? "null").AppendLine(); | ||
sb.AppendFormat(" ignoreMembers: [{0}],", Commas(members, m => "'" + Json.CamelCase(m) + "'")).AppendLine(); | ||
sb.AppendLine(" connection: function () { return signalR.hub; }"); | ||
sb.AppendFormat(" }}"); | ||
if (methods.Any() || observableProperties.Any()) | ||
{ | ||
sb.Append(",").AppendLine(); | ||
} | ||
else | ||
{ | ||
sb.AppendLine(); | ||
} | ||
|
||
bool firstMethod = true; | ||
|
||
foreach (var method in methods) | ||
{ | ||
if (!firstMethod) | ||
{ | ||
sb.Append(",").AppendLine(); | ||
} | ||
GenerateMethod(serviceUrl, sb, type, method); | ||
firstMethod = false; | ||
} | ||
|
||
if (observableProperties.Any() && methods.Any()) | ||
{ | ||
sb.Append(",").AppendLine(); | ||
} | ||
|
||
bool firstProperty = true; | ||
|
||
foreach (var observableProperty in observableProperties) | ||
{ | ||
if (!firstProperty) | ||
{ | ||
sb.Append(",").AppendLine(); | ||
} | ||
GenerateRxSubject(sb, type, observableProperty); | ||
firstProperty = false; | ||
} | ||
|
||
sb.AppendLine(); | ||
sb.Append(" }"); | ||
} | ||
|
||
protected virtual string GetHubName(Type type) | ||
{ | ||
return ReflectionHelper.GetAttributeValue<HubNameAttribute, string>(type, a => a.HubName) ?? Json.CamelCase(type.Name); | ||
} | ||
|
||
protected virtual IEnumerable<MethodInfo> GetMethods(Type type) | ||
{ | ||
// Pick the overload with the minimum number of arguments | ||
return from method in ReflectionHelper.GetExportedHubMethods(type) | ||
group method by method.Name into overloads | ||
let oload = (from overload in overloads | ||
orderby overload.GetParameters().Length | ||
select overload).FirstOrDefault() | ||
select oload; | ||
} | ||
|
||
private void GenerateMethod(string serviceUrl, StringBuilder sb, Type type, MethodInfo method) | ||
{ | ||
var parameters = method.GetParameters(); | ||
var parameterNames = parameters.Select(p => p.Name).ToList(); | ||
parameterNames.Add("callback"); | ||
sb.AppendLine(); | ||
sb.AppendFormat(" {0}: function ({1}) {{", GetMethodName(method), Commas(parameterNames)).AppendLine(); | ||
sb.AppendFormat(" return serverCall(this, \"{0}\", $.makeArray(arguments));", method.Name).AppendLine(); | ||
sb.Append(" }"); | ||
} | ||
|
||
private void GenerateRxSubject(StringBuilder sb, Type type, PropertyInfo property) | ||
{ | ||
Json.CamelCase(property.Name); | ||
var hubName = Json.CamelCase(type.Name); | ||
sb.AppendFormat(" subject : new Rx.Subject(),"); | ||
sb.AppendLine(); | ||
sb.AppendFormat(" subjectOnNext: function(value) {{ signalR.{0}.subject.onNext(value); }},", hubName); | ||
sb.AppendLine(); | ||
sb.AppendFormat(" getObservable: function (eventName) {{ ").AppendLine(); | ||
sb.AppendFormat(" return Rx.Observable.create(function (obs) {{ ").AppendLine(); | ||
sb.AppendFormat(" var disposable = signalR.{0}.subject ", hubName).AppendLine(); | ||
sb.AppendFormat(" .asObservable() ").AppendLine(); | ||
sb.AppendFormat(" .where(function (x) {{ return x.EventName.toLowerCase() === eventName.toLowerCase(); }}) ").AppendLine(); | ||
sb.AppendFormat(" .subscribe(function (x) {{ ").AppendLine(); | ||
sb.AppendFormat(" if (x.Type === 'onNext') obs.onNext(x.Data); ").AppendLine(); | ||
sb.AppendFormat(" if (x.Type === 'onError') obs.onError(x.Data); ").AppendLine(); | ||
sb.AppendFormat(" if (x.Type === 'onCompleted') obs.onCompleted(); ").AppendLine(); | ||
sb.AppendFormat(" }}); ").AppendLine(); | ||
sb.AppendFormat(" return disposable.dispose; ").AppendLine(); | ||
sb.AppendFormat(" }}); ").AppendLine(); | ||
sb.AppendFormat(" }} ").AppendLine(); | ||
} | ||
|
||
private static string GetMethodName(MethodInfo method) | ||
{ | ||
return ReflectionHelper.GetAttributeValue<HubMethodNameAttribute, string>(method, a => a.MethodName) ?? Json.CamelCase(method.Name); | ||
} | ||
|
||
private static string Commas(IEnumerable<string> values) | ||
{ | ||
return Commas(values, v => v); | ||
} | ||
|
||
private static string Commas<T>(IEnumerable<T> values, Func<T, string> selector) | ||
{ | ||
return String.Join(", ", values.Select(selector)); | ||
} | ||
|
||
private static string GetTemplate() | ||
{ | ||
using (Stream resourceStream = typeof(DefaultJavaScriptProxyGenerator).Assembly.GetManifestResourceStream(ScriptResource)) | ||
{ | ||
using (var reader = new StreamReader(resourceStream)) | ||
{ | ||
return reader.ReadToEnd(); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.