diff --git a/.gitignore b/.gitignore index 256b66e..6ac084c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,7 @@ _ReSharper*/ *.vs10x Source/Gallio Reports -Source/TestResults \ No newline at end of file +Source/TestResults +Source/_UpgradeReport_Files/ +*.orig +Source/UpgradeLog* \ No newline at end of file diff --git a/Source/FluentMetadata.Core.Specs/ClassMetadata_with_Person.cs b/Source/FluentMetadata.Core.Specs/ClassMetadata_with_Person.cs index 5725708..3825cf7 100644 --- a/Source/FluentMetadata.Core.Specs/ClassMetadata_with_Person.cs +++ b/Source/FluentMetadata.Core.Specs/ClassMetadata_with_Person.cs @@ -1,3 +1,5 @@ +using System.Linq; +using FluentMetadata.Rules; using FluentMetadata.Specs.SampleClasses; using Xunit; @@ -10,20 +12,19 @@ public class ClassMetadata_with_Person : MetadataTestBase public ClassMetadata_with_Person() { var query = new QueryFluentMetadata(); - classMetadata = query.GetMetadataFor(typeof (Person)); + classMetadata = query.GetMetadataFor(typeof(Person)); } [Fact] public void Metadata_ModelType_is_Person() { - Assert.Equal(typeof (Person), classMetadata.ModelType); + Assert.Equal(typeof(Person), classMetadata.ModelType); } [Fact] public void Metadata_ModelName_is_Null() { - Assert.Null(classMetadata.ModelName); -// Assert.Equal("Person", classMetadata.ModelName); + Assert.Null(classMetadata.ModelName); } [Fact] @@ -31,5 +32,21 @@ public void Metadata_Display_is_Benutzer() { Assert.Equal("Benutzer", classMetadata.DisplayName); } + + [Fact] + public void Instance_with_FirstName_different_from_LastName_is_invalid() + { + var rule = classMetadata.Rules.OfType>().Last(); + var person = new Person { FirstName = "foo", LastName = "bar" }; + Assert.False(rule.IsValid(person)); + } + + [Fact] + public void Instance_with_FirstName_equal_to_LastName_is_valid() + { + var rule = classMetadata.Rules.OfType>().Last(); + var person = new Person { FirstName = "foo", LastName = "foo" }; + Assert.True(rule.IsValid(person)); + } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core.Specs/FluentMetadata.Core.Specs.csproj b/Source/FluentMetadata.Core.Specs/FluentMetadata.Core.Specs.csproj index e8a0055..9ef80eb 100644 --- a/Source/FluentMetadata.Core.Specs/FluentMetadata.Core.Specs.csproj +++ b/Source/FluentMetadata.Core.Specs/FluentMetadata.Core.Specs.csproj @@ -87,6 +87,7 @@ + diff --git a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_Person.cs b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_Person.cs index fb90c62..58569eb 100644 --- a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_Person.cs +++ b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_Person.cs @@ -11,8 +11,8 @@ public class PropertyMedata_with_Person : MetadataTestBase public PropertyMedata_with_Person() { var query = new QueryFluentMetadata(); - firstName = query.GetMetadataFor(typeof (Person), "FirstName"); - lastName = query.GetMetadataFor(typeof (Person), "LastName"); + firstName = query.GetMetadataFor(typeof(Person), "FirstName"); + lastName = query.GetMetadataFor(typeof(Person), "LastName"); } [Fact] @@ -24,7 +24,7 @@ public void FirstName_ModelName_is_FirstName() [Fact] public void FirstName_ModelType_is_string() { - Assert.Equal(typeof (string), firstName.ModelType); + Assert.Equal(typeof(string), firstName.ModelType); } [Fact] @@ -42,7 +42,7 @@ public void LastName_ModelName_is_LastName() [Fact] public void LastName_ModelType_is_string() { - Assert.Equal(typeof (string), lastName.ModelType); + Assert.Equal(typeof(string), lastName.ModelType); } [Fact] diff --git a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUser.cs b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUser.cs index 2dea710..b1dbaa3 100644 --- a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUser.cs +++ b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUser.cs @@ -1,18 +1,21 @@ -using FluentMetadata.Specs.SampleClasses; +using System; +using FluentMetadata.Specs.SampleClasses; using Xunit; namespace FluentMetadata.Specs { public class PropertyMedata_with_WebUser : MetadataTestBase { - private Metadata username; - private Metadata id; + Metadata lastLogin, username, id, passWordHash, role; public PropertyMedata_with_WebUser() { var query = new QueryFluentMetadata(); username = query.GetMetadataFor(typeof(WebUser), "Username"); id = query.GetMetadataFor(typeof(WebUser), "Id"); + lastLogin = query.GetMetadataFor(typeof(WebUser), "LastLogin"); + passWordHash = query.GetMetadataFor(typeof(WebUser), "PasswordHash"); + role = query.GetMetadataFor(typeof(WebUser), "Role"); } [Fact] @@ -39,6 +42,42 @@ public void Username_Required_is_true() Assert.True(username.Required.Value); } + [Fact] + public void Username_MinLength_is_3() + { + Assert.Equal(3, username.GetMinimumLength()); + } + + [Fact] + public void Username_MaxLength_is_256() + { + Assert.Equal(256, username.GetMaximumLength()); + } + + [Fact] + public void PassWordHash_MinLength_is_32() + { + Assert.Equal(32, passWordHash.GetMinimumLength()); + } + + [Fact] + public void PassWordHash_MaxLength_is_null() + { + Assert.Null(passWordHash.GetMaximumLength()); + } + + [Fact] + public void Role_MinLength_is_null() + { + Assert.Null(role.GetMinimumLength()); + } + + [Fact] + public void Role_MaxLength_is_256() + { + Assert.Equal(256, role.GetMaximumLength()); + } + [Fact] public void Id_ModelName_is_Id() { @@ -56,5 +95,17 @@ public void Id_Required_is_false() { Assert.False(id.Required.HasValue); } + + [Fact] + public void Last_Login_Minimum_is_2010_1_23() + { + Assert.Equal(new DateTime(2010, 1, 23), lastLogin.GetRangeMinimum()); + } + + [Fact] + public void Last_Login_Maximum_is_DoomsDay() + { + Assert.Equal(DateTime.MaxValue, lastLogin.GetRangeMaximum()); + } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUserIndexModel.cs b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUserIndexModel.cs index 8f4c351..7f2b399 100644 --- a/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUserIndexModel.cs +++ b/Source/FluentMetadata.Core.Specs/PropertyMedata_with_WebUserIndexModel.cs @@ -5,16 +5,16 @@ namespace FluentMetadata.Specs { public class PropertyMedata_with_WebUserIndexModel : MetadataTestBase { - private Metadata username; - private Metadata id; - private Metadata autorName; + Metadata username, id, autorName, email, role; public PropertyMedata_with_WebUserIndexModel() { var query = new QueryFluentMetadata(); username = query.GetMetadataFor(typeof(WebUserIndexModel), "Username"); id = query.GetMetadataFor(typeof(WebUserIndexModel), "Id"); + email = query.GetMetadataFor(typeof(WebUserIndexModel), "EMail"); autorName = query.GetMetadataFor(typeof(WebUserIndexModel), "AutorName"); + role = query.GetMetadataFor(typeof(WebUserIndexModel), "Role"); } [Fact] @@ -62,7 +62,101 @@ public void Id_Required_is_false() [Fact] public void AutorName_DisplayName_is_emaN() { - Assert.Equal("emaN",autorName.DisplayName); + Assert.Equal("emaN", autorName.DisplayName); + } + + [Fact] + public void EMail_DataTypeName_is_EmailAddress() + { + Assert.Equal("EmailAddress", email.DataTypeName); + } + + [Fact] + public void Username_Description_is_Name_des_Benutzers() + { + Assert.Equal("Name des Benutzers", username.Description); + } + + [Fact] + public void EMail_DisplayFormat_is_MailtoLink() + { + Assert.Equal("{0}", email.DisplayFormat); + } + + [Fact] + public void EMail_EditorFormat_is_plain_value() + { + Assert.Equal("{0}", email.EditorFormat); + } + + [Fact] + public void Id_HideSurroundingHtml_is_true() + { + Assert.True(id.HideSurroundingHtml.HasValue); + Assert.True(id.HideSurroundingHtml.Value); + } + + [Fact] + public void Username_ReadOnly_is_true() + { + Assert.True(username.Readonly); + } + + [Fact] + public void AutorName_NullDisplayText_is_Anonymous_Autor() + { + Assert.Equal("Anonymous Autor", autorName.NullDisplayText); + } + + [Fact] + public void Id_ShowDisplay_is_false() + { + Assert.False(id.ShowDisplay); + } + + [Fact] + public void Id_ShowEditor_is_false() + { + Assert.False(id.ShowEditor); + } + + [Fact] + public void Role_TemplateHint_is_Roles() + { + Assert.Equal("Roles", role.TemplateHint); + } + + [Fact] + public void EMail_Watermark_is_dummy_address() + { + Assert.Equal("john@doe.com", email.Watermark); + } + + [Fact] + public void Username_ConvertEmptyStringToNull_is_false() + { + Assert.False(username.ConvertEmptyStringToNull); + } + + [Fact] + public void Id_Hidden_is_true() + { + Assert.True(id.Hidden.HasValue); + Assert.True(id.Hidden.Value); + } + + [Fact] + public void Username_GetMaximumLength_is_256() + { + var maxLength = username.GetMaximumLength(); + Assert.True(maxLength.HasValue); + Assert.Equal(256, maxLength); + } + + [Fact] + public void Username_ContainerType_is_WebUserIndexModel() + { + Assert.Equal(typeof(WebUserIndexModel), username.ContainerType); } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRuleSpecs.cs b/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRuleSpecs.cs new file mode 100644 index 0000000..93d9499 --- /dev/null +++ b/Source/FluentMetadata.Core.Specs/Rules/PropertyMustMatchRuleSpecs.cs @@ -0,0 +1,38 @@ +using FluentMetadata.Rules; +using Xunit; + +namespace FluentMetadata.Specs.Rules +{ + public class ChangePasswordModel + { + public string OldPassword { get; set; } + public string NewPassword { get; set; } + public string ConfirmPassword { get; set; } + } + + public class When_two_properties_should_be_equal : InstanceContextSpecification> + { + protected override PropertyMustMatchRule CreateSut() + { + return new PropertyMustMatchRule(x => x.NewPassword, x => x.ConfirmPassword); + } + + protected override void Because() + { + } + + [Observation] + public void Should_be_valid_if_properties_match() + { + var model = new ChangePasswordModel { NewPassword = "asdf", ConfirmPassword = "asdf" }; + Sut.IsValid(model).ShouldBeTrue(); + } + + [Observation] + public void Should_be_invalid_if_properties_do_not_match() + { + var model = new ChangePasswordModel { NewPassword = "qwer", ConfirmPassword = "asdf" }; + Sut.IsValid(model).ShouldBeFalse(); + } + } +} \ No newline at end of file diff --git a/Source/FluentMetadata.Core.Specs/Rules/StringLengthRuleSpecs.cs b/Source/FluentMetadata.Core.Specs/Rules/StringLengthRuleSpecs.cs index ca13a79..e614e73 100644 --- a/Source/FluentMetadata.Core.Specs/Rules/StringLengthRuleSpecs.cs +++ b/Source/FluentMetadata.Core.Specs/Rules/StringLengthRuleSpecs.cs @@ -59,9 +59,94 @@ public void Should_Valid_with_a_string_with_length_0() [Observation] public void Should_Valid_with_a_string_is_NULL() { - string badLength = null; + string badLength = null; Sut.IsValid(badLength).ShouldBeTrue(); } + } + + public class When_the_minimal_StringLength_is_8_and_Maximal_StringLength_is_null : InstanceContextSpecification + { + protected override StringLengthRule CreateSut() + { + return new StringLengthRule(8, null); + } + + protected override void Because() + { + } + + [Observation] + public void Should_be_invalid_with_a_null_string() + { + Sut.IsValid(null).ShouldBeFalse(); + } + [Observation] + public void Should_be_invalid_with_a_string_with_length_7() + { + var value = new string('a', 7); + Sut.IsValid(value).ShouldBeFalse(); + } + + [Observation] + public void Should_be_valid_with_a_string_with_length_8() + { + var value = new string('a', 8); + Sut.IsValid(value).ShouldBeTrue(); + } + + [Observation] + public void Should_be_valid_with_a_string_with_length_4001() + { + var value = new string('a', 4001); + Sut.IsValid(value).ShouldBeTrue(); + } + } + + public class When_the_minimal_StringLength_is_5_and_Maximal_StringLength_is_250 : InstanceContextSpecification + { + protected override StringLengthRule CreateSut() + { + return new StringLengthRule(5, 250); + } + + protected override void Because() + { + } + + [Observation] + public void Should_be_invalid_with_a_string_with_length_4() + { + var value = new string('a', 4); + Sut.IsValid(value).ShouldBeFalse(); + } + + [Observation] + public void Should_be_valid_with_a_string_with_length_5() + { + var value = new string('a', 5); + Sut.IsValid(value).ShouldBeTrue(); + } + + [Observation] + public void Should_be_valid_with_a_string_with_length_249() + { + var value = new string('a', 249); + Sut.IsValid(value).ShouldBeTrue(); + } + + [Observation] + public void Should_be_valid_with_a_string_with_length_250() + { + var value = new string('a', 249); + Sut.IsValid(value).ShouldBeTrue(); + } + + [Observation] + public void Should_be_invalid_with_a_string_with_length_251() + { + var value = new string('a', 251); + Sut.IsValid(value).ShouldBeFalse(); + } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/PersonMetadata.cs b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/PersonMetadata.cs index 6490b72..425930e 100644 --- a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/PersonMetadata.cs +++ b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/PersonMetadata.cs @@ -5,8 +5,10 @@ public class PersonMetadata : ClassMetadata public PersonMetadata() { Property(p => p.FirstName).Is.Required(); - Class.Display.Name("Benutzer"); - Class.Display.Format("{0} der Benutzer"); + Class + .Display.Name("Benutzer") + .Display.Format("{0} der Benutzer") + .PropertiesShouldMatch(p => p.FirstName, p => p.LastName); } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/WebUserMetadata.cs b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/WebUserMetadata.cs index 8376a6d..803dd4c 100644 --- a/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/WebUserMetadata.cs +++ b/Source/FluentMetadata.Core.Specs/SampleClasses/MetaData/WebUserMetadata.cs @@ -1,21 +1,27 @@ +using System; + namespace FluentMetadata.Specs.SampleClasses.MetaData { - public class WebUserMetadata : DomainObjectMetadata + public class WebUserMetadata : DomainObjectMetadata { public WebUserMetadata() { - Property(x => x.Username).Length(256).Is.Required().Is.ReadOnly() - .Display.Name("Benutzername"); + Property(x => x.Username).Length(3, 256).Is.Required().Is.ReadOnly() + .Display.Name("Benutzername").Description("Name des Benutzers") + .Is.Not.ConvertEmptyStringToNull(); Property(x => x.EMail).Length(128).Is.Required().As.EmailAddress() - .Display.Name("E-Mail").As.EmailAddress(); - Property(x => x.PasswordHash).Length(64).Is.Required() + .Display.Name("E-Mail").Display.Format("{0}") + .Editor.Format("{0}").Editor.Watermark("john@doe.com"); + Property(x => x.PasswordHash).Length(32, null).Is.Required() .Should.Not.ShowInDisplay().Should.Not.ShowInEditor(); Property(x => x.Role).UIHint("Roles").Length(256).Is.Required() .Display.Name("Rolle"); Property(x => x.PasswordHash).Should.Not.ShowInDisplay().Should.Not.ShowInEditor(); Property(x => x.ConfirmationKey).Should.Not.ShowInEditor().Should.Not.ShowInDisplay(); Property(x => x.LastLogin).Should.Not.ShowInEditor() - .Display.Name("Letzte Anmeldung").Display.NullText(""); + .Display.Name("Letzte Anmeldung") + .Display.NullText("") + .Range(new DateTime(2010, 1, 23), DateTime.MaxValue); //support ends on doomsday Property(x => x.BounceCount).Should.Not.ShowInEditor() .Display.Name("E-Mail Fehler"); Property(x => x.Confirmed).Should.Not.HiddenInput() @@ -30,7 +36,8 @@ public class AutorMetadata : DomainObjectMetadata { public AutorMetadata() { - Property(e => e.Name).Display.Name("emaN"); + Property(e => e.Name).Display.Name("emaN") + .Display.NullText("Anonymous Autor"); } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/Builder/AsBuilder.cs b/Source/FluentMetadata.Core/Builder/AsBuilder.cs index 3c08180..0732289 100644 --- a/Source/FluentMetadata.Core/Builder/AsBuilder.cs +++ b/Source/FluentMetadata.Core/Builder/AsBuilder.cs @@ -11,7 +11,7 @@ public AsBuilder(PropertyMetadataBuilder propertyMetaDataBuilder) this.propertyMetaDataBuilder = propertyMetaDataBuilder; } - public IProperty EmailAddress() + public IProperty EmailAddress() { SetDataTypeName(DataType.EmailAddress); return propertyMetaDataBuilder; @@ -22,34 +22,40 @@ private void SetDataTypeName(DataType dataType) propertyMetaDataBuilder.Metadata.DataTypeName = dataType.ToString(); } - public IProperty Url() + public IProperty Url() { SetDataTypeName(DataType.Url); return propertyMetaDataBuilder; } - public IProperty Html() + public IProperty Html() { SetDataTypeName(DataType.Html); return propertyMetaDataBuilder; } - public IProperty Text() + public IProperty Text() { SetDataTypeName(DataType.Text); return propertyMetaDataBuilder; } - public IProperty MultilineText() + public IProperty MultilineText() { SetDataTypeName(DataType.MultilineText); return propertyMetaDataBuilder; } - public IProperty Password() + public IProperty Password() { SetDataTypeName(DataType.Password); return propertyMetaDataBuilder; } + + public IProperty Custom(string dataTypeName) + { + propertyMetaDataBuilder.Metadata.DataTypeName = dataTypeName; + return propertyMetaDataBuilder; + } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/Builder/ClassMetadataBuilder.cs b/Source/FluentMetadata.Core/Builder/ClassMetadataBuilder.cs index f6bb82b..9d0b26a 100644 --- a/Source/FluentMetadata.Core/Builder/ClassMetadataBuilder.cs +++ b/Source/FluentMetadata.Core/Builder/ClassMetadataBuilder.cs @@ -1,5 +1,6 @@ using System; - +using System.Linq.Expressions; +using FluentMetadata.Rules; namespace FluentMetadata.Builder { internal class ClassMetadataBuilder : IClassBuilder @@ -11,18 +12,16 @@ internal class ClassMetadataBuilder : IClassBuilder public ClassMetadataBuilder(Metadata metadata) { this.metadata = metadata; - metadata.ModelType = typeof (T); -// metadata.ModelName = typeof (T).Name; + metadata.ModelType = typeof(T); InitPropertyMetadata(); } private void InitPropertyMetadata() { - string a; - var builder = FluentMetadataBuilder.GetTypeBuilder(typeof (T)); - foreach (var propertyInfo in typeof (T).GetProperties()) + var builder = FluentMetadataBuilder.GetTypeBuilder(typeof(T)); + foreach (var propertyInfo in typeof(T).GetProperties()) { - if (propertyInfo.GetIndexParameters().Length==0) + if (propertyInfo.GetIndexParameters().Length == 0) { var propertyMetadata = builder.MapProperty(typeof(T), propertyInfo.Name, propertyInfo.PropertyType); metadata.Properties.Add(propertyMetadata); @@ -44,5 +43,12 @@ public Metadata Metadata { get { return metadata; } } + + public IClassBuilder PropertiesShouldMatch(Expression> expression, + Expression> confirmExpression) + { + metadata.AddRule(new PropertyMustMatchRule(expression, confirmExpression)); + return this; + } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/Builder/IsBuilder.cs b/Source/FluentMetadata.Core/Builder/IsBuilder.cs index d6b5899..a87cad7 100644 --- a/Source/FluentMetadata.Core/Builder/IsBuilder.cs +++ b/Source/FluentMetadata.Core/Builder/IsBuilder.cs @@ -17,7 +17,7 @@ public IsBuilder(PropertyMetadataBuilder propertyMetaDataBuilder) this.propertyMetaDataBuilder = propertyMetaDataBuilder; } - public IProperty Required() + public IProperty Required() { Metadata.Required = !notted; notted = false; @@ -28,7 +28,7 @@ public IProperty Required() return propertyMetaDataBuilder; } - public IProperty ReadOnly() + public IProperty ReadOnly() { Metadata.Readonly = !notted; notted = false; @@ -44,5 +44,12 @@ public IIsNotProperty Not return this; } } + + public IProperty ConvertEmptyStringToNull() + { + Metadata.ConvertEmptyStringToNull = !notted; + notted = false; + return propertyMetaDataBuilder; + } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/Builder/PropertyMetadataBuilder.cs b/Source/FluentMetadata.Core/Builder/PropertyMetadataBuilder.cs index 5667206..f718057 100644 --- a/Source/FluentMetadata.Core/Builder/PropertyMetadataBuilder.cs +++ b/Source/FluentMetadata.Core/Builder/PropertyMetadataBuilder.cs @@ -8,7 +8,8 @@ internal abstract class PropertyMetadataBuilder { private readonly Metadata metadata; - protected PropertyMetadataBuilder() : this(new Metadata()) + protected PropertyMetadataBuilder() + : this(new Metadata()) { } @@ -23,7 +24,7 @@ public Metadata Metadata } } - internal class PropertyMetadataBuilder : PropertyMetadataBuilder, IProperty + internal class PropertyMetadataBuilder : PropertyMetadataBuilder, IProperty { public PropertyMetadataBuilder(Metadata metadata) @@ -33,45 +34,48 @@ public PropertyMetadataBuilder(Metadata metadata) public PropertyMetadataBuilder(Expression> expression) { - Metadata.ContainerType = typeof (T); + Metadata.ContainerType = typeof(T); Metadata.ModelName = ExpressionHelper.GetPropertyName(expression); Metadata.ModelType = ExpressionHelper.GetPropertyType(expression); } - public PropertyMetadataBuilder(string propertyName) { Metadata.ContainerType = null; Metadata.ModelName = propertyName; - Metadata.ModelType = typeof (T); + Metadata.ModelType = typeof(T); } + public IProperty Length(int maxLength) + { + Metadata.AddRule(new StringLengthRule(maxLength)); + return this; + } - public IProperty Length(int length) + public IProperty Length(int minLength, int? maxLength) { - Metadata.StringLength = length; - Metadata.AddRule(new StringLengthRule(length)); + Metadata.AddRule(new StringLengthRule(minLength, maxLength)); return this; } - public IProperty UIHint(string templateHint) + public IProperty UIHint(string templateHint) { Metadata.TemplateHint = templateHint; return this; } - public IProperty Description(string description) + public IProperty Description(string description) { Metadata.Description = description; return this; } - public IEditorProperty Editor + public IEditorProperty Editor { get { return new EditorBuilder(this); } } - public IDisplayProperty Display + public IDisplayProperty Display { get { return new DisplayBuilder(this); } } @@ -90,5 +94,12 @@ public IShouldProperty Should { get { return new ShouldBuilder(this); } } + + public IProperty Range(IComparable minimum, IComparable maximum) + { + Metadata.AddRule(new RangeRule(minimum, maximum)); + + return this; + } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/IAsProperty.cs b/Source/FluentMetadata.Core/IAsProperty.cs index 041217f..e46c815 100644 --- a/Source/FluentMetadata.Core/IAsProperty.cs +++ b/Source/FluentMetadata.Core/IAsProperty.cs @@ -2,11 +2,12 @@ { public interface IAsProperty { - IProperty EmailAddress(); - IProperty Url(); - IProperty Html(); - IProperty Text(); - IProperty MultilineText(); - IProperty Password(); + IProperty EmailAddress(); + IProperty Url(); + IProperty Html(); + IProperty Text(); + IProperty MultilineText(); + IProperty Password(); + IProperty Custom(string dataTypeName); } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/IClassBuilder.cs b/Source/FluentMetadata.Core/IClassBuilder.cs index 98f0cae..6302ae4 100644 --- a/Source/FluentMetadata.Core/IClassBuilder.cs +++ b/Source/FluentMetadata.Core/IClassBuilder.cs @@ -1,8 +1,15 @@ -namespace FluentMetadata +using System; +using System.Linq.Expressions; + +namespace FluentMetadata { public interface IClassBuilder { Metadata Metadata { get; } IDisplayClass Display { get; } + IClassBuilder PropertiesShouldMatch( + Expression> expression, + Expression> confirmExpression + ); } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/IIsProperty.cs b/Source/FluentMetadata.Core/IIsProperty.cs index d7b9cdf..a698d97 100644 --- a/Source/FluentMetadata.Core/IIsProperty.cs +++ b/Source/FluentMetadata.Core/IIsProperty.cs @@ -1,16 +1,14 @@ -using System; -using System.Linq.Expressions; - -namespace FluentMetadata +namespace FluentMetadata { public interface IIsProperty : IIsNotProperty { IIsNotProperty Not { get; } } - public interface IIsNotProperty + public interface IIsNotProperty { - IProperty Required(); - IProperty ReadOnly(); + IProperty Required(); + IProperty ReadOnly(); + IProperty ConvertEmptyStringToNull(); } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/IProperty.cs b/Source/FluentMetadata.Core/IProperty.cs index 9d9c98f..bd9fa56 100644 --- a/Source/FluentMetadata.Core/IProperty.cs +++ b/Source/FluentMetadata.Core/IProperty.cs @@ -1,14 +1,15 @@ -namespace FluentMetadata +using System; +namespace FluentMetadata { - public interface IProperty + public interface IProperty { - IProperty Length(int length); - IProperty UIHint(string templateHint); - - IProperty Description(string description); - - IEditorProperty Editor { get; } - IDisplayProperty Display { get; } + IProperty Length(int maxLength); + IProperty Length(int minLength, int? maxLength); + IProperty UIHint(string templateHint); + IProperty Description(string description); + IProperty Range(IComparable minimum, IComparable maximum); + IEditorProperty Editor { get; } + IDisplayProperty Display { get; } IAsProperty As { get; } IIsProperty Is { get; } IShouldProperty Should { get; } diff --git a/Source/FluentMetadata.Core/MetaData.cs b/Source/FluentMetadata.Core/MetaData.cs index 2d77d9c..4429b9e 100644 --- a/Source/FluentMetadata.Core/MetaData.cs +++ b/Source/FluentMetadata.Core/MetaData.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using FluentMetadata.Rules; namespace FluentMetadata @@ -11,6 +12,7 @@ public class Metadata public Metadata() { + ConvertEmptyStringToNull = true; ShowDisplay = true; ShowEditor = true; rules = new List(); @@ -28,21 +30,28 @@ public Metadata(Metadata metadata, Type containerType) internal void CopyMetaDataFrom(Metadata metadata) { - Required = metadata.Required; - StringLength = metadata.StringLength; - ErrorMessage = metadata.ErrorMessage; + //TODO write tests for CopyMetaDataFrom: properties commented here have not associated tests yet + ConvertEmptyStringToNull = metadata.ConvertEmptyStringToNull; DataTypeName = metadata.DataTypeName; + Description = metadata.Description; + DisplayFormat = metadata.DisplayFormat; + DisplayName = metadata.DisplayName; + EditorFormat = metadata.EditorFormat; + HideSurroundingHtml = metadata.HideSurroundingHtml; Readonly = metadata.Readonly; + Required = metadata.Required; + NullDisplayText = metadata.NullDisplayText; ShowDisplay = metadata.ShowDisplay; ShowEditor = metadata.ShowEditor; TemplateHint = metadata.TemplateHint; - NullDisplayText = metadata.NullDisplayText; - DisplayName = metadata.DisplayName; + Watermark = metadata.Watermark; + //ErrorMessage = metadata.ErrorMessage; + Hidden = metadata.Hidden; + foreach (var rule in metadata.Rules) { AddRule(rule); } - } #region properties corresponding to System.Web.Mvc.ModelMetadata @@ -52,7 +61,7 @@ internal void CopyMetaDataFrom(Metadata metadata) // // Returns: // A dictionary that contains additional metadata about the model. - // TODO MVC2 public virtual Dictionary AdditionalValues { get; } + // TODO MVC2 [in order to complete properties corresponding to System.Web.Mvc.ModelMetadata] public virtual Dictionary AdditionalValues { get; } // ~ System.Web.Mvc.ModelMetadata.ContainerType /// @@ -63,15 +72,15 @@ internal void CopyMetaDataFrom(Metadata metadata) /// public Type ContainerType { get; set; } - // - // Summary: - // Gets or sets a value that indicates whether empty strings that are posted - // back in forms should be converted to null. - // - // Returns: - // true if empty strings that are posted back in forms should be converted to - // null; otherwise, false. The default value is true. - // TODO MVC2 public virtual bool ConvertEmptyStringToNull { get; set; } + /// + /// Gets or sets a value that indicates whether empty strings that are posted + /// back in forms should be converted to null. + /// + /// + /// true if empty strings that are posted back in forms should be + /// converted to null; otherwise, false. The default value is true + /// + public bool ConvertEmptyStringToNull { get; set; } // ~ System.Web.Mvc.ModelMetadata.DataTypeName /// @@ -136,7 +145,7 @@ internal void CopyMetaDataFrom(Metadata metadata) // Returns: // A value that indicates whether the model is considered a complex type by // the MVC framework. - // TODO MVC2 ? public virtual bool IsComplexType { get; } + // MVC2 [in order to complete properties corresponding to System.Web.Mvc.ModelMetadata] public virtual bool IsComplexType { get; } // // Summary: @@ -144,7 +153,7 @@ internal void CopyMetaDataFrom(Metadata metadata) // // Returns: // true if the type is nullable; otherwise, false. - // TODO MVC2 ? public bool IsNullableValueType { get; } + // MVC2 [in order to complete properties corresponding to System.Web.Mvc.ModelMetadata] public bool IsNullableValueType { get; } // ~ System.Web.Mvc.ModelMetadata.IsReadOnly /// @@ -172,7 +181,7 @@ internal void CopyMetaDataFrom(Metadata metadata) // The value of the model. For more information about System.Web.Mvc.ModelMetadata, // see the entry ASP.NET MVC 2 Templates, Part 2: ModelMetadata on Brad Wilson's // blog - // TODO MVC2 ? public object Model { get; set; } + // TODO MVC2 [in order to complete properties corresponding to System.Web.Mvc.ModelMetadata] public object Model { get; set; } // ~ System.Web.Mvc.ModelMetadata.ModelType /// @@ -198,7 +207,7 @@ internal void CopyMetaDataFrom(Metadata metadata) // // Returns: // The order value of the current metadata. - // TODO MVC3 public virtual int Order { get; set; } + // TODO MVC3 [in order to complete properties corresponding to System.Web.Mvc.ModelMetadata] public virtual int Order { get; set; } // ~ System.Web.Mvc.ModelMetadata.Properties /// @@ -229,7 +238,7 @@ internal void CopyMetaDataFrom(Metadata metadata) // // Returns: // The short display name. - // TODO MVC2 public virtual string ShortDisplayName { get; set; } + // TODO MVC2 [in order to complete properties corresponding to System.Web.Mvc.ModelMetadata] public virtual string ShortDisplayName { get; set; } // ~ System.Web.Mvc.ModelMetadata.ShowForDisplay /// @@ -255,10 +264,10 @@ internal void CopyMetaDataFrom(Metadata metadata) // // Returns: // The simple display string for the model. - // TODO MVC2 public virtual string SimpleDisplayText { get; set; } + // TODO MVC2 [in order to complete properties corresponding to System.Web.Mvc.ModelMetadata] public virtual string SimpleDisplayText { get; set; } /// - /// Gets or sets the template hint. + /// Gets or sets a hint that suggests what template to use for this model. /// /// /// A hint that suggests what template to use for this model. @@ -266,7 +275,7 @@ internal void CopyMetaDataFrom(Metadata metadata) public string TemplateHint { get; set; } /// - /// Gets or sets a hint that suggests what template to use for this model. + /// Gets or sets a value that can be used as a watermark. /// /// /// The watermark. @@ -275,9 +284,16 @@ internal void CopyMetaDataFrom(Metadata metadata) #endregion - // TODO add StringLengthRule to rules automatically on set - public int? StringLength { get; set; } + //TODO [DerAlbertCom] What kind of ErrorMessage is this? Write some XML docs and/or tests. public string ErrorMessage { get; set; } + + //~System.Web.Mvc.HiddenInputAttribute + /// + /// Gets or sets a value indicating whether a property or field value should be rendered as a hidden input element. + /// + /// + /// true if the model should be rendered as a hidden input element; otherwise, false. + /// public bool? Hidden { get; set; } public IEnumerable Rules @@ -289,5 +305,42 @@ public void AddRule(IRule rule) { rules.Add(rule); } + + public object GetRangeMinimum() + { + var rangeRule = GetLastRuleOfType(); + return rangeRule == null ? null : rangeRule.Minimum; + } + + public object GetRangeMaximum() + { + var rangeRule = GetLastRuleOfType(); + return rangeRule == null ? null : rangeRule.Maximum; + } + + public int? GetMaximumLength() + { + var lengthRule = GetLastRuleOfType(); + if (lengthRule == null) + { + return null; + } + return lengthRule.Maximum; + } + + public int? GetMinimumLength() + { + var lengthRule = GetLastRuleOfType(); + if (lengthRule == null) + { + return null; + } + return lengthRule.Minimum; + } + + T GetLastRuleOfType() + { + return Rules.OfType().LastOrDefault(); + } } } \ No newline at end of file diff --git a/Source/FluentMetadata.Core/Rules/PropertyMustMatchRule.cs b/Source/FluentMetadata.Core/Rules/PropertyMustMatchRule.cs index c42ab1b..e586a0c 100644 --- a/Source/FluentMetadata.Core/Rules/PropertyMustMatchRule.cs +++ b/Source/FluentMetadata.Core/Rules/PropertyMustMatchRule.cs @@ -4,17 +4,18 @@ namespace FluentMetadata.Rules { - public class PropertyMustMatchRule : ClassRule where T : class + public class PropertyMustMatchRule : ClassRule { private const string DefaultErrorMessage = "'{0}' and '{1}' do not match."; - private readonly string originalPropertyName; private readonly string confirmPropertyName; private Type currentType; - public PropertyMustMatchRule(Expression> expression, - Expression> confirmExpression) : base(DefaultErrorMessage) + public PropertyMustMatchRule( + Expression> expression, + Expression> confirmExpression) + : base(DefaultErrorMessage) { originalPropertyName = ExpressionHelper.GetPropertyName(expression); confirmPropertyName = ExpressionHelper.GetPropertyName(confirmExpression); @@ -22,11 +23,12 @@ public PropertyMustMatchRule(Expression> expression, public override string FormatErrorMessage(string name) { - return String.Format(CultureInfo.CurrentCulture, - ErrorMessageFormat, - GetPropertyDisplayName(originalPropertyName), - GetPropertyDisplayName(confirmPropertyName) - ); + return String.Format( + CultureInfo.CurrentCulture, + ErrorMessageFormat, + GetPropertyDisplayName(originalPropertyName), + GetPropertyDisplayName(confirmPropertyName) + ); } private string GetPropertyDisplayName(string propertyName) @@ -34,7 +36,9 @@ private string GetPropertyDisplayName(string propertyName) var metaData = FluentMetadataBuilder.GetTypeBuilder(currentType).MetaDataFor(propertyName); if (metaData != null) { - propertyName = string.IsNullOrEmpty(metaData.DisplayName) ? propertyName : metaData.DisplayName; + propertyName = string.IsNullOrEmpty(metaData.DisplayName) ? + propertyName : + metaData.DisplayName; } return propertyName; } @@ -45,8 +49,10 @@ public override bool IsValid(T instance) return true; currentType = instance.GetType(); - return Equals(GetValueFromProperty(instance, originalPropertyName), - GetValueFromProperty(instance, confirmPropertyName)); + return Equals( + GetValueFromProperty(instance, originalPropertyName), + GetValueFromProperty(instance, confirmPropertyName) + ); } private static object GetValueFromProperty(object instance, string propertyName) diff --git a/Source/FluentMetadata.Core/Rules/RangeRule.cs b/Source/FluentMetadata.Core/Rules/RangeRule.cs index ebca20a..62ab350 100644 --- a/Source/FluentMetadata.Core/Rules/RangeRule.cs +++ b/Source/FluentMetadata.Core/Rules/RangeRule.cs @@ -5,26 +5,31 @@ namespace FluentMetadata.Rules { public class RangeRule : Rule { - private Func valueConversion; - private object valueMaximum; - private object valueMinimum; + private IComparable valueMaximum; + private IComparable valueMinimum; - private RangeRule() - : base("the value of {0} must be between {0} and {1}") + internal object Minimum { + get + { + return valueMinimum; + } } - public RangeRule(double minimum, double maximum) : this() + internal object Maximum { - Initialize(minimum, maximum, o => Convert.ToDouble(o)); + get + { + return valueMaximum; + } } - public RangeRule(int minimum, int maximum) : this() + private RangeRule() + : base("the value of '{0}' must be between {1} and {2}") { - Initialize(minimum, maximum, o => Convert.ToInt32(o)); } - public RangeRule(DateTime minimum, DateTime maximum) + public RangeRule(IComparable minimum, IComparable maximum) : this() { Initialize(minimum, maximum, o => Convert.ToDateTime(o)); @@ -34,14 +39,19 @@ private void Initialize(IComparable minimum, IComparable maximum, Func 0) { - throw new ArgumentOutOfRangeException("maximum", maximum, - string.Format(CultureInfo.CurrentCulture, - "the minimum vallue {1} is higher then the maximum value {0}", - minimum, maximum)); + throw new ArgumentOutOfRangeException( + "maximum", + maximum, + string.Format( + CultureInfo.CurrentCulture, + "the minimum value {0} is higher then the maximum value {1}", + minimum, + maximum + ) + ); } valueMinimum = minimum; valueMaximum = maximum; - valueConversion = conversion; } public override bool IsValid(object value) @@ -50,14 +60,13 @@ public override bool IsValid(object value) { return true; } - if ((value is string) && string.IsNullOrEmpty((string) value)) + if ((value is string) && string.IsNullOrEmpty(value as string)) { return true; } - object currentValue = valueConversion(value); - var min = (IComparable) valueMinimum; - var max = (IComparable) valueMaximum; - return ((min.CompareTo(currentValue) <= 0) && (max.CompareTo(currentValue) >= 0)); + var min = (IComparable)valueMinimum; + var max = (IComparable)valueMaximum; + return ((min.CompareTo(value) <= 0) && (max.CompareTo(value) >= 0)); } public override string FormatErrorMessage(string name) diff --git a/Source/FluentMetadata.Core/Rules/StringLengthRule.cs b/Source/FluentMetadata.Core/Rules/StringLengthRule.cs index 71d0c58..05f0033 100644 --- a/Source/FluentMetadata.Core/Rules/StringLengthRule.cs +++ b/Source/FluentMetadata.Core/Rules/StringLengthRule.cs @@ -3,36 +3,69 @@ namespace FluentMetadata.Rules { // ~ System.ComponentModel.DataAnnotations.StringLengthAttribute.MaximumLength + // ~ System.ComponentModel.DataAnnotations.StringLengthAttribute.MinimumLength public class StringLengthRule : Rule { - private readonly int maxLength; + readonly int? minLength, maxLength; + + internal int? Minimum + { + get + { + return minLength; + } + } + + internal int? Maximum + { + get + { + return maxLength; + } + } public StringLengthRule(int maxLength) - : base("the string for {0} should be longer than {1} characters") + : base("the string for '{0}' should not be longer than {1} characters") + { + this.maxLength = maxLength; + } + + public StringLengthRule(int minLength, int? maxLength) + : base("'{0}' must be " + + (maxLength.HasValue ? " between {2} and {1}" : " at least {2}") + + " characters long") { + this.minLength = minLength; this.maxLength = maxLength; } public override bool IsValid(object value) { - if (value == null) + var valueAsString = value as string; + if (valueAsString == null) { - return true; + return minLength.HasValue ? false : true; } - var strValue = (string)value; - return strValue.Length <= maxLength; + + var length = valueAsString.Length; + if (maxLength.HasValue && length > maxLength || + minLength.HasValue && length < minLength) + { + return false; + } + + return true; } public override string FormatErrorMessage(string name) { - return string.Format(ErrorMessageFormat, name, maxLength); + return string.Format(ErrorMessageFormat, name, maxLength, minLength); } } - // TODO rule equivalent to System.ComponentModel.DataAnnotations.StringLengthAttribute.MinimumLength // TODO rule equivalent to System.ComponentModel.DataAnnotations.RegularExpressionAttribute - // TODO implement or delete: What does this rule validate? + //TODO [DerAlbertCom] implement or delete: What does this rule validate? public class EqualToRule : Rule { public EqualToRule(string errorMessageFormat) diff --git a/Source/FluentMetadata.EntityFramework.Specs/DbContextTest.cs b/Source/FluentMetadata.EntityFramework.Specs/DbContextTest.cs index ea6abff..f60b0bd 100644 --- a/Source/FluentMetadata.EntityFramework.Specs/DbContextTest.cs +++ b/Source/FluentMetadata.EntityFramework.Specs/DbContextTest.cs @@ -1,7 +1,5 @@ -using System; using System.Data.Entity; using System.Data.Entity.Infrastructure; -using System.Data.Entity.ModelConfiguration; using System.IO; using Xunit; @@ -14,7 +12,8 @@ public DbContextTest() Database.SetInitializer(new AlwaysRecreateDatabase()); } - [Fact] + //TODO refactor this test. It breaks the build if Entity Framework is not installed + [Fact(Skip = "This test requires Entity Framework to be installed")] public void CanCreateDbContext() { if (File.Exists("TestDatabase.sdf")) @@ -33,7 +32,6 @@ public class NoDatabaseCreate : IDatabaseInitializer where T : DbContext { public void InitializeDatabase(T context) { - } } } \ No newline at end of file diff --git a/Source/FluentMetadata.EntityFramework/EntityFrameworkAdapter.cs b/Source/FluentMetadata.EntityFramework/EntityFrameworkAdapter.cs index c6f55fe..ab1301f 100644 --- a/Source/FluentMetadata.EntityFramework/EntityFrameworkAdapter.cs +++ b/Source/FluentMetadata.EntityFramework/EntityFrameworkAdapter.cs @@ -33,7 +33,7 @@ internal void MapProperties(Type instanceType, StructuralTypeConfiguration confi { continue; } - if (!data.StringLength.HasValue && !data.Required.HasValue) + if (!data.GetMaximumLength().HasValue && !data.Required.HasValue) { continue; } @@ -47,7 +47,7 @@ internal void MapProperties(Type instanceType, StructuralTypeConfiguration confi var lambda = generator.CreateExpressionForProperty(instanceType, data.ModelName); if (lambda != null) { - var propertyConfiguration = (PropertyConfiguration) methodInfo.Invoke(configuration, new[] {lambda}); + var propertyConfiguration = (PropertyConfiguration)methodInfo.Invoke(configuration, new[] { lambda }); factory.Create(propertyConfiguration).Convert(data, propertyConfiguration); } diff --git a/Source/FluentMetadata.EntityFramework/Internal/ConfigurationAdapters/StringPropertyConfigurationAdapter.cs b/Source/FluentMetadata.EntityFramework/Internal/ConfigurationAdapters/StringPropertyConfigurationAdapter.cs index f380899..69c0c82 100644 --- a/Source/FluentMetadata.EntityFramework/Internal/ConfigurationAdapters/StringPropertyConfigurationAdapter.cs +++ b/Source/FluentMetadata.EntityFramework/Internal/ConfigurationAdapters/StringPropertyConfigurationAdapter.cs @@ -4,17 +4,19 @@ namespace FluentMetadata.EntityFramework.Internal.ConfigurationAdapters { internal class StringPropertyConfigurationAdapter : ConfigurationAdapter { - public StringPropertyConfigurationAdapter() : base(new OptionalPrimitivePropertyConfigurationAdapter()) + public StringPropertyConfigurationAdapter() + : base(new OptionalPrimitivePropertyConfigurationAdapter()) { } protected override void ConvertToConfiguration(Metadata data) { - if (!data.StringLength.HasValue) + var maxLength = data.GetMaximumLength(); + if (!maxLength.HasValue) { return; } - Configuration.MaxLength = data.StringLength; + Configuration.MaxLength = maxLength.Value; } } } \ No newline at end of file diff --git a/Source/FluentMetadata.FluentNHibernate/Conventions/FluentMetaDataConvention.cs b/Source/FluentMetadata.FluentNHibernate/Conventions/FluentMetaDataConvention.cs index 8d496e8..5b61e04 100644 --- a/Source/FluentMetadata.FluentNHibernate/Conventions/FluentMetaDataConvention.cs +++ b/Source/FluentMetadata.FluentNHibernate/Conventions/FluentMetaDataConvention.cs @@ -8,14 +8,15 @@ public class FluentMetaDataConvention : IPropertyConvention private readonly QueryFluentMetadata query = new QueryFluentMetadata(); public void Apply(IPropertyInstance instance) { - var meta = query.GetMetadataFor(instance.EntityType,instance.Property.Name); + var meta = query.GetMetadataFor(instance.EntityType, instance.Property.Name); if (meta.Required.HasValue) { ApplyRequired(meta.Required.Value, instance); } - if (meta.StringLength.HasValue) + var maxLength = meta.GetMaximumLength(); + if (maxLength.HasValue) { - ApplyStringLength(meta.StringLength.Value, instance); + ApplyStringLength(maxLength.Value, instance); } } diff --git a/Source/FluentMetadata.MVC.Specs/ComplexModel.cs b/Source/FluentMetadata.MVC.Specs/ComplexModel.cs index f406e48..6becdd5 100644 --- a/Source/FluentMetadata.MVC.Specs/ComplexModel.cs +++ b/Source/FluentMetadata.MVC.Specs/ComplexModel.cs @@ -1,22 +1,50 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Web.Mvc; namespace FluentMetadata.MVC.Specs { - public class ComplexModelMetadata:ClassMetadata + public class ComplexModelMetadata : ClassMetadata { public ComplexModelMetadata() { Class.Display.Name("Komplex"); - Property(e => e.FirstName).Display.Name("Vorname"); + Property(e => e.Id) + .Should.HiddenInput() + .Is.ReadOnly() + .Should.Not.ShowInDisplay() + .Should.Not.ShowInEditor(); + Property(e => e.FirstName) + .Display.Name("Vorname") + .Is.Not.ConvertEmptyStringToNull() + .Is.Required(); + Property(e => e.LastName) + .Display.NullText("No lastname set"); + Property(e => e.Age) + .As.Custom("Years") + .UIHint("Spinner"); + Property(e => e.Amount) + .Display.Format("{0:c}") + .Editor.Format("{0:c}"); } } [DisplayName("Komplex")] public class ComplexModel { + [HiddenInput(DisplayValue = false)] + [ReadOnly(true)] + [ScaffoldColumn(false)] + public int Id { get; set; } [DisplayName("Vorname")] + [DisplayFormat(ConvertEmptyStringToNull = false)] + [Required] public string FirstName { get; set; } + [DisplayFormat(NullDisplayText = "No lastname set")] public string LastName { get; set; } + [DataType("Years")] + [UIHint("Spinner")] public int Age { get; set; } + [DisplayFormat(DataFormatString = "{0:c}", ApplyFormatInEditMode = true)] public decimal Amount { get; set; } public char Sex { get; set; } } diff --git a/Source/FluentMetadata.MVC.Specs/ConcernOfComparingComplexModelMetadata.cs b/Source/FluentMetadata.MVC.Specs/ConcernOfComparingMetadata.cs similarity index 59% rename from Source/FluentMetadata.MVC.Specs/ConcernOfComparingComplexModelMetadata.cs rename to Source/FluentMetadata.MVC.Specs/ConcernOfComparingMetadata.cs index 6e74ed9..10817af 100644 --- a/Source/FluentMetadata.MVC.Specs/ConcernOfComparingComplexModelMetadata.cs +++ b/Source/FluentMetadata.MVC.Specs/ConcernOfComparingMetadata.cs @@ -24,7 +24,6 @@ public void SetFixture(FluentMetadataFixture data) public abstract void CreateMetadata(); - [Observation] public void Equals_ModelMetadata_Properties_Count() { @@ -81,12 +80,13 @@ public void Equals_DispalyFormatString() Assert.Equal(Expected.DisplayFormatString, Fluent.DisplayFormatString); } - [Observation] - public void Equals_Description() - { - Console.WriteLine(Expected.Description); - Assert.Equal(Expected.Description, Fluent.Description); - } + //TODO [MVC3] ModelMetadata.Description cannot be used with the default provider in MVC2 + //[Observation] + //public void Equals_Description() + //{ + // Console.WriteLine(Expected.Description); + // Assert.Equal(Expected.Description, Fluent.Description); + //} [Observation] public void Equals_EditFormatString() @@ -102,5 +102,61 @@ public void Equals_HideSurroundingHtml() Assert.Equal(Expected.HideSurroundingHtml, Fluent.HideSurroundingHtml); } + [Observation] + public void Equals_IsReadOnly() + { + Console.WriteLine(Expected.IsReadOnly); + Assert.Equal(Expected.IsReadOnly, Fluent.IsReadOnly); + } + + [Observation] + public void Equals_IsRequired() + { + Console.WriteLine(Expected.IsRequired); + Assert.Equal(Expected.IsRequired, Fluent.IsRequired); + } + + [Observation] + public void Equals_ModelType() + { + Console.WriteLine(Expected.ModelType); + Assert.Equal(Expected.ModelType, Fluent.ModelType); + } + + [Observation] + public void Equals_NullDisplayText() + { + Console.WriteLine(Expected.NullDisplayText); + Assert.Equal(Expected.NullDisplayText, Fluent.NullDisplayText); + } + + [Observation] + public void Equals_ShowForDisplay() + { + Console.WriteLine(Expected.ShowForDisplay); + Assert.Equal(Expected.ShowForDisplay, Fluent.ShowForDisplay); + } + + [Observation] + public void Equals_ShowForEdit() + { + Console.WriteLine(Expected.ShowForEdit); + Assert.Equal(Expected.ShowForEdit, Fluent.ShowForEdit); + } + + [Observation] + public void Equals_TemplateHint() + { + Console.WriteLine(Expected.TemplateHint); + Assert.Equal(Expected.TemplateHint, Fluent.TemplateHint); + } + + //TODO [MVC3] ModelMetadata.Watermark cannot be used with the default provider in MVC2 + //[Observation] + //public void Equals_Watermark() + //{ + // Console.WriteLine(Expected.Watermark); + // Assert.Equal(Expected.Watermark, Fluent.Watermark); + //} } } \ No newline at end of file diff --git a/Source/FluentMetadata.MVC.Specs/ConcernOfValidationSampleData.cs b/Source/FluentMetadata.MVC.Specs/ConcernOfValidationComplexData.cs similarity index 57% rename from Source/FluentMetadata.MVC.Specs/ConcernOfValidationSampleData.cs rename to Source/FluentMetadata.MVC.Specs/ConcernOfValidationComplexData.cs index 55c434e..d227c59 100644 --- a/Source/FluentMetadata.MVC.Specs/ConcernOfValidationSampleData.cs +++ b/Source/FluentMetadata.MVC.Specs/ConcernOfValidationComplexData.cs @@ -1,13 +1,12 @@ -using System; using Xunit; namespace FluentMetadata.MVC.Specs { - [Concern(typeof(SampleModel),"Validation of SampleData")] - public abstract class ConcernOfValidationSampleData : InstanceContextSpecification + [Concern(typeof(ComplexModel), "Validation of ComplexData")] + public abstract class ConcernOfValidationComplexData : InstanceContextSpecification , IUseFixture { - protected readonly SampleModel Model = new SampleModel(); + protected readonly ComplexModel Model = new ComplexModel(); protected override DummyController CreateSut() { @@ -19,7 +18,7 @@ public void SetFixture(FluentMetadataFixture data) } } - public class When_vorname_is_required_and_not_set : ConcernOfValidationSampleData + public class When_FirstName_is_required_and_not_set : ConcernOfValidationComplexData { protected override void Because() { @@ -29,17 +28,18 @@ protected override void Because() [Observation] public void Should_one_error_vorname() { - Sut.ModelState["VornameRequired"].Errors.Count.ShouldBeEqualTo(1); + Sut.ModelState["FirstName"].Errors.Count.ShouldBeEqualTo(1); } } - public class When_vorname_is_required_and_is_set: ConcernOfValidationSampleData + public class When_FirstName_is_required_and_is_set : ConcernOfValidationComplexData { protected override void EstablishContext() { base.EstablishContext(); - Model.VornameRequired = "Albert"; + Model.FirstName = "Albert"; } + protected override void Because() { Sut.ValidateModel(Model); @@ -48,7 +48,7 @@ protected override void Because() [Observation] public void Should_no_error_on_vorname() { - Sut.ModelState["VornameRequired"].ShouldBeNull(); + Sut.ModelState["FirstName"].ShouldBeNull(); } } } \ No newline at end of file diff --git a/Source/FluentMetadata.MVC.Specs/FluentMetadata.MVC.Specs.csproj b/Source/FluentMetadata.MVC.Specs/FluentMetadata.MVC.Specs.csproj index dcc0914..6815364 100644 --- a/Source/FluentMetadata.MVC.Specs/FluentMetadata.MVC.Specs.csproj +++ b/Source/FluentMetadata.MVC.Specs/FluentMetadata.MVC.Specs.csproj @@ -34,11 +34,9 @@ + - - False - ..\..\..\..\..\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 2\Assemblies\System.Web.Mvc.dll - + @@ -54,15 +52,12 @@ - - + + - - - @@ -75,6 +70,9 @@ FluentMetadata.MVC + + +