Last active
January 30, 2020 02:53
-
-
Save andrewabest/1df91aef9116488480ad8df4d90e8fa0 to your computer and use it in GitHub Desktop.
MustConformToOneOf - Strongly Typed Conventions spike
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
void Main() | |
{ | |
typeof(NonConforming).MustConformToOneOf(new PropertyConventionSpecification[] { | |
new PropertiesMustHavePrivateSettersConventionSpecification(), | |
new PropertiesMustHavePublicSettersConventionSpecification() | |
}); | |
} | |
public class NonConforming | |
{ | |
public string Name {get;set;} | |
public string PrivateName {get;private set;} | |
public string ProtectedName {get;protected set;} | |
} | |
public static class PropertyConventionConformist | |
{ | |
public static PropertyConventionResult[] MustConformToOneOf(this Type type, PropertyConventionSpecification[] conventionSpecifications) | |
{ | |
var results = conventionSpecifications.Select(x => x.IsSatisfiedBy(type)).ToArray(); | |
if (results.All(r => r.IsSatisfied)) | |
{ | |
return results; | |
} | |
var groupedFailures = results.SelectMany(r => r.Failures).GroupBy(k => k.Subject); | |
if (groupedFailures.Any(g => g.Count() == conventionSpecifications.Count())) | |
{ | |
"Fail Whale".Dump(); | |
} | |
else | |
{ | |
"Success".Dump(); | |
} | |
return results; | |
} | |
} | |
public class PropertiesMustHavePrivateSettersConventionSpecification : PropertyConventionSpecification | |
{ | |
protected override string FailureMessage => "All properties must have private setters"; | |
protected override PropertyInfo[] GetNonConformingProperties(Type type) | |
{ | |
return type.GetDeclaredProperties() | |
.Where(subject => subject.CanWrite == false || subject.GetSetMethod(true).IsPrivate == false) | |
.ToArray(); | |
} | |
} | |
public class PropertiesMustHavePublicSettersConventionSpecification : PropertyConventionSpecification | |
{ | |
protected override string FailureMessage => "All properties must have public setters"; | |
protected override PropertyInfo[] GetNonConformingProperties(Type type) | |
{ | |
return type.GetDeclaredProperties() | |
.Where(subject => subject.CanWrite == false || subject.GetSetMethod(true).IsPublic == false) | |
.ToArray(); | |
} | |
} | |
public abstract class PropertyConventionSpecification : ConventionSpecification<Type, PropertyConventionResult> | |
{ | |
protected abstract PropertyInfo[] GetNonConformingProperties(Type type); | |
public override PropertyConventionResult IsSatisfiedBy(Type type) | |
{ | |
var failures = GetNonConformingProperties(type); | |
if (failures.Any()) | |
{ | |
var failureMessage = | |
BuildFailureMessage(failures.Aggregate(string.Empty, | |
(s, info) => s + "\t- " + info.Name + Environment.NewLine)); | |
return PropertyConventionResult.NotSatisfied(type, failures.Select(f => new Failure<PropertyInfo>(f, "failed")).ToArray()); | |
} | |
return PropertyConventionResult.Satisfied(type); | |
} | |
} | |
public abstract class ConventionSpecification<TSubject, TResult> : IConventionSpecification<TSubject, TResult> where TResult : IHaveSubject<TSubject> | |
{ | |
protected abstract string FailureMessage { get; } | |
protected string BuildFailureMessage(string details) | |
{ | |
return FailureMessage + | |
Environment.NewLine + | |
details; | |
} | |
public abstract TResult IsSatisfiedBy(TSubject type); | |
} | |
public interface IConventionSpecification<TSubject, TResult> where TResult : IHaveSubject<TSubject> | |
{ | |
TResult IsSatisfiedBy(TSubject type); | |
} | |
public class PropertyConventionResult : ConventionResult<Type, PropertyInfo> | |
{ | |
public PropertyConventionResult(Type subject) : base(subject) | |
{ | |
} | |
public static PropertyConventionResult Satisfied(Type subject) | |
{ | |
return new PropertyConventionResult(subject) { IsSatisfied = true }; | |
} | |
public static PropertyConventionResult NotSatisfied(Type subject, Failure<PropertyInfo>[] failures) | |
{ | |
return new PropertyConventionResult(subject) { Failures = failures }; | |
} | |
} | |
public class GenericConventionResult : ConventionResult<string, string> | |
{ | |
public GenericConventionResult(string subject) : base(subject) | |
{ | |
} | |
} | |
// Define other methods and classes here | |
public class ConventionResult<TSubject, TFailure> : IHaveSubject<TSubject> | |
{ | |
public ConventionResult(TSubject subject) | |
{ | |
Subject = subject; | |
Failures = new Failure<TFailure>[0]; | |
} | |
public TSubject Subject { get; } | |
public bool IsSatisfied { get; protected set; } | |
public IReadOnlyCollection<Failure<TFailure>> Failures { get; protected set; } | |
} | |
public interface IHaveSubject<T> | |
{ | |
T Subject { get; } | |
} | |
public class Failure<T> | |
{ | |
public Failure(T subject, string message) | |
{ | |
Subject = subject; | |
Message = message; | |
} | |
public T Subject {get;} | |
public string Message {get;} | |
} | |
public static class TypeExtensions | |
{ | |
internal static PropertyInfo[] GetDeclaredProperties(this Type type) | |
{ | |
return type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | | |
BindingFlags.Public); | |
} | |
internal static bool IsGenericImplementation(this Type type) | |
{ | |
return (type.BaseType != null && type.BaseType.IsGenericType) || | |
type.GetInterfaces().Any(i => i.IsGenericType); | |
} | |
public static IEnumerable<Type> WhereTypes(this IEnumerable<Assembly> assemblies, Func<Type, bool> predicate) | |
{ | |
var types = assemblies.SelectMany(x => x.GetExportedTypes()).Where(predicate).ToArray(); | |
return types; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment