Skip to content

Commit

Permalink
Merge pull request #1 from DyegoMaas/feature/bridge-from-onebuilder-t…
Browse files Browse the repository at this point in the history
…o-manybuilder

Feature/bridge from onebuilder to manybuilder
  • Loading branch information
DyegoMaas authored May 23, 2021
2 parents 1ab5ee3 + d8e6817 commit 3450a1e
Show file tree
Hide file tree
Showing 34 changed files with 1,258 additions and 449 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ Additionally, we can create multiple sets of objects:
var hundredPeople = MagicFactory.For<Person>()
.Many(10).With(x => x.Age = 5)
.Plus(20).With(x => x.Age = 60)
.Build(); // creates 10 persons with age 5, and 20 with age 60
.PlusOne().With(x => x.Age = 100)
.Build(); // creates 10 persons with age 5, 20 with age 60, and 1 with age 100
```

### Custom Factories
Expand Down Expand Up @@ -172,6 +173,7 @@ dotnet stryker
## Roadmap

- Support multi-level object creation
- Support custom constructor scoped by builder (for now, custom constructors are shared along the linked builders)
- The default factory could have an option to fill all properties with valid values
- Allow configuration by rules
- Allow sequences in numbers properties and things like email, etc
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using ForeverFactory.Builders.Common;
using ForeverFactory.Transforms;

namespace ForeverFactory.Builders.Adapters
{
internal class MagicFactoryOneBuilderToLinkedOneBuilderAdapter<T> : ILinkedBuilder<T>
where T : class
{
private readonly IOneBuilder<T> _builder;
private readonly TransformList<T> _defaultTransforms;
private readonly Func<T> _customConstructor;

public MagicFactoryOneBuilderToLinkedOneBuilderAdapter(
MagicFactory<T> builder,
TransformList<T> defaultTransforms,
Func<T> customConstructor)
{
_builder = builder;
_defaultTransforms = defaultTransforms;
_customConstructor = customConstructor;
}

public ILinkedOneBuilder<T> PlusOne()
{
return new LinkedOneBuilder<T>(
new SharedContext<T>(_defaultTransforms, _customConstructor),
previous: this
);
}

public IManyBuilder<T> Plus(int count)
{
return new LinkedManyBuilder<T>(count,
new SharedContext<T>(_defaultTransforms, _customConstructor),
previous: null
);
}

public IEnumerable<T> Build()
{
yield return _builder.Build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Collections.Generic;

namespace ForeverFactory.Builders.Adapters
{
internal class OneBuilderToLinkedOneBuilderAdapter<T> : ILinkedBuilder<T>
where T : class
{
private readonly OneBuilder<T> _builder;

public OneBuilderToLinkedOneBuilderAdapter(OneBuilder<T> builder)
{
_builder = builder;
}

public ILinkedOneBuilder<T> PlusOne()
{
return new LinkedOneBuilder<T>(_builder.SharedContext, this);
}

public IManyBuilder<T> Plus(int count)
{
return new LinkedManyBuilder<T>(count,
sharedContext: _builder.SharedContext,
previous: null
);
}

public IEnumerable<T> Build()
{
yield return _builder.Build();
}
}
}
36 changes: 36 additions & 0 deletions src/ForeverFactory/Builders/Common/BaseBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using ForeverFactory.Transforms;

namespace ForeverFactory.Builders.Common
{
internal abstract class BaseBuilder<T>
where T : class
{
public ISharedContext<T> SharedContext { get; }
private TransformList<T> Transforms { get; }

protected BaseBuilder(ISharedContext<T> sharedContext)
{
SharedContext = sharedContext;
Transforms = new TransformList<T>();
}

protected void AddTransform(Transform<T> transform)
{
Transforms.Add(transform);
}

protected IEnumerable<Transform<T>> GetTransformsToApply()
{
return SharedContext.DefaultTransforms
.Union(Transforms)
.AsEnumerable();
}

protected T CreateInstance()
{
return SharedContext.CustomConstructor?.Invoke() ?? Activator.CreateInstance<T>();
}
}
}
12 changes: 12 additions & 0 deletions src/ForeverFactory/Builders/Common/ISharedContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using ForeverFactory.Transforms;

namespace ForeverFactory.Builders.Common
{
internal interface ISharedContext<T>
where T : class
{
TransformList<T> DefaultTransforms { get; }
Func<T> CustomConstructor { get; }
}
}
18 changes: 18 additions & 0 deletions src/ForeverFactory/Builders/Common/SharedContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using ForeverFactory.Transforms;

namespace ForeverFactory.Builders.Common
{
internal class SharedContext<T> : ISharedContext<T>
where T : class
{
public TransformList<T> DefaultTransforms { get; }
public Func<T> CustomConstructor { get; }

public SharedContext(TransformList<T> defaultTransforms, Func<T> customConstructor)
{
DefaultTransforms = defaultTransforms;
CustomConstructor = customConstructor;
}
}
}
25 changes: 25 additions & 0 deletions src/ForeverFactory/Builders/ILinkedBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;

namespace ForeverFactory.Builders
{
public interface ILinkedBuilder<out T>
{
/// <summary>
/// Creates a new builder of "T". It will build a new object, in addition to the previous configurations.
/// </summary>
/// <returns>A builder of "T"</returns>
ILinkedOneBuilder<T> PlusOne();

/// <summary>
/// Creates a new set of customizable objects, following the previous sets created used the "Many" or "Plus" methods.
/// </summary>
/// <param name="count">The number of objects to be created.<param>
IManyBuilder<T> Plus(int count);

/// <summary>
/// Builds all the objects configured, including all sets created used the "Many" or "Plus" methods.
/// </summary>
/// <returns>A collection of instances of "T", with all configurations applied.</returns>
IEnumerable<T> Build();
}
}
13 changes: 13 additions & 0 deletions src/ForeverFactory/Builders/ILinkedOneBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace ForeverFactory.Builders
{
public interface ILinkedOneBuilder<out T> : ILinkedBuilder<T>
{
/// <summary>
/// Defines the default value of a property.
/// </summary>
/// <param name="setMember">Sets the value of a Property. <example>x => x.Name = "Karen"</example>></param>
ILinkedOneBuilder<T> With<TValue>(Func<T, TValue> setMember);
}
}
15 changes: 1 addition & 14 deletions src/ForeverFactory/Builders/IManyBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;

namespace ForeverFactory.Builders
{
/// <summary>
/// This interface allows building many customized objects of type "T".
/// </summary>
/// <typeparam name="T">The type of objects that will be built.</typeparam>
public interface IManyBuilder<out T>
public interface IManyBuilder<out T> : ILinkedBuilder<T>
{
/// <summary>
/// Defines the default value of a property for all instances.
Expand All @@ -28,17 +27,5 @@ public interface IManyBuilder<out T>
/// <param name="count">How many instances will have this property set.</param>
/// <param name="setMember">Sets the value of a Property. <example>x => x.Name = "Karen"</example></param>
IManyBuilder<T> WithLast<TValue>(int count, Func<T, TValue> setMember);

/// <summary>
/// Creates a new set of customizable objects, following the previous sets created used the "Many" or "Plus" methods.
/// </summary>
/// <param name="count">The number of objects to be created.<param>
IManyBuilder<T> Plus(int count);

/// <summary>
/// Builds all the objects configured, including all sets created used the "Many" or "Plus" methods.
/// </summary>
/// <returns>A collection of instances of "T", with all configurations applied.</returns>
IEnumerable<T> Build();
}
}
16 changes: 16 additions & 0 deletions src/ForeverFactory/Builders/LinkedBaseBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using ForeverFactory.Builders.Common;

namespace ForeverFactory.Builders
{
internal abstract class LinkedBaseBuilder<T> : BaseBuilder<T>
where T : class
{
protected ILinkedBuilder<T> Previous { get; }

protected LinkedBaseBuilder(ISharedContext<T> sharedContext, ILinkedBuilder<T> previous)
: base(sharedContext)
{
Previous = previous;
}
}
}
87 changes: 87 additions & 0 deletions src/ForeverFactory/Builders/LinkedManyBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ForeverFactory.Builders.Common;
using ForeverFactory.ExecutionContext;
using ForeverFactory.Transforms;
using ForeverFactory.Transforms.Conditions;

namespace ForeverFactory.Builders
{
internal class LinkedManyBuilder<T> : LinkedBaseBuilder<T>, IManyBuilder<T>
where T : class
{
private readonly int _quantityToProduce;

public LinkedManyBuilder(int quantityToProduce, ISharedContext<T> sharedContext, ILinkedBuilder<T> previous = null)
: base(sharedContext, previous)
{
_quantityToProduce = quantityToProduce;
}

public IManyBuilder<T> With<TValue>(Func<T, TValue> setMember)
{
AddTransform(new FuncTransform<T,TValue>(setMember, Conditions.NoConditions()));
return this;
}

public IManyBuilder<T> WithFirst<TValue>(int count, Func<T, TValue> setMember)
{
ValidateCount(count);

AddTransform(new FuncTransform<T,TValue>(setMember, Conditions.ToApplyFirst(count, GetExecutionContext())));
return this;
}

public IManyBuilder<T> WithLast<TValue>(int count, Func<T, TValue> setMember)
{
ValidateCount(count);

AddTransform(new FuncTransform<T,TValue>(setMember, Conditions.ToApplyLast(count, GetExecutionContext())));
return this;
}

private void ValidateCount(int count)
{
if (count > _quantityToProduce)
{
throw new ArgumentOutOfRangeException("count", count,
$"Count should be less or equal to the set size ({_quantityToProduce})");
}
}

private InstanceSetExecutionContext GetExecutionContext() => new InstanceSetExecutionContext(_quantityToProduce);

public ILinkedOneBuilder<T> PlusOne()
{
return new LinkedOneBuilder<T>(SharedContext, previous: this);
}

public IManyBuilder<T> Plus(int count)
{
return new LinkedManyBuilder<T>(count, SharedContext, previous: this);
}

public IEnumerable<T> Build()
{
foreach (var linkedInstance in Previous?.Build() ?? Enumerable.Empty<T>())
{
yield return linkedInstance;
}

for (var i = 0; i < _quantityToProduce; i++)
{
var instance = CreateInstance();
foreach (var transform in GetTransformsToApply())
{
if (transform.ConditionToApply.CanApplyFor(index: i) is false)
continue;

transform.ApplyTo(instance);
}

yield return instance;
}
}
}
}
Loading

0 comments on commit 3450a1e

Please sign in to comment.