Skip to content

Commit 4f75505

Browse files
authored
Merge pull request #1343 from reactiveui/develop
+semver: feature
2 parents e48c357 + a353559 commit 4f75505

16 files changed

+693
-319
lines changed

CODE_OF_CONDUCT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ This Code of Conduct applies both within project spaces and in public spaces
3535
when an individual is representing the project or its community.
3636

3737
Instances of abusive, harassing, or otherwise unacceptable behavior may be
38-
reported by contacting a project maintainer at [email protected]. All
38+
reported by contacting a project maintainer at [email protected]. All
3939
complaints will be reviewed and investigated and will result in a response that
4040
is deemed necessary and appropriate to the circumstances. Maintainers are
4141
obligated to maintain confidentiality with regard to the reporter of an

README.md

Lines changed: 99 additions & 23 deletions
Large diffs are not rendered by default.

src/ReactiveUI.Tests/ReactiveCommandTest.cs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -311,26 +311,18 @@ public void ExecutePassesThroughParameter()
311311
}
312312

313313
[Fact]
314-
public void ExecuteExecutesOnTheSpecifiedScheduler()
314+
public void ExecuteResultIsDeliveredOnSpecifiedScheduler()
315315
{
316316
(new TestScheduler()).With(sched => {
317-
var execute = Observables.Unit.Delay(TimeSpan.FromSeconds(1), sched);
317+
var execute = Observables.Unit;
318318
var fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: sched);
319-
var isExecuting = fixture
320-
.IsExecuting
321-
.CreateCollection();
319+
var executed = false;
322320

323-
fixture.Execute().Subscribe();
324-
sched.AdvanceByMs(999);
321+
fixture.Execute().Subscribe(_ => executed = true);
325322

326-
Assert.Equal(2, isExecuting.Count);
327-
Assert.False(isExecuting[0]);
328-
Assert.True(isExecuting[1]);
329-
330-
sched.AdvanceByMs(2);
331-
332-
Assert.Equal(3, isExecuting.Count);
333-
Assert.False(isExecuting[2]);
323+
Assert.False(executed);
324+
sched.AdvanceByMs(1);
325+
Assert.True(executed);
334326
});
335327
}
336328

src/ReactiveUI.Tests/ViewLocatorTests.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public void ByDefaultViewModelIsReplacedWithViewWhenDeterminingTheServiceName()
8888
FooViewModel vm = new FooViewModel();
8989

9090
var result = fixture.ResolveView(vm);
91-
Assert.True(result is FooView);
91+
Assert.IsType<FooView>(result);
9292
}
9393
}
9494

@@ -106,7 +106,7 @@ public void TheRuntimeTypeOfTheViewModelIsUsedToResolveTheView()
106106
object vm = new FooViewModel();
107107

108108
var result = fixture.ResolveView(vm);
109-
Assert.True(result is FooView);
109+
Assert.IsType<FooView>(result );
110110
}
111111
}
112112

@@ -125,7 +125,7 @@ public void ViewModelToViewNamingConventionCanBeCustomized()
125125
FooViewModel vm = new FooViewModel();
126126

127127
var result = fixture.ResolveView(vm);
128-
Assert.True(result is FooWithWeirdConvention);
128+
Assert.IsType<FooWithWeirdConvention>(result);
129129
}
130130
}
131131

@@ -143,7 +143,7 @@ public void CanResolveViewFromViewModelClassUsingClassRegistration()
143143
FooViewModel vm = new FooViewModel();
144144

145145
var result = fixture.ResolveView(vm);
146-
Assert.True(result is FooView);
146+
Assert.IsType<FooView>(result);
147147
}
148148
}
149149

@@ -161,7 +161,7 @@ public void CanResolveViewFromViewModelClassUsingInterfaceRegistration()
161161
FooViewModel vm = new FooViewModel();
162162

163163
var result = fixture.ResolveView(vm);
164-
Assert.True(result is FooView);
164+
Assert.IsType<FooView>(result);
165165
}
166166
}
167167

@@ -179,7 +179,7 @@ public void CanResolveViewFromViewModelClassUsingIViewForRegistration()
179179
FooViewModel vm = new FooViewModel();
180180

181181
var result = fixture.ResolveView(vm);
182-
Assert.True(result is FooView);
182+
Assert.IsType<FooView>(result);
183183
}
184184
}
185185

