Skip to content

Commit 5e049f1

Browse files
committed
Resolve #34 - Add placeholder extension methods API
1 parent 85f5c98 commit 5e049f1

5 files changed

Lines changed: 255 additions & 17 deletions

File tree

CSF.Validation.Tests/Messages/MessageProviderIntegrationTests.cs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,23 +86,18 @@ IMessageProvider GetSut()
8686
var templateProvider = new FailureMessageTemplates();
8787
var provider = new ByIdentityFormatterProvider();
8888

89-
object identity;
90-
PlaceholderMessageFormatter<StubValidatedObject> formatter;
91-
92-
identity = new DefaultManifestIdentity(typeof(StubValidatedObject),
93-
typeof(NumericRangeValueRule),
94-
validatedMember: Reflect.Member<StubValidatedObject>(x => x.IntegerProperty));
95-
formatter = new PlaceholderMessageFormatter<StubValidatedObject>();
96-
formatter.AddPlaceholder("{min}", 5);
97-
formatter.AddPlaceholder("{max}", 20);
98-
provider.RegisteredFormatters.Add(identity, formatter);
99-
100-
identity = new DefaultManifestIdentity(typeof(StubValidatedObject),
101-
typeof(DateTimeRangeValueRule),
102-
validatedMember: Reflect.Member<StubValidatedObject>(x => x.DateTimeProperty));
103-
formatter = new PlaceholderMessageFormatter<StubValidatedObject>();
104-
formatter.AddPlaceholder("{actual}", x => x.DateTimeProperty.Year);
105-
provider.RegisteredFormatters.Add(identity, formatter);
89+
provider.RegisterPlaceholderFormatter<StubValidatedObject>(typeof(NumericRangeValueRule),
90+
x => x.IntegerProperty,
91+
r => {
92+
r.Placeholder("{min}", 5);
93+
r.Placeholder("{max}", 20);
94+
});
95+
96+
provider.RegisterPlaceholderFormatter<StubValidatedObject>(typeof(DateTimeRangeValueRule),
97+
x => x.DateTimeProperty,
98+
r => {
99+
r.Placeholder("{actual}", x => x.DateTimeProperty.Year);
100+
});
106101

107102
return new MessageProvider(templateProvider, provider);
108103
}

CSF.Validation/CSF.Validation.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@
120120
<Compile Include="Messages\ITemplateProvider.cs" />
121121
<Compile Include="Messages\ResourceFileTemplateProvider.cs" />
122122
<Compile Include="Messages\MessageProvider.cs" />
123+
<Compile Include="Messages\PlaceholderRegistrationHelper.cs" />
124+
<Compile Include="Messages\ByIdentityFormatterProviderExtensions.cs" />
123125
</ItemGroup>
124126
<ItemGroup>
125127
<Folder Include="Rules\" />
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
//
2+
// ByIdentityFormatterProviderExtensions.cs
3+
//
4+
// Author:
5+
// Craig Fowler <craig@csf-dev.com>
6+
//
7+
// Copyright (c) 2017 Craig Fowler
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in
17+
// all copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
// THE SOFTWARE.
26+
using System;
27+
using System.Linq.Expressions;
28+
using System.Reflection;
29+
using CSF.Reflection;
30+
using CSF.Validation.Manifest;
31+
32+
namespace CSF.Validation.Messages
33+
{
34+
/// <summary>
35+
/// Extension methods for the <see cref="ByIdentityFormatterProvider"/> type.
36+
/// </summary>
37+
public static class ByIdentityFormatterProviderExtensions
38+
{
39+
/// <summary>
40+
/// Registers a <see cref="T:PlaceholderMessageFormatter{TValidated}"/> with a
41+
/// <see cref="ByIdentityFormatterProvider"/>, using a rule's <see cref="DefaultManifestIdentity"/>.
42+
/// </summary>
43+
/// <remarks>
44+
/// <para>
45+
/// Please note, that if you have changed your default identity generation strategy then you will not be able to
46+
/// use this extension method. It depends upon rules using the <see cref="DefaultManifestIdentity"/> type as
47+
/// their identities.
48+
/// </para>
49+
/// </remarks>
50+
/// <param name="provider">Provider.</param>
51+
/// <param name="ruleType">The rule type.</param>
52+
/// <param name="name">Name.</param>
53+
/// <param name="validatedMember">Validated member.</param>
54+
/// <param name="registration">Registration.</param>
55+
/// <typeparam name="TValidated">The type under validation.</typeparam>
56+
public static void RegisterPlaceholderFormatter<TValidated>(this ByIdentityFormatterProvider provider,
57+
Type ruleType,
58+
string name,
59+
Expression<Func<TValidated,object>> validatedMember,
60+
Action<PlaceholderRegistrationHelper<TValidated>> registration)
61+
where TValidated : class
62+
{
63+
if(ruleType == null)
64+
throw new ArgumentNullException(nameof(ruleType));
65+
if(registration == null)
66+
throw new ArgumentNullException(nameof(registration));
67+
if(provider == null)
68+
throw new ArgumentNullException(nameof(provider));
69+
70+
var helper = GetHelper<TValidated>();
71+
var identity = GetIdentity(typeof(TValidated),
72+
ruleType,
73+
name,
74+
(validatedMember != null)? Reflect.Member(validatedMember) : null);
75+
76+
registration(helper);
77+
78+
provider.RegisteredFormatters.Add(identity, helper.Formatter);
79+
}
80+
81+
/// <summary>
82+
/// Registers a <see cref="T:PlaceholderMessageFormatter{TValidated}"/> with a
83+
/// <see cref="ByIdentityFormatterProvider"/>, using a rule's <see cref="DefaultManifestIdentity"/>.
84+
/// </summary>
85+
/// <remarks>
86+
/// <para>
87+
/// Please note, that if you have changed your default identity generation strategy then you will not be able to
88+
/// use this extension method. It depends upon rules using the <see cref="DefaultManifestIdentity"/> type as
89+
/// their identities.
90+
/// </para>
91+
/// </remarks>
92+
/// <param name="provider">Provider.</param>
93+
/// <param name="ruleType">The rule type.</param>
94+
/// <param name="validatedMember">Validated member.</param>
95+
/// <param name="registration">Registration.</param>
96+
/// <typeparam name="TValidated">The type under validation.</typeparam>
97+
public static void RegisterPlaceholderFormatter<TValidated>(this ByIdentityFormatterProvider provider,
98+
Type ruleType,
99+
Expression<Func<TValidated,object>> validatedMember,
100+
Action<PlaceholderRegistrationHelper<TValidated>> registration)
101+
where TValidated : class
102+
{
103+
RegisterPlaceholderFormatter(provider, ruleType, null, validatedMember, registration);
104+
}
105+
106+
/// <summary>
107+
/// Registers a <see cref="T:PlaceholderMessageFormatter{TValidated}"/> with a
108+
/// <see cref="ByIdentityFormatterProvider"/>, using a rule's <see cref="DefaultManifestIdentity"/>.
109+
/// </summary>
110+
/// <remarks>
111+
/// <para>
112+
/// Please note, that if you have changed your default identity generation strategy then you will not be able to
113+
/// use this extension method. It depends upon rules using the <see cref="DefaultManifestIdentity"/> type as
114+
/// their identities.
115+
/// </para>
116+
/// </remarks>
117+
/// <param name="provider">Provider.</param>
118+
/// <param name="ruleType">The rule type.</param>
119+
/// <param name="registration">Registration.</param>
120+
/// <typeparam name="TValidated">The type under validation.</typeparam>
121+
public static void RegisterPlaceholderFormatter<TValidated>(this ByIdentityFormatterProvider provider,
122+
Type ruleType,
123+
Action<PlaceholderRegistrationHelper<TValidated>> registration)
124+
where TValidated : class
125+
{
126+
RegisterPlaceholderFormatter(provider, ruleType, null, null, registration);
127+
}
128+
129+
/// <summary>
130+
/// Registers a <see cref="T:PlaceholderMessageFormatter{TValidated}"/> with a
131+
/// <see cref="ByIdentityFormatterProvider"/>, using a rule's <see cref="DefaultManifestIdentity"/>.
132+
/// </summary>
133+
/// <remarks>
134+
/// <para>
135+
/// Please note, that if you have changed your default identity generation strategy then you will not be able to
136+
/// use this extension method. It depends upon rules using the <see cref="DefaultManifestIdentity"/> type as
137+
/// their identities.
138+
/// </para>
139+
/// </remarks>
140+
/// <param name="provider">Provider.</param>
141+
/// <param name="ruleType">The rule type.</param>
142+
/// <param name="name">Name.</param>
143+
/// <param name="registration">Registration.</param>
144+
/// <typeparam name="TValidated">The type under validation.</typeparam>
145+
public static void RegisterPlaceholderFormatter<TValidated>(this ByIdentityFormatterProvider provider,
146+
Type ruleType,
147+
string name,
148+
Action<PlaceholderRegistrationHelper<TValidated>> registration)
149+
where TValidated : class
150+
{
151+
RegisterPlaceholderFormatter(provider, ruleType, name, null, registration);
152+
}
153+
154+
static PlaceholderRegistrationHelper<TValidated> GetHelper<TValidated>()
155+
where TValidated : class
156+
{
157+
var formatter = new PlaceholderMessageFormatter<TValidated>();
158+
return new PlaceholderRegistrationHelper<TValidated>(formatter);
159+
}
160+
161+
static DefaultManifestIdentity GetIdentity(Type validatedType,
162+
Type ruleType,
163+
string name,
164+
MemberInfo validatedMember)
165+
{
166+
return new DefaultManifestIdentity(validatedType, ruleType, name, validatedMember);
167+
}
168+
}
169+
}

