Skip to content
This repository was archived by the owner on Oct 5, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Endjin.Templify.Domain.Contracts.Packager.Tokeniser {

public interface IFunctionTokenizer {

string TokenizeContent( string Content );

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
namespace Endjin.Templify.Domain.Domain.Packager.Tokeniser {
using System;
using System.ComponentModel.Composition;
using System.Text.RegularExpressions;
using Endjin.Templify.Domain.Contracts.Packager.Tokeniser;

[Export( typeof(IFunctionTokenizer) )]
public class DateFunctionTokenizer : IFunctionTokenizer {
private readonly IDateTimeNowFactory dateTimeNowFactory;
private readonly Regex dateRegex;

[ImportingConstructor]
public DateFunctionTokenizer( IDateTimeNowFactory DateTimeNowFactory ) {
if ( DateTimeNowFactory == null ) {
throw new ArgumentNullException( "DateTimeNowFactory" );
}
this.dateTimeNowFactory = DateTimeNowFactory;
this.dateRegex = new Regex( @"__DATE\(([^)]*)\)__", RegexOptions.Compiled );
}

public string TokenizeContent( string Content ) {
string results = Content;
if ( !string.IsNullOrWhiteSpace( Content ) ) {
results = dateRegex.Replace(
Content, match => {
string format = "";
if ( match.Groups.Count > 1 ) {
format = match.Groups[1].Value;
}
if ( string.IsNullOrWhiteSpace( format ) ) {
format = "d"; // Short date
}
return this.dateTimeNowFactory.Now().ToString( format );
}
);
}
return results;
}

}

// For testability:

public interface IDateTimeNowFactory {
DateTime Now();
}

[Export( typeof(IDateTimeNowFactory) )]
public class DateTimeNowFactory : IDateTimeNowFactory {
public DateTime Now() {
return DateTime.Now;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace Endjin.Templify.Domain.Domain.Packager.Tokeniser {
using System;
using System.ComponentModel.Composition;
using System.Text.RegularExpressions;
using Endjin.Templify.Domain.Contracts.Packager.Tokeniser;

[Export( typeof(IFunctionTokenizer) )]
public class GuidFunctionTokenizer : IFunctionTokenizer {
private readonly IGuidFactory guidFactory;
private readonly Regex guidRegex;

[ImportingConstructor]
public GuidFunctionTokenizer( IGuidFactory GuidFactory ) {
if ( GuidFactory == null ) {
throw new ArgumentNullException( "GuidFactory" );
}
this.guidFactory = GuidFactory;
this.guidRegex = new Regex( @"__GUID\(([A-Za-z]?)\)__", RegexOptions.Compiled );
}

public string TokenizeContent( string Content ) {
string results = Content;
if ( !string.IsNullOrWhiteSpace( Content ) ) {
results = guidRegex.Replace(
Content, match => {
string format = "";
if ( match.Groups.Count > 1 ) {
format = match.Groups[1].Value;
}
Guid guid = this.guidFactory.NewGuid();
return guid.ToString( format ); // Valid format values: http://msdn.microsoft.com/en-us/library/97af8hh4.aspx
}
);
}
return results;
}

}

// For testability:

public interface IGuidFactory {
Guid NewGuid();
}

[Export( typeof(IGuidFactory) )]
public class GuidFactory : IGuidFactory {
public Guid NewGuid() {
return Guid.NewGuid();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ namespace Endjin.Templify.Domain.Domain.Packager.Tokeniser
public class TemplateTokeniser : ITemplateTokeniser
{
private readonly IFileContentProcessor fileContentProcessor;
private readonly IEnumerable<IFunctionTokenizer> functionTokenizers;
private readonly IRenameFileProcessor renameFileProcessor;

[ImportingConstructor]
public TemplateTokeniser(IRenameFileProcessor renameFileProcessor, IFileContentProcessor fileContentProcessor)
public TemplateTokeniser(IRenameFileProcessor renameFileProcessor, IFileContentProcessor fileContentProcessor, [ImportMany] IEnumerable<IFunctionTokenizer> FunctionTokenizers)
{
this.renameFileProcessor = renameFileProcessor;
this.fileContentProcessor = fileContentProcessor;
this.functionTokenizers = FunctionTokenizers;
}

public void TokeniseDirectoryAndFilePaths(string file, Dictionary<string, string> tokens)
Expand All @@ -38,9 +40,13 @@ public void TokeniseFileContent(string file, Dictionary<string, string> tokens)
this.fileContentProcessor.WriteContents(file, contents);
}

private static string Replace(Dictionary<string, string> tokens, string value)
private string Replace(Dictionary<string, string> tokens, string value)
{
return tokens.Aggregate(value, (current, token) => Regex.Replace(current, token.Key, match => token.Value));
string val = tokens.Aggregate(value, (current, token) => Regex.Replace(current, token.Key, match => token.Value));
if ( this.functionTokenizers != null ) {
val = this.functionTokenizers.Aggregate(val, (current, tokenizer) => tokenizer.TokenizeContent(current));
}
return val;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
<Compile Include="Contracts\Packager\Processors\IRenameFileProcessor.cs" />
<Compile Include="Contracts\Packager\Specifications\IFileExclusionsSpecification.cs" />
<Compile Include="Contracts\Packager\Tokeniser\IEnvironmentalTokenResolver.cs" />
<Compile Include="Contracts\Packager\Tokeniser\IFunctionTokenizer.cs" />
<Compile Include="Contracts\Packager\Tokeniser\ITemplateTokeniser.cs" />
<Compile Include="Contracts\Packager\Tokeniser\IReservedTokenResolver.cs" />
<Compile Include="Contracts\Packages\IPackageFactory.cs" />
Expand All @@ -93,7 +94,9 @@
<Compile Include="Domain\Packager\Processors\PackageProcessor.cs" />
<Compile Include="Domain\Packager\Processors\RenameFileProcessor.cs" />
<Compile Include="Domain\Packager\Specifications\FileExclusionSpecification.cs" />
<Compile Include="Domain\Packager\Tokeniser\DateFunctionTokenizer.cs" />
<Compile Include="Domain\Packager\Tokeniser\EnvironmentalTokenResolver.cs" />
<Compile Include="Domain\Packager\Tokeniser\GuidFunctionTokenizer.cs" />
<Compile Include="Domain\Packager\Tokeniser\PackageTokeniser.cs" />
<Compile Include="Domain\Packager\Tokeniser\TemplateTokeniser.cs" />
<Compile Include="Domain\Packager\Tokeniser\ReservedTokenResolver.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace Endjin.Templify.Specifications.Domain.Packager.Tokeniser {
using System;
using Endjin.Templify.Domain.Domain.Packager.Tokeniser;
using NUnit.Framework;

[TestFixture]
public class DateFunctionTokenizerSpecs {

private static readonly DateTime testDate = new DateTime( 2000, 5, 12, 2, 52, 19 ); // FRAGILE: Tests hard-code this in expected results

[TestCase( "", "" )]
[TestCase( (string)null, (string)null )]
[TestCase( "__DATE()__", "5/12/2000" )]
[TestCase( "abc__DATE()__def", "abc5/12/2000def" )]
[TestCase( "abc__DATE(__def", "abc__DATE(__def" )]
[TestCase( "abc__DATE()__def__DATE()__ghi", "abc5/12/2000def5/12/2000ghi" )]
[TestCase( "abc\n__DATE()__\ndef\n__DATE()__\nghi", "abc\n5/12/2000\ndef\n5/12/2000\nghi" )]
public void ReplacementExpressions_Work( string Content, string Expected ) {

// Arrange
MockDateTimeNowFactory mock = new MockDateTimeNowFactory( testDate );
DateFunctionTokenizer d = new DateFunctionTokenizer( mock );

// Act
string actual = d.TokenizeContent( Content );

// Assert
Assert.That( actual, Is.EqualTo( Expected ) );

}

[TestCase( "" )]
[TestCase( "d" )]
[TestCase( "D" )]
[TestCase( "t" )]
[TestCase( "T" )]
[TestCase( "f" )]
[TestCase( "F" )]
[TestCase( "dd-MM-yyyy hh:mm:ss tt" )]
[TestCase( "yyyy" )]
[TestCase( "ddd" )]
public void Format_Works( string Format ) {

// Arrange
string content = "__DATE(" + Format + ")__";
if ( string.IsNullOrEmpty( Format ) ) {
Format = "d"; // FRAGILE: Duplicates business logic in class under test
}
MockDateTimeNowFactory mock = new MockDateTimeNowFactory( testDate );
DateFunctionTokenizer d = new DateFunctionTokenizer( mock );

// Act
string actual = d.TokenizeContent( content );

// Assert
string expected = mock.Now().ToString( Format );
Assert.That( actual, Is.EqualTo( expected ) );
}


public class MockDateTimeNowFactory : IDateTimeNowFactory {
private readonly DateTime dateTime;

public MockDateTimeNowFactory( DateTime DateTime ) {
this.dateTime = DateTime;
}

public DateTime Now() {
return dateTime;
}

}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
namespace Endjin.Templify.Specifications.Domain.Packager.Tokeniser {
using System;
using System.Collections.Generic;
using Endjin.Templify.Domain.Domain.Packager.Tokeniser;
using NUnit.Framework;

[TestFixture]
public class GuidFunctionTokenizerSpecs {

[TestCase( "", "" )]
[TestCase( (string)null, (string)null )]
[TestCase( "__GUID()__", "00000000-0000-0000-0000-000000000000" )]
[TestCase( "abc__GUID()__def", "abc00000000-0000-0000-0000-000000000000def" )]
[TestCase( "abc__GUID(__def", "abc__GUID(__def" )]
[TestCase( "abc__GUID()__def__GUID()__ghi", "abc00000000-0000-0000-0000-000000000000def00000000-0000-0000-0000-000000000000ghi" )]
[TestCase( "abc\n__GUID()__\ndef\n__GUID()__\nghi", "abc\n00000000-0000-0000-0000-000000000000\ndef\n00000000-0000-0000-0000-000000000000\nghi" )]
public void ReplacementExpressions_Work( string Content, string Expected ) {

// Arrange
MockGuidFactory mock = new MockGuidFactory( Guid.Empty );
GuidFunctionTokenizer g = new GuidFunctionTokenizer( mock );

// Act
string actual = g.TokenizeContent( Content );

// Assert
Assert.That( actual, Is.EqualTo( Expected ) );
}

[Test]
public void EachTokenGetsUniqueGuid() {

// Arrange
List<Guid> guids = new List<Guid>() {
Guid.NewGuid(),
Guid.NewGuid(),
Guid.NewGuid()
};
string content = "__GUID()__ __GUID()__ __GUID()__";
string expected = string.Format( "{0} {1} {2}", guids[0], guids[1], guids[2] );
MockGuidListFactory mock = new MockGuidListFactory( guids );
GuidFunctionTokenizer g = new GuidFunctionTokenizer( mock );

// Act
string actual = g.TokenizeContent( content );

// Assert
Assert.That( actual, Is.EqualTo( expected ) );
}

[TestCase( "" )]
[TestCase( "N" )]
[TestCase( "D" )]
[TestCase( "B" )]
[TestCase( "P" )]
[TestCase( "X" )]
public void Format_Works( string Format ) {

// Arrange
string content = "__GUID(" + Format + ")__";
MockGuidFactory mock = new MockGuidFactory( Guid.NewGuid() );
GuidFunctionTokenizer g = new GuidFunctionTokenizer( mock );

// Act
string actual = g.TokenizeContent( content );

// Assert
string expected = mock.NewGuid().ToString( Format );
Assert.That( actual, Is.EqualTo( expected ) );
}


public class MockGuidFactory : IGuidFactory {
private readonly Guid guid;

public MockGuidFactory( Guid Guid ) {
this.guid = Guid;
}

public Guid NewGuid() {
return guid;
}
}

public class MockGuidListFactory : IGuidFactory {
private readonly List<Guid> guids;
private int step = -1;

public MockGuidListFactory( List<Guid> Guids ) {
this.guids = Guids;
}

public Guid NewGuid() {
// The next one
step++;
Guid g = guids.Count > step ? guids[step] : Guid.Empty;
return g;
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
<Name>Endjin.Templify.Domain</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down