@@ -197,7 +197,7 @@ public void CanResolveViewFromViewModelInterfaceUsingClassRegistration()
197197
IFooViewModel vm = new FooViewModelWithWeirdName();
198198

199199
var result = fixture.ResolveView(vm);
200-
Assert.True(result is FooView);
200+
Assert.IsType<FooView>(result);
201201
}
202202
}
203203

@@ -215,7 +215,7 @@ public void CanResolveViewFromViewModelInterfaceUsingInterfaceRegistration()
215215
IFooViewModel vm = new FooViewModel();
216216

217217
var result = fixture.ResolveView(vm);
218-
Assert.True(result is FooView);
218+
Assert.IsType<FooView>(result);
219219
}
220220
}
221221

@@ -233,7 +233,7 @@ public void CanResolveViewFromViewModelInterfaceUsingIViewForRegistration()
233233
IFooViewModel vm = new FooViewModel();
234234

235235
var result = fixture.ResolveView(vm);
236-
Assert.True(result is FooView);
236+
Assert.IsType<FooView>(result);
237237
}
238238
}
239239

@@ -255,10 +255,10 @@ public void ContractIsUsedWhenResolvingView()
255255
Assert.Null(result);
256256

257257
result = fixture.ResolveView(vm, "first");
258-
Assert.True(result is FooView);
258+
Assert.IsType<FooView>(result);
259259

260260
result = fixture.ResolveView(vm, "second");
261-
Assert.True(result is FooWithWeirdConvention);
261+
Assert.IsType<FooWithWeirdConvention>(result);
262262
}
263263
}
264264

@@ -333,4 +333,4 @@ public void AnErrorIsRaisedIfTheCreationOfTheViewFails()
333333
}
334334
}
335335
}
336-
}
336+
}

src/ReactiveUI/Android/ControlFetcherMixin.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,22 @@ static ControlFetcherMixin()
3030
var assm = AppDomain.CurrentDomain.GetAssemblies()[1];
3131
var resources = assm.GetModules().SelectMany(x => x.GetTypes()).First(x => x.Name == "Resource");
3232

33-
controlIds = resources.GetNestedType("Id").GetFields()
34-
.Where(x => x.FieldType == typeof(int))
35-
.ToDictionary(k => k.Name.ToLowerInvariant(), v => (int)v.GetRawConstantValue());
33+
try {
34+
controlIds = resources.GetNestedType("Id").GetFields()
35+
.Where(x => x.FieldType == typeof(int))
36+
.ToDictionary(k => k.Name.ToLowerInvariant(), v => (int)v.GetRawConstantValue());
37+
} catch (ArgumentException argumentException) {
38+
var duplicates = resources.GetNestedType("Id").GetFields()
39+
.Where(x => x.FieldType == typeof(int))
40+
.GroupBy(k => k.Name.ToLowerInvariant())
41+
.Where(g => g.Count() > 1)
42+
.Select(g => "{ " + string.Join(" = ", g.Select(v => v.Name)) + " }");
43+
44+
if (duplicates.Any())
45+
throw new InvalidOperationException("You're using multiple resource ID's with the same name but with different casings which isn't allowed for WireUpControls: " + string.Join(", ", duplicates), argumentException);
46+
47+
throw argumentException;
48+
}
3649

3750
var type = typeof(ControlFetcherMixin);
3851
getControlActivity = type.GetMethod("GetControl", new[] { typeof(Activity), typeof(string) });

src/ReactiveUI/AutoPersistHelper.cs

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using ReactiveUI;
21
using System;
32
using System.Collections.Generic;
43
using System.Collections.Specialized;
@@ -7,9 +6,8 @@
76
using System.Reactive.Concurrency;
87
using System.Reactive.Disposables;
98
using System.Reactive.Linq;
10-
using System.Runtime.Serialization;
11-
using System.Text;
129
using System.Reflection;
10+
using System.Runtime.Serialization;
1311
using Splat;
1412

