Skip to content

Commit 87272fd

Browse files
Merge pull request #15 from JasperFx/description-model
Description model improvements
2 parents 71d5e4f + a3b623c commit 87272fd

File tree

5 files changed

+151
-12
lines changed

5 files changed

+151
-12
lines changed

src/JasperFx.Core.Tests/Descriptions/reading_descriptions.cs

+30
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,29 @@ public void read_in_description()
105105
theTarget.Name = "Shiner"; // our previous family dog
106106
var description = new OptionsDescription(theTarget);
107107

108+
description.Subject.ShouldBe(theTarget.GetType().FullNameInCode());
109+
108110
description.Properties.Select(x => x.Name)
109111
.ToArray()
110112
.ShouldBe(new string[]{"Name", "IsTrue", "Age", "Color", "Uri", "Duration"});
111113

112114
description.Children["YesThis"].Properties.Select(x => x.Name)
113115
.ShouldHaveTheSameElementsAs("Number", "Suffix");
116+
117+
118+
}
119+
120+
[Fact]
121+
public void read_from_children_that_implement_IDescribeMyself()
122+
{
123+
theTarget.Name = "Shiner"; // our previous family dog
124+
var description = new OptionsDescription(theTarget);
125+
126+
description.Children["DescribedThing"]
127+
.PropertyFor("Foo").Value.ShouldBe("Bar");
114128
}
129+
130+
115131
}
116132

117133
public enum Color
@@ -140,6 +156,20 @@ public class Target
140156
{
141157
Number = 4, Suffix = "Jr"
142158
};
159+
160+
[ChildDescription]
161+
public DescribedThing DescribedThing { get; set; } = new();
162+
}
163+
164+
public class DescribedThing : IDescribeMyself
165+
{
166+
public OptionsDescription ToDescription()
167+
{
168+
var description = new OptionsDescription(this);
169+
description.AddValue("Foo", "Bar");
170+
171+
return description;
172+
}
143173
}
144174

145175
public class Thing
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace JasperFx.Core.Descriptions;
2+
3+
public enum MetricsType
4+
{
5+
Histogram,
6+
Counter,
7+
ObservableGauge
8+
}
9+
10+
public class MetricDescription
11+
{
12+
public string Name { get; }
13+
public MetricsType Type { get; }
14+
15+
public MetricDescription(string name, MetricsType type)
16+
{
17+
Name = name;
18+
Type = type;
19+
}
20+
21+
public string Units { get; set; } = "Number";
22+
23+
public Dictionary<string, string> Tags = new();
24+
}

src/JasperFx.Core/Descriptions/OptionsDescription.cs

+70-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
namespace JasperFx.Core.Descriptions;
44

5+
/// <summary>
6+
/// Just gives an object more control over how it creates an OptionsDescription
7+
/// </summary>
8+
public interface IDescribeMyself
9+
{
10+
OptionsDescription ToDescription();
11+
}
12+
13+
public class OptionSet
14+
{
15+
public string Subject { get; set; }
16+
public string[] SummaryColumns { get; set; } = Array.Empty<string>();
17+
public List<OptionsDescription> Rows { get; set; } = new();
18+
}
19+
520
/// <summary>
621
/// Just a serializable, readonly view of system configuration to be used for diagnostic purposes
722
/// </summary>
@@ -11,6 +26,11 @@ public class OptionsDescription
1126
public List<OptionsValue> Properties { get; set; } = new();
1227

1328
public Dictionary<string, OptionsDescription> Children = new();
29+
30+
/// <summary>
31+
/// Children "sets" of option descriptions
32+
/// </summary>
33+
public Dictionary<string, OptionSet> Sets = new();
1434

1535
// For serialization
1636
public OptionsDescription()
@@ -25,14 +45,17 @@ public OptionsDescription(object subject)
2545
}
2646

2747
var type = subject.GetType();
48+
49+
Subject = type.FullNameInCode();
50+
2851
foreach (var property in type.GetProperties().Where(x => !x.HasAttribute<IgnoreDescriptionAttribute>()))
2952
{
3053
if (property.HasAttribute<ChildDescriptionAttribute>())
3154
{
3255
var child = property.GetValue(subject);
3356
if (child == null) continue;
3457

35-
var childDescription = new OptionsDescription(child);
58+
var childDescription = child is IDescribeMyself describes ? describes.ToDescription() : new OptionsDescription(child);
3659
Children[property.Name] = childDescription;
3760

3861
continue;
@@ -42,4 +65,50 @@ public OptionsDescription(object subject)
4265
Properties.Add(OptionsValue.Read(property, subject));
4366
}
4467
}
68+
69+
public OptionSet AddChildSet(string name)
70+
{
71+
var subject = $"{Subject}.{name}";
72+
var set = new OptionSet { Subject = subject };
73+
Sets[name] = set;
74+
return set;
75+
}
76+
77+
public OptionSet AddChildSet(string name, IEnumerable<object> children)
78+
{
79+
var set = AddChildSet(name);
80+
foreach (var child in children)
81+
{
82+
var description = child is IDescribeMyself describes
83+
? describes.ToDescription()
84+
: new OptionsDescription(child);
85+
86+
set.Rows.Add(description);
87+
}
88+
89+
return set;
90+
}
91+
92+
public OptionsValue AddValue(string name, object value)
93+
{
94+
var subject = $"{Subject}.{name}";
95+
var optionsValue = new OptionsValue(subject, name, value);
96+
Properties.Add(optionsValue);
97+
98+
return optionsValue;
99+
}
100+
101+
/// <summary>
102+
/// Case insensitive search for the first property that matches this name
103+
/// </summary>
104+
/// <param name="name"></param>
105+
/// <returns></returns>
106+
public OptionsValue? PropertyFor(string name)
107+
{
108+
return Properties.FirstOrDefault(x => x.Name.EqualsIgnoreCase(name));
109+
}
110+
111+
public Dictionary<string, string> Tags = new();
112+
113+
public List<MetricDescription> Metrics { get; set; } = new();
45114
}

src/JasperFx.Core/Descriptions/OptionsValue.cs

+26-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ public OptionsValue()
1111
{
1212
}
1313

14+
public OptionsValue(string subject, string name, object rawValue)
15+
{
16+
Subject = subject;
17+
Name = name;
18+
RawValue = rawValue;
19+
Value = rawValue?.ToString();
20+
21+
WriteValue(rawValue);
22+
}
23+
1424
// make this containing object full name.name
1525
// Can be deep? That would cover DurabilitySettings
1626
// use this to tag fancier tooltips
@@ -47,33 +57,39 @@ public static OptionsValue Read(PropertyInfo property, object subject)
4757
Type = PropertyType.Text, // safest guess
4858
};
4959

60+
description.WriteValue(value);
61+
62+
return description;
63+
}
64+
65+
public void WriteValue(object? value)
66+
{
67+
OptionsValue description;
5068
if (value == null)
5169
{
52-
description.Type = PropertyType.None;
53-
description.Value = "None";
70+
Type = PropertyType.None;
71+
Value = "None";
5472
}
5573
else if (value.GetType().IsNumeric())
5674
{
57-
description.Type = PropertyType.Numeric;
75+
Type = PropertyType.Numeric;
5876
}
5977
else if (value.GetType().IsEnum)
6078
{
61-
description.Type = PropertyType.Enum;
79+
Type = PropertyType.Enum;
6280
}
6381
else if (value is bool)
6482
{
65-
description.Type = PropertyType.Boolean;
83+
Type = PropertyType.Boolean;
6684
}
6785
else if (value is Uri)
6886
{
69-
description.Type = PropertyType.Uri;
87+
Type = PropertyType.Uri;
7088
}
7189
else if (value is TimeSpan time)
7290
{
73-
description.Type = PropertyType.TimeSpan;
74-
description.Value = time.ToDisplay();
91+
Type = PropertyType.TimeSpan;
92+
Value = time.ToDisplay();
7593
}
76-
77-
return description;
7894
}
7995
}

src/JasperFx.Core/JasperFx.Core.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Common extension methods and reflection helpers used by JasperFx projects</Description>
5-
<Version>1.10.0</Version>
5+
<Version>1.11.0</Version>
66
<Authors>Jeremy D. Miller</Authors>
77
<AssemblyName>JasperFx.Core</AssemblyName>
88
<PackageId>JasperFx.Core</PackageId>

0 commit comments

Comments
 (0)