Skip to content

Commit 49f0df5

Browse files
committed
Merge pull request #14 from robdmoore/listbuilder-tidy-up
Added breaking changes file and tidied up list builder classes
2 parents 3cc5f40 + ac87f8a commit 49f0df5

File tree

8 files changed

+277
-112
lines changed

8 files changed

+277
-112
lines changed

BREAKING_CHANGES.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
NTestDataBuilder Breaking Changes
2+
=================================
3+
4+
Version 2.0.0
5+
-------------
6+
7+
The way that lists are generated no longer uses NBuilder - the new syntax is backwards compatible with NBuilder except that the namespace you need to include is different. You can also refactor your list generation to be a lot more terse, but that is optional. Any `BuildList` extension methods you created will now need to be deleted since they are no longer needed.
8+
9+
### Reason
10+
In order to support a new, much terser syntax for generating lists we rewrote the list generation code ourselves. You can now do this:
11+
12+
```c#
13+
var customers = CustomerBuilder.CreateListOfSize(3)
14+
.TheFirst(1).WithFirstName("Robert")
15+
.TheLast(1).WithEmail("[email protected]")
16+
.BuildList();
17+
```
18+
19+
That's instead of this syntax (which still works as well):
20+
21+
```c#
22+
var customers = CustomerBuilder.CreateListOfSize(3)
23+
.TheFirst(1).With(b => b.WithFirstName("Robert"))
24+
.TheLast(1).With(b => b.WithEmail("[email protected]"))
25+
.BuildList();
26+
```
27+
28+
You also no longer need a custom extension method for the `BuildList` method so you will need to delete any of these that you have created. If you don't use NBuilder's features outside of the list generation you may uninstall the NBuilder package.
29+
30+
### Fix
31+
32+
Simply add the following to the files that generate lists of builders and the existing syntax should work:
33+
34+
```
35+
using NTestDataBuilder.Lists;
36+
```
37+
38+
If you uninstall the NBuilder package then you will need to remove the using statements for that library too.
39+
40+
Also, remove any `BuildList` extension methods you created.

NTestDataBuilder.sln

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 2013
4-
VisualStudioVersion = 12.0.30501.0
4+
VisualStudioVersion = 12.0.30723.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NTestDataBuilder", "NTestDataBuilder\NTestDataBuilder.csproj", "{01E4EE61-AB1A-4177-8B6C-D50205D167A9}"
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NTestDataBuilder.Tests", "NTestDataBuilder.Tests\NTestDataBuilder.Tests.csproj", "{DC49CE57-CB3F-487D-83DC-6A4E78CB908C}"
99
EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BC8508D1-6FCB-46B2-9C14-F41F6AD76B09}"
1111
ProjectSection(SolutionItems) = preProject
12+
BREAKING_CHANGES.md = BREAKING_CHANGES.md
1213
LICENSE = LICENSE
1314
logo.png = logo.png
1415
README.md = README.md
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection;
4+
using Castle.DynamicProxy;
5+
6+
namespace NTestDataBuilder.Lists
7+
{
8+
internal class EnsureAllMethodsVirtual : IProxyGenerationHook
9+
{
10+
public void MethodsInspected()
11+
{
12+
}
13+
14+
public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo)
15+
{
16+
if (new[]{"get_Any", "Build", "AsProxy", "Get", "GetOrDefault", "Set", "Has", "get_ListBuilder", "set_ListBuilder"}.Contains(memberInfo.Name))
17+
return;
18+
throw new InvalidOperationException(string.Format("Tried to build a list with a builder who has non-virtual method. Please make {0} on type {1} virtual.", memberInfo.Name, type.Name));
19+
}
20+
21+
public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo)
22+
{
23+
return true;
24+
}
25+
}
26+
}
Lines changed: 46 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,15 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Reflection;
54
using Castle.DynamicProxy;
65

76
namespace NTestDataBuilder.Lists
87
{
9-
public static class ListBuilderGenerator
10-
{
11-
static ListBuilderGenerator()
12-
{
13-
Generator = new ProxyGenerator(true);
14-
}
15-
16-
public static ProxyGenerator Generator { get; private set; }
17-
}
18-
19-
public static class ListBuilderExtensions
20-
{
21-
public static TBuilder TheFirst<TObject, TBuilder>(this TestDataBuilder<TObject, TBuilder> b, int howMany)
22-
where TObject : class
23-
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
24-
{
25-
return b.ListBuilder.TheFirst(howMany);
26-
}
27-
28-
public static TBuilder TheNext<TObject, TBuilder>(this TestDataBuilder<TObject, TBuilder> b, int howMany)
29-
where TObject : class
30-
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
31-
{
32-
return b.ListBuilder.TheNext(howMany);
33-
}
34-
35-
public static TBuilder TheLast<TObject, TBuilder>(this TestDataBuilder<TObject, TBuilder> b, int howMany)
36-
where TObject : class
37-
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
38-
{
39-
return b.ListBuilder.TheLast(howMany);
40-
}
41-
42-
public static TBuilder ThePrevious<TObject, TBuilder>(this TestDataBuilder<TObject, TBuilder> b, int howMany)
43-
where TObject : class
44-
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
45-
{
46-
return b.ListBuilder.ThePrevious(howMany);
47-
}
48-
49-
public static TBuilder All<TObject, TBuilder>(this TestDataBuilder<TObject, TBuilder> b)
50-
where TObject : class
51-
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
52-
{
53-
return b.ListBuilder.All();
54-
}
55-
56-
public static ListBuilder<TObject, TBuilder> With<TObject, TBuilder>(this TestDataBuilder<TObject, TBuilder> b, Func<TBuilder, TBuilder> modifier)
57-
where TObject : class
58-
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
59-
{
60-
return b.ListBuilder.With(modifier);
61-
}
62-
63-
public static IList<TObject> BuildList<TObject, TBuilder>(this TestDataBuilder<TObject, TBuilder> b)
64-
where TObject : class
65-
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
66-
{
67-
return b.ListBuilder.BuildList();
68-
}
69-
}
70-
71-
public class ListBuilderInterceptor<TObject, TBuilder> : IInterceptor
72-
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
73-
where TObject : class
74-
{
75-
private readonly ListBuilder<TObject, TBuilder> _builder;
76-
77-
public ListBuilderInterceptor(ListBuilder<TObject, TBuilder> builder)
78-
{
79-
_builder = builder;
80-
}
81-
82-
public void Intercept(IInvocation invocation)
83-
{
84-
if (invocation.Method.ReturnType != typeof (TBuilder))
85-
{
86-
throw new InvalidOperationException("Non-fluent builder method invoked while creating a list of builders: " + invocation.Method.Name);
87-
}
88-
89-
_builder.Execute(invocation);
90-
invocation.ReturnValue = _builder.BuilderProxy;
91-
}
92-
}
93-
8+
/// <summary>
9+
/// Class that builds lists of objects by proxying a list of object builders.
10+
/// </summary>
11+
/// <typeparam name="TObject">The type of object being built</typeparam>
12+
/// <typeparam name="TBuilder">The type of builder that is building the object</typeparam>
9413
public class ListBuilder<TObject, TBuilder>
9514
where TBuilder : TestDataBuilder<TObject, TBuilder>, new()
9615
where TObject : class
@@ -99,7 +18,7 @@ public class ListBuilder<TObject, TBuilder>
9918
private int _count = 0;
10019
private readonly List<TBuilder> _list;
10120

102-
public ListBuilder(int size)
21+
internal ListBuilder(int size)
10322
{
10423
BuilderProxy = (TBuilder) ListBuilderGenerator.Generator
10524
.CreateClassProxy(typeof (TBuilder), new ProxyGenerationOptions(new EnsureAllMethodsVirtual()), new ListBuilderInterceptor<TObject, TBuilder>(this));
@@ -109,43 +28,74 @@ public ListBuilder(int size)
10928
_list.Add(new TBuilder());
11029
}
11130

112-
public TBuilder BuilderProxy { get; private set; }
31+
internal TBuilder BuilderProxy { get; private set; }
11332

33+
/// <summary>
34+
/// Will target the first x objects (as specified) for subsequent builder calls (or .With call).
35+
/// You can combine this with .TheNext(y) to target the next y after the first x.
36+
/// </summary>
37+
/// <param name="howMany">The first {howMany} objects should be targeted?</param>
38+
/// <returns>The builder proxy so that calls can continue to be chained</returns>
11439
public TBuilder TheFirst(int howMany)
11540
{
11641
_start = 0;
11742
_count = howMany;
11843
return BuilderProxy;
11944
}
12045

46+
/// <summary>
47+
/// Will target the next x objects (as specified) for subsequent builder calls (or .With call).
48+
/// </summary>
49+
/// <param name="howMany">The next {howMany} objects should be targeted?</param>
50+
/// <returns>The builder proxy so that calls can continue to be chained</returns>
12151
public TBuilder TheNext(int howMany)
12252
{
12353
_start += _count;
12454
_count = howMany;
12555
return BuilderProxy;
12656
}
12757

128-
public TBuilder ThePrevious(int howMany)
58+
/// <summary>
59+
/// Will target the last x objects (as specified) for subsequent builder calls (or .With call).
60+
/// You can combine this with .ThePrevious(y) to target the previous y after the last x.
61+
/// </summary>
62+
/// <param name="howMany">The last {howMany} objects should be targeted?</param>
63+
/// <returns>The builder proxy so that calls can continue to be chained</returns>
64+
public TBuilder TheLast(int howMany)
12965
{
130-
_start -= howMany;
66+
_start = _list.Count - howMany;
13167
_count = howMany;
13268
return BuilderProxy;
13369
}
13470

135-
public TBuilder TheLast(int howMany)
71+
/// <summary>
72+
/// Will target the previous x objects (as specified) for subsequent builder calls (or .With call).
73+
/// </summary>
74+
/// <param name="howMany">The previous {howMany} objects should be targeted?</param>
75+
/// <returns>The builder proxy so that calls can continue to be chained</returns>
76+
public TBuilder ThePrevious(int howMany)
13677
{
137-
_start = _list.Count - howMany;
78+
_start -= howMany;
13879
_count = howMany;
13980
return BuilderProxy;
14081
}
14182

83+
/// <summary>
84+
/// Will target all objects for subsequent builder calls (or .With call).
85+
/// </summary>
86+
/// <returns>The builder proxy so that calls can continue to be chained</returns>
14287
public TBuilder All()
14388
{
14489
_start = 0;
14590
_count = _list.Count;
14691
return BuilderProxy;
14792
}
14893

94+
/// <summary>
95+
/// Will apply the given lambda expression to all builders that are currently targeted (e.g. via .TheFirst, .TheNext, etc. calls).
96+
/// </summary>
97+
/// <param name="modifier">The lambda expression to apply to the targeted builders</param>
98+
/// <returns>The builder proxy so that calls can continue to be chained</returns>
14999
public ListBuilder<TObject, TBuilder> With(Func<TBuilder, TBuilder> modifier)
150100
{
151101
_list.Skip(_start)
@@ -155,6 +105,10 @@ public ListBuilder<TObject, TBuilder> With(Func<TBuilder, TBuilder> modifier)
155105
return this;
156106
}
157107

108+
/// <summary>
109+
/// Builds the list of objects by processing all of the builder calls and then calling .Build on all the builders.
110+
/// </summary>
111+
/// <returns>The list of generated objects</returns>
158112
public IList<TObject> BuildList()
159113
{
160114
return _list.Select(b => b.Build()).ToArray();
@@ -168,23 +122,4 @@ internal void Execute(IInvocation invocation)
168122
.ForEach(b => invocation.Method.Invoke(b, invocation.Arguments));
169123
}
170124
}
171-
172-
public class EnsureAllMethodsVirtual : IProxyGenerationHook
173-
{
174-
public void MethodsInspected()
175-
{
176-
}
177-
178-
public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo)
179-
{
180-
if (new[]{"get_Any", "Build", "AsProxy", "Get", "GetOrDefault", "Set", "Has", "get_ListBuilder", "set_ListBuilder"}.Contains(memberInfo.Name))
181-
return;
182-
throw new InvalidOperationException(string.Format("Tried to build a list with a builder who has non-virtual method. Please make {0} on type {1} virtual.", memberInfo.Name, type.Name));
183-
}
184-
185-
public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo)
186-
{
187-
return true;
188-
}
189-
}
190125
}

0 commit comments

Comments
 (0)