1513
namespace ReactiveUI
@@ -33,11 +31,16 @@ public static class AutoPersistHelper
3331
/// Changes to properties not marked with DataMember will not trigger the
3432
/// object to be saved.
3533
/// </summary>
36-
/// <param name="doPersist">The asynchronous method to call to save the
37-
/// object to disk.</param>
38-
/// <param name="interval">The interval to save the object on. Note that
39-
/// if an object is constantly changing, it is possible that it will never
40-
/// be saved.</param>
34+
/// <param name="This">
35+
/// The reactive object to watch for changes
36+
/// </param>
37+
/// <param name="doPersist">
38+
/// The asynchronous method to call to save the object to disk.
39+
/// </param>
40+
/// <param name="interval">
41+
/// The interval to save the object on. Note that if an object is constantly changing,
42+
/// it is possible that it will never be saved.
43+
/// </param>
4144
/// <returns>A Disposable to disable automatic persistence.</returns>
4245
public static IDisposable AutoPersist<T>(this T This, Func<T, IObservable<Unit>> doPersist, TimeSpan? interval = null)
4346
where T : IReactiveObject
@@ -52,13 +55,19 @@ public static IDisposable AutoPersist<T>(this T This, Func<T, IObservable<Unit>>
5255
/// Changes to properties not marked with DataMember will not trigger the
5356
/// object to be saved.
5457
/// </summary>
55-
/// <param name="doPersist">The asynchronous method to call to save the
56-
/// object to disk.</param>
57-
/// <param name="manualSaveSignal">When invoked, the object will be saved
58-
/// regardless of whether it has changed.</param>
59-
/// <param name="interval">The interval to save the object on. Note that
60-
/// if an object is constantly changing, it is possible that it will never
61-
/// be saved.</param>
58+
/// <param name="This">
59+
/// The reactive object to watch for changes
60+
/// </param>
61+
/// <param name="doPersist">
62+
/// The asynchronous method to call to save the object to disk.
63+
/// </param>
64+
/// <param name="manualSaveSignal">
65+
/// When invoked, the object will be saved regardless of whether it has changed.
66+
/// </param>
67+
/// <param name="interval">
68+
/// The interval to save the object on. Note that if an object is constantly changing,
69+
/// it is possible that it will never be saved.
70+
/// </param>
6271
/// <returns>A Disposable to disable automatic persistence.</returns>
6372
public static IDisposable AutoPersist<T, TDontCare>(this T This, Func<T, IObservable<Unit>> doPersist, IObservable<TDontCare> manualSaveSignal, TimeSpan? interval = null)
6473
where T : IReactiveObject
@@ -100,11 +109,16 @@ public static IDisposable AutoPersist<T, TDontCare>(this T This, Func<T, IObserv
100109
/// Apply AutoPersistence to all objects in a collection. Items that are
101110
/// no longer in the collection won't be persisted anymore.
102111
/// </summary>
103-
/// <param name="doPersist">The asynchronous method to call to save the
104-
/// object to disk.</param>
105-
/// <param name="interval">The interval to save the object on. Note that
106-
/// if an object is constantly changing, it is possible that it will never
107-
/// be saved.</param>
112+
/// <param name="This">
113+
/// The reactive collection to watch for changes
114+
/// </param>
115+
/// <param name="doPersist">
116+
/// The asynchronous method to call to save the object to disk.
117+
/// </param>
118+
/// <param name="interval">
119+
/// The interval to save the object on. Note that if an object is constantly changing,
120+
/// it is possible that it will never be saved.
121+
/// </param>
108122
/// <returns>A Disposable to disable automatic persistence.</returns>
109123
public static IDisposable AutoPersistCollection<T>(this IReactiveCollection<T> This, Func<T, IObservable<Unit>> doPersist, TimeSpan? interval = null)
110124
where T : IReactiveObject
@@ -116,13 +130,19 @@ public static IDisposable AutoPersistCollection<T>(this IReactiveCollection<T> T
116130
/// Apply AutoPersistence to all objects in a collection. Items that are
117131
/// no longer in the collection won't be persisted anymore.
118132
/// </summary>
119-
/// <param name="doPersist">The asynchronous method to call to save the
120-
/// object to disk.</param>
121-
/// <param name="manualSaveSignal">When invoked, the object will be saved
122-
/// regardless of whether it has changed.</param>
123-
/// <param name="interval">The interval to save the object on. Note that
124-
/// if an object is constantly changing, it is possible that it will never
125-
/// be saved.</param>
133+
/// <param name="This">
134+
/// The reactive collection to watch for changes
135+
/// </param>
136+
/// <param name="doPersist">
137+
/// The asynchronous method to call to save the object to disk.
138+
/// </param>
139+
/// <param name="manualSaveSignal">
140+
/// When invoked, the object will be saved regardless of whether it has changed.
141+
/// </param>
142+
/// <param name="interval">
143+
/// The interval to save the object on. Note that if an object is constantly changing,
144+
/// it is possible that it will never be saved.
145+
/// </param>
126146
/// <returns>A Disposable to disable automatic persistence.</returns>
127147
public static IDisposable AutoPersistCollection<T, TDontCare>(this IReactiveCollection<T> This, Func<T, IObservable<Unit>> doPersist, IObservable<TDontCare> manualSaveSignal, TimeSpan? interval = null)
128148
where T : IReactiveObject
@@ -150,10 +170,15 @@ public static IDisposable AutoPersistCollection<T, TDontCare>(this IReactiveColl
150170
/// removed from a collection. This class correctly handles both when
151171
/// a collection is initialized, as well as when the collection is Reset.
152172
/// </summary>
153-
/// <param name="onAdd">A method to be called when an object is added
154-
/// to the collection.</param>
155-
/// <param name="onRemove">A method to be called when an object is removed
156-
/// from the collection.</param>
173+
/// <param name="This">
174+
/// The reactive collection to watch for changes
175+
/// </param>
176+
/// <param name="onAdd">
177+
/// A method to be called when an object is added to the collection.
178+
/// </param>
179+
/// <param name="onRemove">
180+
/// A method to be called when an object is removed from the collection.
181+
/// </param>
157182
/// <returns>A Disposable that deactivates this behavior.</returns>
158183
public static IDisposable ActOnEveryObject<T>(this IReactiveCollection<T> This, Action<T> onAdd, Action<T> onRemove)
159184
where T : IReactiveObject

src/ReactiveUI/CompatMixins.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,22 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Reactive.Subjects;
54

65
namespace ReactiveUI
76
{
8-
public static class CompatMixins
7+
internal static class CompatMixins
98
{
109
internal static void ForEach<T>(this IEnumerable<T> This, Action<T> block)
1110
{
1211
foreach (var v in This) {
13-
block(v);
12+
block(v);
1413
}
1514
}
1615

1716
internal static IEnumerable<T> SkipLast<T>(this IEnumerable<T> This, int count)
1817
{
1918
return This.Take(This.Count() - count);
2019
}
21-
22-
internal static IObservable<T> PermaRef<T>(this IConnectableObservable<T> This)
23-
{
24-
This.Connect();
25-
return This;
26-
}
2720
}
2821

2922
// according to spouliot, this is just a string match, and will cause the

src/ReactiveUI/Interactions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public TOutput GetOutput()
115115
/// <para>
116116
/// Note that handlers are not required to handle an interaction. They can choose to ignore it, leaving it
117117
/// for some other handler to handle. If no handler handles the interaction, the <see cref="Handle"/> method
118-
/// will throw an <see cref="UnhandledInteractionException"/>.
118+
/// will throw an <see cref="UnhandledInteractionException{TInput, TOutput}"/>.
119119
/// </para>
120120
/// </remarks>
121121
/// <typeparam name="TInput">
@@ -228,15 +228,15 @@ public IDisposable RegisterHandler<TDontCare>(Func<InteractionContext<TInput, TO
228228
/// <para>
229229
/// This method passes the interaction through to relevant handlers in reverse order of registration,
230230
/// ceasing once any handler handles the interaction. If the interaction remains unhandled after all
231-
/// relevant handlers have executed, an <see cref="UnhandledInteractionException"/> is thrown.
231+
/// relevant handlers have executed, an <see cref="UnhandledInteractionException{TInput, TOutput}"/> is thrown.
232232
/// </para>
233233
/// </remarks>
234234
/// <param name="input">
235235
/// The input for the interaction.
236236
/// </param>
237237
/// <returns>
238238
/// An observable that ticks when the interaction completes, or throws an
239-
/// <see cref="UnhandledInteractionException"/> if no handler handles the interaction.
239+
/// <see cref="UnhandledInteractionException{TInput, TOutput}"/> if no handler handles the interaction.
240240
/// </returns>
241241
public virtual IObservable<TOutput> Handle(TInput input)
242242
{

0 commit comments

Comments
 (0)