Skip to content

Commit dfb9174

Browse files
committed
returning ModelClientValidationEqualToRule for PropertyMustMatchRule
1 parent 0f5790e commit dfb9174

11 files changed

+123
-16
lines changed

Source/FluentMetadata.Core.Specs/Builder/When_FluentMetadataBuilder_builds_metadata_copying_from_other_metadata.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ public void It_does_not_duplicate_generic_property_rules()
134134
Assert.Equal(1, someViewModelMyPropertyRules.OfType<GenericRule<int>>().Count());
135135
}
136136

137+
[Fact]
138+
public void It_does_not_duplicate_ClassRuleValidatingAPropertyWrapper()
139+
{
140+
Assert.Equal(1, someViewModelMyPropertyRules.OfType<ClassRuleValidatingAPropertyWrapper>().Count());
141+
}
142+
137143
#region System under test
138144

139145
#region dependent metadata is defined before its dependency

Source/FluentMetadata.Core.Specs/Builder/When_FluentMetadataBuilder_builds_metadata_copying_from_other_metadata_that_does_not_apply.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ public void It_does_not_copy_generic_property_rules()
9090
Assert.Equal(0, someViewModelMyPropertyRules.OfType<GenericRule<int>>().Count());
9191
}
9292

93+
[Fact]
94+
public void It_does_not_copy_ClassRuleValidatingAPropertyWrapper()
95+
{
96+
Assert.Equal(0, someViewModelMyPropertyRules.OfType<ClassRuleValidatingAPropertyWrapper>().Count());
97+
}
98+
9399
#region System under test
94100

95101
class SomeDomainModel

Source/FluentMetadata.Core/FluentMetadata.Core.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
<Compile Include="Builder\ClassMetadataBuilder.cs" />
7575
<Compile Include="Builder\PropertiesInClassContextBuilder.cs" />
7676
<Compile Include="MetadataDefinitionSorter.cs" />
77+
<Compile Include="Rules\ClassRuleValidatingAPropertyWrapper.cs" />
78+
<Compile Include="Rules\ICompareProperties.cs" />
7779
<Compile Include="Rules\PropertyMustBeLessThanOtherRule.cs" />
7880
<Compile Include="Rules\GenericClassRule.cs" />
7981
<Compile Include="Rules\GenericRule.cs" />

