Skip to content

Instantly share code, notes, and snippets.

@ventaur
Created August 22, 2014 22:29
Show Gist options
  • Save ventaur/e9e99e4959356b954784 to your computer and use it in GitHub Desktop.
Save ventaur/e9e99e4959356b954784 to your computer and use it in GitHub Desktop.
Small modifications to Headspring Enumeration and HTML conventions to support drop-downs for them
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using FubuCore.Util;
namespace My.Core.Domain.Models {
[Serializable]
[DebuggerDisplay("{DisplayName} - {Value}")]
public abstract class Enumeration<TEnumeration> : Enumeration<TEnumeration, int>
where TEnumeration : Enumeration<TEnumeration> {
protected Enumeration(int value, string displayName) : base(value, displayName) {}
public static TEnumeration FromInt32(int value) {
return FromValue(value);
}
public static bool TryFromInt32(int listItemValue, out TEnumeration result) {
return TryParse(listItemValue, out result);
}
}
[Serializable]
[DebuggerDisplay("{DisplayName} - {Value}")]
public abstract class Enumeration<TEnumeration, TValue> : IComparable<TEnumeration>, IEquatable<TEnumeration>
where TEnumeration : Enumeration<TEnumeration, TValue>
where TValue : IComparable {
readonly string _displayName;
readonly TValue _value;
private static readonly Lazy<TEnumeration[]> _enumerations = new Lazy<TEnumeration[]>(GetEnumerations);
protected Enumeration(TValue value, string displayName) {
_value = value;
_displayName = displayName;
}
public TValue Value {
get { return _value; }
}
public string DisplayName {
get { return _displayName; }
}
public virtual int CompareTo(TEnumeration other) {
return Value.CompareTo(other.Value);
}
public override sealed string ToString() {
return DisplayName;
}
public static TEnumeration[] GetAll() {
return _enumerations.Value;
}
private static TEnumeration[] GetEnumerations() {
Type enumerationType = typeof(TEnumeration);
return enumerationType
.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
.Where(info => enumerationType.IsAssignableFrom(info.FieldType))
.Select(info => info.GetValue(null))
.Cast<TEnumeration>()
.ToArray();
}
public static IEnumerable<string> GetAllDisplayNames() {
return GetAll().Select(enumeration => enumeration.DisplayName);
}
public override bool Equals(object obj) {
return Equals(obj as TEnumeration);
}
public bool Equals(TEnumeration other) {
return other != null && Value.Equals(other.Value);
}
public override int GetHashCode() {
return Value.GetHashCode();
}
public static bool operator ==(Enumeration<TEnumeration, TValue> left, Enumeration<TEnumeration, TValue> right) {
return Equals(left, right);
}
public static bool operator !=(Enumeration<TEnumeration, TValue> left, Enumeration<TEnumeration, TValue> right) {
return !Equals(left, right);
}
public static TEnumeration FromValue(TValue value) {
return Parse(value, "value", item => item.Value.Equals(value));
}
public static TEnumeration Parse(string displayName) {
return Parse(displayName, "display name", item => item.DisplayName == displayName);
}
static bool TryParse(Func<TEnumeration, bool> predicate, out TEnumeration result) {
result = GetAll().FirstOrDefault(predicate);
return result != null;
}
private static TEnumeration Parse(object value, string description, Func<TEnumeration, bool> predicate) {
TEnumeration result;
if (!TryParse(predicate, out result)) {
string message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(TEnumeration));
throw new ArgumentException(message, "value");
}
return result;
}
public static bool TryParse(TValue value, out TEnumeration result) {
return TryParse(e => e.Value.Equals(value), out result);
}
public static bool TryParse(string displayName, out TEnumeration result) {
return TryParse(e => e.DisplayName == displayName, out result);
}
}
public static class EnumerationReflectionHelper {
private static readonly Cache<Type, MethodInfo> _parseMethodCache = new Cache<Type, MethodInfo>(GetParseMethod);
public static object ParseForType(Type enumerationType, string displayName) {
MethodInfo parseMethod = _parseMethodCache[enumerationType];
object value = parseMethod.Invoke(null, new object[] { displayName });
return value;
}
private static MethodInfo GetParseMethod(Type propertyType) {
MethodInfo parseMethod = propertyType.GetMethod(
"Parse",
BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy,
null,
new[] { typeof(string) },
null);
return parseMethod;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using FubuCore;
using FubuCore.Reflection;
using FubuCore.Util;
using FubuMVC.Core.UI.Elements;
using HtmlTags;
using HtmlTags.Conventions;
using My.Core.Domain.Models;
using My.Website.Conventions.Attributes;
namespace My.Website.Conventions {
public class EnumerationDropdownBuilder : IElementBuilderPolicy {
public const string DefaultPromptText = "[ Select one ]";
private static readonly Cache<Type, IEnumerable<string>> _displayNamesByType = new Cache<Type, IEnumerable<string>>(GetDisplayNamesForType);
public bool Matches(ElementRequest subject) {
Type propertyType = subject.Accessor.PropertyType;
return propertyType.Closes(typeof(Enumeration<,>));
}
public ITagBuilder<ElementRequest> BuilderFor(ElementRequest subject) {
return new LambdaTagBuilder<ElementRequest>(request => new SelectTag(tag => BuildOptions(request, tag)));
}
private static void BuildOptions(ElementRequest request, SelectTag tag) {
bool showPrompt = request.Accessor.HasAttribute<PromptAttribute>();
if (showPrompt) {
var promptAttribute = request.Accessor.GetAttribute<PromptAttribute>();
tag.Option(promptAttribute.Text ?? DefaultPromptText, string.Empty);
}
foreach (string displayName in _displayNamesByType[request.Accessor.PropertyType]) {
tag.Option(displayName, displayName);
}
string requestValue = request.StringValue();
tag.SelectByValue(requestValue);
}
private static IEnumerable<string> GetDisplayNamesForType(Type enumerationType) {
var getAllDisplayNamesMethod = enumerationType.GetMethod("GetAllDisplayNames", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
var displayNames = getAllDisplayNamesMethod.Invoke(null, null) as IEnumerable<string> ?? Enumerable.Empty<string>();
return displayNames;
}
}
}
using System;
namespace My.Website.Conventions.Attributes {
/// <summary>
/// Used to adorn properties that are represented as a drop-down/select list to indicate a prompt option/item should be shown.
/// </summary>
public class PromptAttribute : Attribute {
public string Text { get; set; }
public PromptAttribute(string text = null) {
Text = text;
}
}
}
using System;
using System.Collections.Generic;
using FubuCore;
using FubuMVC.Core.UI;
using FubuMVC.Core.UI.Elements;
using HtmlTags;
namespace My.Website.Conventions {
public class SiteHtmlConventions : HtmlConventionRegistry {
public SiteHtmlConventions() {
Editors.BuilderPolicy<EnumerationDropdownBuilder>();
// ...
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment