Skip to content

Instantly share code, notes, and snippets.

@Jaben
Forked from htuomola/Hubs.tt
Last active January 4, 2016 03:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jaben/8560776 to your computer and use it in GitHub Desktop.
Save Jaben/8560776 to your computer and use it in GitHub Desktop.
Updated to support generics (1 type parameter currently) and inherited types. Setup for SignalR v1.1.3 -- but just change reference to support SignalR v2.0.
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".d.ts" #>
<# /* Update this line to match your version of SignalR */ #>
<#@ assembly name="$(SolutionDir)\packages\Microsoft.AspNet.SignalR.Core.1.1.3\lib\net40\Microsoft.AspNet.SignalR.Core.dll" #>
<# /* Load the current project's DLL to make sure the DefaultHubManager can find things */ #>
<#@ assembly name="$(TargetPath)" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Web" #>
<#@ assembly name="System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" #>
<#@ assembly name="System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" #>
<#@ import namespace="Microsoft.AspNet.SignalR" #>
<#@ import namespace="Microsoft.AspNet.SignalR.Hubs" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Threading.Tasks" #>
<#@ import namespace="System.Xml.Linq" #>
<#
var hubmanager = new DefaultHubManager(new DefaultDependencyResolver());
#>
////////////////////
// available hubs //
////////////////////
//#region available hubs
interface SignalR {
<#
foreach (var hub in hubmanager.GetHubs())
{
#>
/**
* The hub implemented by <#=hub.HubType.FullName #>
*/
<#= FirstCharLowered(hub.Name) #> : <#= hub.HubType.Name #>;
<#
}
#>
}
//#endregion available hubs
///////////////////////
// Service Contracts //
///////////////////////
//#region service contracts
<#
foreach (var hub in hubmanager.GetHubs())
{
var hubType = hub.HubType;
string clientContractName = hubType.Namespace + ".I" + hubType.Name + "Client";
var clientType = hubType.Assembly.GetType(clientContractName);
#>
//#region <#= hub.Name #> hub
interface <#= hubType.Name #> {
/**
* This property lets you send messages to the <#= hub.Name #> hub.
*/
server : <#= hubType.Name #>Server;
/**
* The functions on this property should be replaced if you want to receive messages from the <#= hub.Name #> hub.
*/
client : <#= clientType != null ? (hubType.Name + "Client") : "any" #>;
}
<#
/* Server type definition */
#>
interface <#= hubType.Name #>Server {
<#
foreach (var method in hubmanager.GetHubMethods(hub.Name))
{
var ps = method.Parameters.Select(x => x.Name + ": " + GetTypeContractName(x.ParameterType));
var docs = GetXmlDocForMethod(hubType.GetMethod(method.Name));
#>
/**
* Sends a "<#= FirstCharLowered(method.Name) #>" message to the <#= hub.Name #> hub.
* Contract Documentation: <#= docs.Summary #>
<#
foreach (var p in method.Parameters)
{
#>
* @param <#=p.Name #> {<#=GetTypeContractName(p.ParameterType) #>} <#=docs.ParameterSummary(p.Name) #>
<#
}
#>
* @return {JQueryPromise of <#= GetTypeContractName(method.ReturnType) #>}
*/
<#= FirstCharLowered(method.Name) #>(<#=string.Join(", ", ps) #>): JQueryPromise<<#= GetTypeContractName(method.ReturnType, null, true) #>>;
<#
}
#>
}
<#
/* Client type definition */
#>
<#
if (clientType != null)
{
#>
interface <#= hubType.Name #>Client
{
<#
foreach (var method in clientType.GetMethods())
{
var ps = method.GetParameters().Select(x => x.Name + ": " + GetTypeContractName(x.ParameterType));
var docs = GetXmlDocForMethod(method);
#>
/**
* Set this function with a "function(<#=string.Join(", ", ps) #>){}" to receive the "<#= FirstCharLowered(method.Name) #>" message from the <#= hub.Name #> hub.
* Contract Documentation: <#= docs.Summary #>
<#
foreach (var p in method.GetParameters())
{
#>
* @param <#=p.Name #> {<#=GetTypeContractName(p.ParameterType) #>} <#=docs.ParameterSummary(p.Name) #>
<#
}
#>
* @return {void}
*/
<#= FirstCharLowered(method.Name) #> : (<#=string.Join(", ", ps) #>) => void;
<#
}
#>
}
<#
}
#>
//#endregion <#= hub.Name #> hub
<#
}
#>
//#endregion service contracts
////////////////////
// Data Contracts //
////////////////////
//#region data contracts
<#
while (viewTypes.Count != 0)
{
var type = viewTypes.Pop();
#>
/**
* Data contract for <#= type.FullName #>
*/
interface <#= GenericSpecificName(type) #><#= GetInheritedType(type) #>
{
<#
foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
#>
<#= FirstCharLowered(property.Name) #>?: <#= GetTypeContractName(property.PropertyType, type) #>;
<#
}
#>
}
<#
}
#>
//#endregion data contracts
<#+
private Stack<Type> viewTypes = new Stack<Type>();
private HashSet<Type> doneTypes = new HashSet<Type>();
private string GetInheritedType(Type type)
{
var baseType = type.BaseType;
if (baseType != null && type != baseType && baseType != typeof(object) && !baseType.IsAbstract)
{
return string.Format(" extends {0}", this.GetTypeContractName(baseType));
}
return "";
}
public static TAttribute[] GetAttributesOf<TAttribute>(ICustomAttributeProvider attributeProvider, bool inherited = false)
where TAttribute : Attribute
{
return attributeProvider.GetCustomAttributes(typeof(TAttribute), inherited).Cast<TAttribute>().ToArray();
}
private string GetTypeContractName(Type type, Type parentType = null, bool concreteType = false)
{
if (type == typeof(Task))
{
return "void /*task*/";
}
if (type.IsArray)
{
return GetTypeContractName(type.GetElementType(), parentType, concreteType) + "[]";
}
if (type.IsGenericType && typeof(Task<>).IsAssignableFrom(type.GetGenericTypeDefinition()))
{
return GetTypeContractName(type.GetGenericArguments()[0], parentType, concreteType);
}
if (type.IsGenericType && typeof(Nullable<>).IsAssignableFrom(type.GetGenericTypeDefinition()))
{
return GetTypeContractName(type.GetGenericArguments()[0], parentType, concreteType);
}
if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type.GetGenericTypeDefinition()))
{
return GetTypeContractName(type.GetGenericArguments()[0], parentType, concreteType) + "[]";
}
if (type.BaseType == typeof(Enum))
{
return "number";
}
if (!concreteType && parentType != null)
{
var args = parentType.GetGenericArguments();
if (args.Any() && args.First() == type)
{
return "T";
}
}
switch (type.Name.ToLowerInvariant())
{
case "object":
return "any";
case "datetime":
return "Date";
case "int16":
case "int32":
case "int64":
case "single":
case "double":
case "byte":
return "number";
case "boolean":
return "boolean";
case "void":
case "string":
return type.Name.ToLowerInvariant();
}
if (!doneTypes.Contains(type))
{
doneTypes.Add(type);
viewTypes.Push(type);
}
return GenericSpecificName(type, concreteType);
}
private string GenericSpecificName(Type type, bool concreteType = false)
{
string name = type.Name;
int index = name.IndexOf('`');
name = index == -1 ? name : name.Substring(0, index);
if (type.IsGenericType)
{
if (!concreteType)
{
name += "<T>";
//string.Format("<{0}>",string.Join(",", Enumerable.Range(1, type.GenericTypeArguments.Length).Select(s => string.Format("T{0}", s))));
}
else
{
name += string.Format(
"<{0}>",
string.Join(",", type.GenericTypeArguments.Select(t => GetTypeContractName(t, null, true))));
}
}
if (!(name[0] == 'I' && char.IsUpper(name[1])))
{
name = "I" + name;
}
return name;
}
private string FirstCharLowered(string s)
{
return Regex.Replace(s, "^.", x => x.Value.ToLowerInvariant());
}
private Dictionary<Assembly, XDocument> xmlDocs = new Dictionary<Assembly, XDocument>();
private XDocument XmlDocForAssembly(Assembly a)
{
XDocument value;
if (!xmlDocs.TryGetValue(a, out value))
{
var path = new Uri(a.CodeBase.Replace(".dll", ".xml")).LocalPath;
xmlDocs[a] = value = File.Exists(path) ? XDocument.Load(path) : null;
}
return value;
}
private MethodDocs GetXmlDocForMethod(MethodInfo method)
{
var xmlDocForHub = XmlDocForAssembly(method.DeclaringType.Assembly);
if (xmlDocForHub == null)
{
return new MethodDocs();
}
var methodName = string.Format(
"M:{0}.{1}({2})",
method.DeclaringType.FullName,
method.Name,
string.Join(",", method.GetParameters().Select(x => x.ParameterType.FullName)));
var xElement = xmlDocForHub.Descendants("member").SingleOrDefault(x => (string)x.Attribute("name") == methodName);
return xElement == null ? new MethodDocs() : new MethodDocs(xElement);
}
private class MethodDocs
{
public MethodDocs()
{
Summary = "---";
Parameters = new Dictionary<string, string>();
}
public MethodDocs(XElement xElement)
{
Summary = ((string)xElement.Element("summary") ?? "").Trim();
Parameters = xElement.Elements("param").ToDictionary(x => (string)x.Attribute("name"), x => x.Value);
}
public string Summary { get; set; }
public Dictionary<string, string> Parameters { get; set; }
public string ParameterSummary(string name)
{
if (Parameters.ContainsKey(name))
{
return Parameters[name];
}
return "";
}
}
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment