Skip to content

Instantly share code, notes, and snippets.

@andrewabest
Last active January 30, 2020 02:53
Show Gist options
  • Select an option

  • Save andrewabest/1df91aef9116488480ad8df4d90e8fa0 to your computer and use it in GitHub Desktop.

Select an option

Save andrewabest/1df91aef9116488480ad8df4d90e8fa0 to your computer and use it in GitHub Desktop.

Revisions

  1. andrewabest revised this gist Jan 30, 2020. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions MustConformToOneOf.cs
    Original file line number Diff line number Diff line change
    @@ -145,6 +145,7 @@ public ConventionResult(TSubject subject)

    public interface IHaveSubject<T>
    {
    T Subject { get; }
    }

    public class Failure<T>
  2. andrewabest created this gist Jan 30, 2020.
    182 changes: 182 additions & 0 deletions MustConformToOneOf.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,182 @@
    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>
    {
    }

    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;
    }
    }