Skip to content

Commit

Permalink
Pluralize generated class property names (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
hhoangnl authored Oct 25, 2020
1 parent c0e8693 commit d2507f3
Show file tree
Hide file tree
Showing 24 changed files with 365 additions and 106 deletions.
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Here is a visual representation of the above:
Installation is pretty simple, just copy and paste the following command:

~~~
dotnet tool install --global dotnet-dash --version 0.5.0-alpha
dotnet tool install --global dotnet-dash --version 0.6.0-alpha
~~~

If you have successfully installed Dash, the following command will bring up the help screen:
Expand All @@ -33,7 +33,7 @@ dotnet dash --help
Updating to the latest, or different, version of Dash is equally simple:

~~~
dotnet tool update --global dotnet-dash --version 0.5.0-alpha
dotnet tool update --global dotnet-dash --version 0.6.0-alpha
~~~

## Hello World example
Expand Down
14 changes: 14 additions & 0 deletions docs/model-file/configuration-section.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ The header that will be injected as comments in every generated file.
<auto-generated>This code was generated by dotnet dash CLI. Manual changes to this file will be overwritten if the code is regenerated.</auto-generated>
~~~

#### Pluralize
Pluralize generated class property names. This only applies when a class property is a collection.

**data type:** `boolean`

**default:**
~~~ xml
true
~~~

!! note

Only English pluralization is supported

#### Templates
Specify the templates that will be used for code generation.

Expand Down
6 changes: 4 additions & 2 deletions src/Dash/src/Dash/Dash.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Nullable>enable</Nullable>
<Version>0.5.1-alpha</Version>
<Version>0.6.0-alpha</Version>
<PackAsTool>true</PackAsTool>
<ToolCommandName>dotnet-dash</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
<AssemblyName>dotnet-dash</AssemblyName>
<Authors>Huy Hoang</Authors>
<CodeAnalysisRuleSet>..\.sonarlint\dotnet-dash_dashcsharp.ruleset</CodeAnalysisRuleSet>
<Description>Dash is a command-line tool for fast model-driven code generation.</Description>
<PackageReleaseNotes>Fixed EF core warning: "The 'bool' property 'X' on entity type 'Y' is configured with a database-generated default. This default will always be used for inserts when the property has the value 'false', since this is the CLR default for the 'bool' type. Consider using the nullable 'bool?' type instead so that the default will only be used for inserts when the property value is 'null'."</PackageReleaseNotes>
<PackageReleaseNotes>Implemented feature to pluralize the names of generated class properties (where applicable)</PackageReleaseNotes>
<PackageProjectUrl>https://github.com/dotnet-dash</PackageProjectUrl>
<PackageIcon>packageicon.png</PackageIcon>
<Copyright>Huy Hoang</Copyright>
Expand Down Expand Up @@ -44,6 +44,7 @@

<ItemGroup>
<PackageReference Include="CsvHelper" Version="15.0.8" />
<PackageReference Include="Inflector.NetStandard" Version="1.2.2" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="3.7.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.7.0" />
Expand All @@ -65,6 +66,7 @@

<ItemGroup>
<Folder Include="PreprocessingSteps\Default\" />
<Folder Include="Templates\ef\" />
</ItemGroup>

</Project>
3 changes: 2 additions & 1 deletion src/Dash/src/Dash/Engine/Generator/DefaultGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ public async Task Generate(SourceCodeNode model)

async Task Generate(string outputFilename, IEnumerable<EntityModel> listOfEntities)
{
var output = await _templateTransformer.Transform(template, listOfEntities);
var options = new TemplateOptions(template, listOfEntities, model.ConfigurationNode.Pluralize);
var output = await _templateTransformer.Transform(options);
output = AddHeader(model.ConfigurationNode.Header!, output);
SaveFile(directory, outputFilename.AppendFilenameSuffix(model.ConfigurationNode.AutogenSuffix), output);
}
Expand Down
4 changes: 1 addition & 3 deletions src/Dash/src/Dash/Engine/ITemplateTransformer.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// Copyright (c) Huy Hoang. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Collections.Generic;
using System.Threading.Tasks;
using Dash.Engine.Models;

namespace Dash.Engine
{
public interface ITemplateTransformer
{
Task<string> Transform(string templateText, IEnumerable<EntityModel> entities);
Task<string> Transform(TemplateOptions options);
}
}
16 changes: 4 additions & 12 deletions src/Dash/src/Dash/Engine/Parsers/DefaultSourceCodeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using Dash.Extensions;
using Dash.Nodes;

#pragma warning disable S1200 // Classes should not be coupled to too many other classes (Single Responsibility Principle)
namespace Dash.Engine.Parsers
{
public class DefaultSourceCodeParser : ISourceCodeParser
Expand Down Expand Up @@ -88,19 +87,13 @@ private void TraverseRelationshipProperties(EntityDeclarationNode entityDeclarat
foreach (var property in objectProperties)
{
ProcessRelationshipProperty(property, "@@Has", (name, referencedEntity) =>
{
entityDeclarationNode.Has.Add(new HasReferenceDeclarationNode(entityDeclarationNode, name, referencedEntity));
});
entityDeclarationNode.AddHasDeclaration(name, referencedEntity));

ProcessRelationshipProperty(property, "@@Has Many", (name, referencedEntity) =>
{
entityDeclarationNode.HasMany.Add(new HasManyReferenceDeclarationNode(entityDeclarationNode, name, referencedEntity));
});
entityDeclarationNode.AddHasManyDeclaration(name, referencedEntity));

ProcessRelationshipProperty(property, "@@Has And Belongs To Many", (name, referencedEntity) =>
{
entityDeclarationNode.HasAndBelongsToMany.Add(new HasAndBelongsToManyDeclarationNode(entityDeclarationNode, name, referencedEntity));
});
entityDeclarationNode.AddHasAndBelongsToManyDeclarationNode(name, referencedEntity));
}
}

Expand Down Expand Up @@ -200,5 +193,4 @@ private void TraverseAttributes(EntityDeclarationNode entityDeclarationNode, Jso
}
}
}
}
#pragma warning restore S1200 // Classes should not be coupled to too many other classes (Single Responsibility Principle)
}
22 changes: 22 additions & 0 deletions src/Dash/src/Dash/Engine/TemplateOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Huy Hoang. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Collections.Generic;
using Dash.Engine.Models;

namespace Dash.Engine
{
public class TemplateOptions
{
public TemplateOptions(string templateText, IEnumerable<EntityModel> entities, bool pluralize)
{
TemplateText = templateText;
Entities = entities;
Pluralize = pluralize;
}

public string TemplateText { get; }
public IEnumerable<EntityModel> Entities { get; }
public bool Pluralize { get; }
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Copyright (c) Huy Hoang. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Globalization;
using Dash.Engine.Models;

namespace Dash.Engine.TemplateTransformers.Scriban
{
public static class CSharpOutputHelpers
{
private static readonly Inflector.Inflector Inflector = new Inflector.Inflector(new CultureInfo("en-US"));
public static string GetCSharpLiteral(object value)
{
if (value == null)
Expand Down Expand Up @@ -35,7 +37,7 @@ public static string GetCSharpLiteral(object value)

public static string GetPropertyDefaultValueAssignment(object value)
{
const string code = "= null!;";
const string code = " = null!;";

if (value is ReferencedEntityModel referencedEntity)
{
Expand Down Expand Up @@ -71,5 +73,15 @@ public static string GetPropertyDefaultValueAssignment(object value)

return string.Empty;
}

public static string? FormatName(object value, bool pluralize)
{
if (pluralize && value is string s)
{
return Inflector.Pluralize(s);
}

return value?.ToString();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// Copyright (c) Huy Hoang. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Dash.Application;
using Dash.Engine.Models;
using Dash.Extensions;
using Microsoft.Extensions.Options;
using Scriban;
Expand All @@ -22,15 +20,16 @@ public ScribanTemplateTransformer(IOptions<DashOptions> options)
_options = options.Value;
}

public async Task<string> Transform(string templateText, IEnumerable<EntityModel> entities)
public async Task<string> Transform(TemplateOptions options)
{
var template = Template.Parse(templateText);
var template = Template.Parse(options.TemplateText);

var scriptObject = new ScriptObject
{
{"namespace", _options.DefaultNamespace!},
{"modelName", Path.GetFileNameWithoutExtension(_options.InputFile!).StartWithCapitalLetter()},
{"entities", entities}
{"entities", options.Entities},
{"pluralize", options.Pluralize},
};
scriptObject.Import(typeof(CSharpOutputHelpers));

Expand Down
3 changes: 1 addition & 2 deletions src/Dash/src/Dash/Engine/Visitors/DefaultModelBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ public DefaultModelBuilder(

public override Task Visit(EntityDeclarationNode node)
{
var model = new EntityModel(node.Name);
_modelRepository.Add(model);
_modelRepository.Add(new EntityModel(node.Name));
return base.Visit(node);
}

Expand Down
5 changes: 5 additions & 0 deletions src/Dash/src/Dash/Nodes/ConfigurationNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public class ConfigurationNode : AstNode

public string? Header { get; set; } = "<auto-generated>This code was generated by dotnet dash CLI. Manual changes to this file will be overwritten if the code is regenerated.</auto-generated>";

/// <summary>
/// Pluralize generated property names (if applicable)
/// </summary>
public bool Pluralize { get; set; } = true;

public IList<TemplateNode> Templates { get; set; } = new List<TemplateNode>();

public ConfigurationNode AddTemplateNode(string template, string output)
Expand Down
25 changes: 13 additions & 12 deletions src/Dash/src/Dash/Nodes/EntityDeclarationNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ public EntityDeclarationNode(ModelNode parent, string name)

public IEnumerable<AbstractDeclarationNode> AbstractDeclarationNodes => _astNodes.OfType<AbstractDeclarationNode>();

public IList<HasReferenceDeclarationNode> Has { get; } = new List<HasReferenceDeclarationNode>();
public IEnumerable<HasReferenceDeclarationNode> Has => _astNodes.OfType<HasReferenceDeclarationNode>();

public IList<HasManyReferenceDeclarationNode> HasMany { get; } = new List<HasManyReferenceDeclarationNode>();
public IEnumerable<HasManyReferenceDeclarationNode> HasMany => _astNodes.OfType<HasManyReferenceDeclarationNode>();

public IList<HasAndBelongsToManyDeclarationNode> HasAndBelongsToMany { get; } = new List<HasAndBelongsToManyDeclarationNode>();
public IEnumerable<HasAndBelongsToManyDeclarationNode> HasAndBelongsToMany => _astNodes.OfType<HasAndBelongsToManyDeclarationNode>();

public IList<AstNode> ChildNodes { get; } = new List<AstNode>();

Expand Down Expand Up @@ -69,29 +69,30 @@ public InheritanceDeclarationNode AddInheritanceDeclaration(string inheritedEnti
public EntityDeclarationNode AddAbstractDeclarationNode(bool value)
{
_astNodes.Add(new AbstractDeclarationNode(this, value));

return this;
}

public EntityDeclarationNode AddHasDeclaration(string name, string referencedEntity)
{
var has = new HasReferenceDeclarationNode(this, name, referencedEntity);
Has.Add(has);

_astNodes.Add(new HasReferenceDeclarationNode(this, name, referencedEntity));
return this;
}

public void AddCsvSeedDeclarationNode(Uri uri, bool firstLineIsHeader, string? delimiter, IDictionary<string, string> mapHeaders)
public EntityDeclarationNode AddHasManyDeclaration(string name, string referencedEntity)
{
ChildNodes.Add(new CsvSeedDeclarationNode(this, uri, firstLineIsHeader, delimiter, mapHeaders));
_astNodes.Add(new HasManyReferenceDeclarationNode(this, name, referencedEntity));
return this;
}

public EntityDeclarationNode AddHasAndBelongsToManyDeclarationNode(string name, string referencedEntity)
{
var hasAndBelongsToManyDeclarationNode = new HasAndBelongsToManyDeclarationNode(this, name, referencedEntity);
HasAndBelongsToMany.Add(hasAndBelongsToManyDeclarationNode);

_astNodes.Add(new HasAndBelongsToManyDeclarationNode(this, name, referencedEntity));
return this;
}

public void AddCsvSeedDeclarationNode(Uri uri, bool firstLineIsHeader, string? delimiter, IDictionary<string, string> mapHeaders)
{
ChildNodes.Add(new CsvSeedDeclarationNode(this, uri, firstLineIsHeader, delimiter, mapHeaders));
}
}
}
2 changes: 1 addition & 1 deletion src/Dash/src/Dash/Templates/efcontext
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace {{ namespace }}
}

{{- for e in entities }}
public DbSet<{{ e.name }}> {{ e.name }} => Set<{{ e.name }}>();
public DbSet<{{ e.name }}> {{ e.name | format_name pluralize }} => Set<{{ e.name }}>();
{{- end }}

protected override void OnModelCreating(ModelBuilder modelBuilder)
Expand Down
2 changes: 1 addition & 1 deletion src/Dash/src/Dash/Templates/efpoco
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace {{ namespace }}
{{- end }}

{{- for r in e.collection_references }}
public ICollection<{{ r.entity_model }}> {{ r.reference_name }} { get; set; } = null!;
public ICollection<{{ r.entity_model }}> {{ r.reference_name | format_name `false` }} { get; set; } = null!;
{{- end }}
}
{{- end }}
Expand Down
10 changes: 10 additions & 0 deletions src/Dash/test/Dash.Tests/Dash.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<None Remove="Samples\HasNullable.json" />
<None Remove="Samples\HelloWorld.json" />
<None Remove="Samples\Inheritance.json" />
<None Remove="Samples\IntegrationTests\Blog.json" />
<None Remove="Samples\OverrideBaseId.json" />
<None Remove="Samples\Seed.json" />
<None Remove="Samples\Smoketest1.json" />
Expand All @@ -38,6 +39,9 @@
<Content Include="Samples\HasArrayValue.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Samples\IntegrationTests\Blog.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Samples\Seed.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
Expand Down Expand Up @@ -105,6 +109,12 @@
</ItemGroup>

<ItemGroup>
<None Update="Samples\ExpectedOutput\Blog_EfContext">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Samples\ExpectedOutput\Blog_EfPoco">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Samples\ExpectedOutput\HelloWorld">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Expand Down
Loading

0 comments on commit d2507f3

Please sign in to comment.