Source/FluentMetadata.Core/MetaData.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,13 @@ internal void AddRule(IRule rule)
331331
{
332332
rules.RemoveAll(r => r.Equals(rule));
333333
rules.Add(rule);
334+
335+
var propertyValidatingRule = rule as IValidateAProperty;
336+
if (propertyValidatingRule != null && Properties.Contains(propertyValidatingRule.PropertyName))
337+
{
338+
Properties[propertyValidatingRule.PropertyName]
339+
.AddRule(new ClassRuleValidatingAPropertyWrapper(propertyValidatingRule));
340+
}
334341
}
335342
}
336343

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
3+
namespace FluentMetadata.Rules
4+
{
5+
/// <summary>
6+
/// A wrapper for a class rule validating a property used to relate the class rule to the property it is validating.
7+
/// </summary>
8+
class ClassRuleValidatingAPropertyWrapper : Rule
9+
{
10+
public override Type PropertyType
11+
{
12+
get
13+
{
14+
return PropertyValidatingRule.ClassType
15+
.GetProperty(PropertyValidatingRule.PropertyName)
16+
.PropertyType;
17+
}
18+
}
19+
20+
internal IValidateAProperty PropertyValidatingRule { get; private set; }
21+
22+
internal ClassRuleValidatingAPropertyWrapper(IValidateAProperty propertyValidatingRule)
23+
: base(null)
24+
{
25+
PropertyValidatingRule = propertyValidatingRule;
26+
}
27+
28+
public override bool IsValid(object value)
29+
{
30+
//should always validate since it's only a dummy for wrapping an IValidateAProperty
31+
return true;
32+
}
33+
34+
public override string FormatErrorMessage(string name)
35+
{
36+
throw new NotSupportedException(
37+
@"This rule is only a wrapper for a class rule validating a property.
38+
Please get the exception message from the wrapped class rule.");
39+
}
40+
41+
protected override bool EqualsRule(Rule rule)
42+
{
43+
var classRuleValidatingAPropertyWrapper = rule as ClassRuleValidatingAPropertyWrapper;
44+
return classRuleValidatingAPropertyWrapper == null ?
45+
false :
46+
classRuleValidatingAPropertyWrapper.PropertyValidatingRule.Equals(PropertyValidatingRule);
47+
}
48+
}
49+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace FluentMetadata.Rules
2+
{
3+
internal interface IValidateAProperty : IClassRule
4+
{
5+
string PropertyName { get; }
6+
}
7+
8+
internal interface ICompareProperties : IValidateAProperty
9+
{
10+
string OtherPropertyName { get; }
11+
}
12+
}

Source/FluentMetadata.Core/Rules/PropertyMustMatchRule.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,29 @@
44

55
namespace FluentMetadata.Rules
66
{
7-
public class PropertyMustMatchRule<T> : ClassRule<T>
7+
public class PropertyMustMatchRule<T> : ClassRule<T>, ICompareProperties
88
{
99
const string DefaultErrorMessage = "'{0}' and '{1}' do not match.";
1010

11-
readonly string originalPropertyName;
12-
readonly string confirmPropertyName;
11+
public string PropertyName { get; private set; }
12+
public string OtherPropertyName { get; private set; }
1313

1414
public PropertyMustMatchRule(
1515
Expression<Func<T, object>> expression,
1616
Expression<Func<T, object>> confirmExpression)
1717
: base(DefaultErrorMessage)
1818
{
19-
originalPropertyName = ExpressionHelper.GetPropertyName(expression);
20-
confirmPropertyName = ExpressionHelper.GetPropertyName(confirmExpression);
19+
PropertyName = ExpressionHelper.GetPropertyName(expression);
20+
OtherPropertyName = ExpressionHelper.GetPropertyName(confirmExpression);
2121
}
2222

2323
public override string FormatErrorMessage(string name)
2424
{
2525
return string.Format(
2626
CultureInfo.CurrentCulture,
2727
ErrorMessageFormat,
28-
GetPropertyDisplayName(originalPropertyName),
29-
GetPropertyDisplayName(confirmPropertyName));
28+
GetPropertyDisplayName(PropertyName),
29+
GetPropertyDisplayName(OtherPropertyName));
3030
}
3131

3232
static string GetPropertyDisplayName(string propertyName)
@@ -48,8 +48,8 @@ public override bool IsValid(T instance)
4848
return true;
4949

5050
return Equals(
51-
GetValueFromProperty(instance, originalPropertyName),
52-
GetValueFromProperty(instance, confirmPropertyName));
51+
GetValueFromProperty(instance, PropertyName),
52+
GetValueFromProperty(instance, OtherPropertyName));
5353
}
5454

5555
static object GetValueFromProperty(object instance, string propertyName)

Source/FluentMetadata.MVC.Specs/ComplexModel.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using System.ComponentModel;
32
using System.ComponentModel.DataAnnotations;
43
using System.Web.Mvc;
@@ -13,11 +12,7 @@ public ComplexModelMetadata()
1312

1413
Class
1514
.Display.Name("Komplex")
16-
.AssertThat(
17-
cm => !"Robert'); DROP TABLE Students; --".Equals( //http://xkcd.com/327/ :)
18-
cm.FirstName + cm.LastName,
19-
StringComparison.InvariantCultureIgnoreCase),
20-
"Gotcha, little Bobby Tables! You'll never be '{0}'!");
15+
.Property(c => c.LastName).ShouldEqual(c => c.FirstName);
2116
Property(e => e.Id)
2217
.Should.HiddenInput()
2318
.Is.ReadOnly()
@@ -63,6 +58,7 @@ public class ComplexModel : ComplexDomainModel
6358
[DisplayFormat(NullDisplayText = "No lastname set")]
6459
[RegularExpression("^[A-Z]'?[- a-zA-Z]+$")]
6560
[AllowHtml]
61+
[Compare("FirstName")]
6662
[StringLength(50)]
6763
public string LastName { get; set; }
6864

Source/FluentMetadata.MVC.Specs/ConcernOfComparingMetadata.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,24 @@ public void RegularExpressionValidatorsMatch()
257257
.OfType<ModelClientValidationRegexRule>()
258258
.Count());
259259
}
260+
261+
[Observation]
262+
public void EqualToClientValidationRulesMatch()
263+
{
264+
var controllerContext = new ControllerContext();
265+
var expectedValidatorCount = new DataAnnotationsModelValidatorProvider()
266+
.GetValidators(Expected, controllerContext)
267+
.SelectMany(rmv => rmv.GetClientValidationRules())
268+
.OfType<ModelClientValidationEqualToRule>()
269+
.Count();
270+
Assert.InRange(expectedValidatorCount, 0, 1);
271+
Assert.Equal(
272+
expectedValidatorCount,
273+
new FluentValidationProvider()
274+
.GetValidators(Fluent, controllerContext)
275+
.SelectMany(rmv => rmv.GetClientValidationRules())
276+
.OfType<ModelClientValidationEqualToRule>()
277+
.Count());
278+
}
260279
}
261280
}

Source/FluentMetadata.MVC.Specs/MetaData_ComplexModel_Specs.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void The_validator_returns_1_ModelValidationResult()
6666
public void The_error_message_of_the_ModelValidationResult_equals_the_message_specified_in_the_rule()
6767
{
6868
Assert.Equal(
69-
"Gotcha, little Bobby Tables! You'll never be 'Komplex'!",
69+
"'LastName' and 'Vorname' do not match.",
7070
validators[0].Validate(model).ToArray()[0].Message);
7171
}
7272

0 commit comments

Comments
 (0)