CSF.Validation/Messages/PlaceholderMessageFormatter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace CSF.Validation.Messages
3333
/// A message formatter which makes placeholder replacements of data into the message template.
3434
/// </summary>
3535
public class PlaceholderMessageFormatter<TValidated> : NoOpFailureMessageFormatter
36+
where TValidated : class
3637
{
3738
readonly IDictionary<string,Func<TValidated,object>> placeholderFillers;
3839

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// PlaceholderRegistrationHelper.cs
3+
//
4+
// Author:
5+
// Craig Fowler <craig@csf-dev.com>
6+
//
7+
// Copyright (c) 2017 Craig Fowler
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in
17+
// all copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
// THE SOFTWARE.
26+
using System;
27+
namespace CSF.Validation.Messages
28+
{
29+
/// <summary>
30+
/// Helper type for registration of placeholders &amp; fillers in a
31+
/// <see cref="T:PlaceholderMessageFormatter{TValidated}"/>.
32+
/// </summary>
33+
public class PlaceholderRegistrationHelper<TValidated> where TValidated : class
34+
{
35+
readonly PlaceholderMessageFormatter<TValidated> formatter;
36+
37+
internal PlaceholderMessageFormatter<TValidated> Formatter => formatter;
38+
39+
/// <summary>
40+
/// Registers a placeholder with the given filler.
41+
/// </summary>
42+
/// <param name="placeholder">Placeholder.</param>
43+
/// <param name="filler">Filler.</param>
44+
public void Placeholder(string placeholder, object filler)
45+
{
46+
Formatter.AddPlaceholder(placeholder, filler);
47+
}
48+
49+
/// <summary>
50+
/// Registers a placeholder with the given filler function.
51+
/// </summary>
52+
/// <param name="placeholder">Placeholder.</param>
53+
/// <param name="filler">Filler.</param>
54+
public void Placeholder(string placeholder, Func<TValidated,object> filler)
55+
{
56+
Formatter.AddPlaceholder(placeholder, filler);
57+
}
58+
59+
/// <summary>
60+
/// Initializes a new instance of the <see cref="T:CSF.Validation.Messages.PlaceholderRegistrationHelper`1"/> class.
61+
/// </summary>
62+
/// <param name="formatter">Formatter.</param>
63+
public PlaceholderRegistrationHelper(PlaceholderMessageFormatter<TValidated> formatter)
64+
{
65+
if(formatter == null)
66+
throw new ArgumentNullException(nameof(formatter));
67+
68+
this.formatter = formatter;
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)