Skip to content

Commit

Permalink
Fix String.Format to handle formatting of multiple types correctly,…
Browse files Browse the repository at this point in the history
… not only DateTime (#31)

* Fix some unit tests not passing due to the ambient culture

* Fix "String.Format" helper to handle generic formatting

"String.Format" used to only support "DateTime" for formatting.

As for now, as soon as the value implements "IFormattable" that interface is used to perform the formatting. Also, if the format provider exposes a "ICustomFormatter" service, that formatter is used first for formatting the value.

The formatting falls back to "ToString()" or to an empty string if the value to format is null.

* Replaced "" by string.Empty to adhere to coding standards

Co-authored-by: Damien Braillard <damien.braillard@otis.com>
  • Loading branch information
DamienBraillard and Damien Braillard committed Apr 23, 2021
1 parent d649edc commit eda63bf
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 8 deletions.
22 changes: 14 additions & 8 deletions src/Handlebars.Net.Helpers/Helpers/StringHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,16 +344,22 @@ public string Uppercase(string value)
}

[HandlebarsWriter(WriterType.String)]
public string Format(object value, string format)
public string Format(object? value, string format)
{
switch (value)
{
case DateTime dateTime:
return dateTime.ToString(format, Context.Configuration.FormatProvider);
var formatProvider = Context.Configuration.FormatProvider;

default:
throw new NotSupportedException($"The method {nameof(Format)} cannot be used on value '{value}' of Type '{value?.GetType()}'.");
}
// Attempt using a custom formatter from the format provider (if any)
var customFormatter = formatProvider?.GetFormat(typeof(ICustomFormatter)) as ICustomFormatter;
var formattedValue = customFormatter?.Format(format, value, formatProvider);

// Fallback to IFormattable
formattedValue ??= (value as IFormattable)?.ToString(format, formatProvider);

// Fallback to ToString
formattedValue ??= value?.ToString();

// Done
return formattedValue ?? string.Empty;
}

public StringHelpers(IHandlebars context) : base(context)
Expand Down
41 changes: 41 additions & 0 deletions test/Handlebars.Net.Helpers.Tests/Helpers/StringHelpersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,5 +312,46 @@ public void Format_DateTime(string format, string expected)
result1.Should().Be(expected);
result2.Should().Be(expected);
}

[Theory]
[InlineData("N0", "1")]
[InlineData("N1", "1.2")]
[InlineData("N2", "1.20")]
public void Format_Decimal(string format, string expected)
{
// Arrange
var value = 1.2m;

// Act
var result1 = _sut.Format(value, format);
var result2 = _sut.Format((decimal?)value, format);

// Assert
result1.Should().Be(expected);
result2.Should().Be(expected);
}

[Fact]
public void Format_NotIFormattableType()
{
// Arrange
var value = new Version(1, 2, 3, 4);

// Act
var result = _sut.Format(value, "should-be-ignored");

// Assert
result.Should().Be(value.ToString());
}

[Fact]
public void Format_Null()
{
// Act
var result = _sut.Format(null, "should-be-ignored");

// Assert
result.Should().BeEmpty();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using FluentAssertions;
using HandlebarsDotNet.Helpers.Utils;
using Moq;
Expand All @@ -21,6 +22,7 @@ public StringHelpersTemplateTests()
_dateTimeServiceMock.Setup(d => d.UtcNow()).Returns(DateTimeNow.ToUniversalTime);

_handlebarsContext = Handlebars.Create();
_handlebarsContext.Configuration.FormatProvider = CultureInfo.InvariantCulture;

HandlebarsHelpers.Register(_handlebarsContext, o =>
{
Expand Down

0 comments on commit eda63bf

Please sign in to comment.