Skip to content

Commit

Permalink
Release/3.7 (#91)
Browse files Browse the repository at this point in the history
* Allow observer callback code to unsubscribe from action notifications (Fixes #84)

* Throw exception if base.OnInitialzed not called (Fixes #82)

* Remove unneeded reflection (Fixes #87) (#90)

* Include ActionSubscriber tutorial in solution
  • Loading branch information
mrpmorris authored Aug 26, 2020
1 parent 118de24 commit 800dee9
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 32 deletions.
14 changes: 12 additions & 2 deletions Docs/releases.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
# Releases

### New in 3.7
* Fix for ([#84](https://github.com/mrpmorris/Fluxor/issues/84) -
Allow observer to unsubscribe from all subscriptions whilst executing
the callback from a previous subscription
* Fix for ([#82](https://github.com/mrpmorris/Fluxor/issues/82)) -
Throw an informative exception when `FluxorComponent` or `FluxorLayout`
has been inherited and the descendant doesn't call `base.OnInitialized()`.
* Fix for ([#77](https://github.com/mrpmorris/Fluxor/issues/87)) -
Exception thrown initialising store in .NET 5.

### New in 3.6
* Ensure synchronous effects are executed synchronously ([#76](https://github.com/mrpmorris/fluxor/issues/76)) -
Reverts changes for [(#74) Endless loop redirects](https://github.com/mrpmorris/Fluxor/issues/74) as
these are no longer occur.
Reverts changes for [(#74) Endless loop redirects](https://github.com/mrpmorris/Fluxor/issues/74) as
these are no longer occur.

### New in 3.5
* Bug fix for ([#74](https://github.com/mrpmorris/Fluxor/issues/74)) - Handle endless loop redirects caused by Routing middleware.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RazorLangVersion>3.0</RazorLangVersion>
<Version>3.6.0</Version>
<Version>3.7.0</Version>
<Authors>Peter Morris</Authors>
<Company />
<Product>ReduxDevTools for Fluxor Blazor (Web)</Product>
Expand All @@ -17,8 +17,8 @@
<RepositoryType>git</RepositoryType>
<PackageTags>Redux Flux DotNet CSharp Blazor RazorComponents ReduxDevTools</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyVersion>3.6.0.0</AssemblyVersion>
<FileVersion>3.6.0.0</FileVersion>
<AssemblyVersion>3.7.0.0</AssemblyVersion>
<FileVersion>3.7.0.0</FileVersion>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>MrPMorris.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
Expand Down
3 changes: 3 additions & 0 deletions Source/Fluxor.Blazor.Web/Components/FluxorComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (StateSubscription == null)
throw new NullReferenceException(ErrorMessages.ForgottenToCallBaseOnInitialized);

StateSubscription.Dispose();
ActionSubscriber?.UnsubscribeFromAllActions(this);
}
Expand Down
3 changes: 3 additions & 0 deletions Source/Fluxor.Blazor.Web/Components/FluxorLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (StateSubscription == null)
throw new NullReferenceException(ErrorMessages.ForgottenToCallBaseOnInitialized);

StateSubscription.Dispose();
ActionSubscriber?.UnsubscribeFromAllActions(this);
}
Expand Down
8 changes: 8 additions & 0 deletions Source/Fluxor.Blazor.Web/ErrorMessages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Fluxor.Blazor.Web
{
internal class ErrorMessages
{
internal const string ForgottenToCallBaseOnInitialized =
"Have you forgotten to call base.OnInitialized() in your component?";
}
}
6 changes: 3 additions & 3 deletions Source/Fluxor.Blazor.Web/Fluxor.Blazor.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<RazorLangVersion>3.0</RazorLangVersion>
<Authors>Peter Morris</Authors>
<Company />
<Version>3.6.0</Version>
<Version>3.7.0</Version>
<Description>A zero boilerplate Redux/Flux framework for Blazor</Description>
<Copyright>Peter Morris</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand All @@ -17,8 +17,8 @@
<PackageTags>Redux Flux DotNet CSharp Blazor RazorComponents</PackageTags>
<Product>Fluxor for Blazor (Web)</Product>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyVersion>3.6.0.0</AssemblyVersion>
<FileVersion>3.6.0.0</FileVersion>
<AssemblyVersion>3.7.0.0</AssemblyVersion>
<FileVersion>3.7.0.0</FileVersion>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>MrPMorris.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
Expand Down
18 changes: 15 additions & 3 deletions Source/Fluxor.sln
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,8 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicConcepts.StateActionsReducersTutorial", "..\Tutorials\01-BasicConcepts\01A-StateActionsReducersTutorial\StateActionsReducersTutorial\BasicConcepts.StateActionsReducersTutorial.csproj", "{5E423494-7C7A-485E-8E84-A5CD11F7C504}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01E-ActionSubscriber", "01E-ActionSubscriber", "{6118BA67-8200-42C8-8A6D-B65E085E07A0}"
ProjectSection(SolutionItems) = preProject
..\Tutorials\01-BasicConcepts\01E-ActionObserver\README.md = ..\Tutorials\01-BasicConcepts\01E-ActionObserver\README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicConcepts.ActionSubscriber", "..\Tutorials\01-BasicConcepts\01E-ActionSubscriber\ActionSubscriber\BasicConcepts.ActionSubscriber.csproj", "{E7D0352B-16E1-4A89-8C29-ED119FF03316}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -323,6 +322,18 @@ Global
{5E423494-7C7A-485E-8E84-A5CD11F7C504}.Release|iPhone.Build.0 = Release|Any CPU
{5E423494-7C7A-485E-8E84-A5CD11F7C504}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{5E423494-7C7A-485E-8E84-A5CD11F7C504}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Debug|iPhone.Build.0 = Debug|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Release|Any CPU.Build.0 = Release|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Release|iPhone.ActiveCfg = Release|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Release|iPhone.Build.0 = Release|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{E7D0352B-16E1-4A89-8C29-ED119FF03316}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -353,6 +364,7 @@ Global
{A56E9867-7EB6-4EF6-A641-737C77E62B37} = {14D676FF-AC1C-4912-81D0-9B98B8B090D8}
{5E423494-7C7A-485E-8E84-A5CD11F7C504} = {4DB29E0A-3D0F-4CD5-AD58-3701BF3C23CC}
{6118BA67-8200-42C8-8A6D-B65E085E07A0} = {755410A3-91C0-4CD3-B234-1DEAE35F19DA}
{E7D0352B-16E1-4A89-8C29-ED119FF03316} = {6118BA67-8200-42C8-8A6D-B65E085E07A0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B1F2E0DF-C651-48A3-83E3-3B77D34EC3A2}
Expand Down
3 changes: 2 additions & 1 deletion Source/Fluxor/ActionSubscriber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public void Notify(object action)
SubscriptionsForType
.Where(x => x.Key.IsAssignableFrom(action.GetType()))
.SelectMany(x => x.Value)
.Select(x => x.Callback);
.Select(x => x.Callback)
.ToArray();
foreach (Action<object> callback in callbacks)
callback(action);
});
Expand Down
6 changes: 3 additions & 3 deletions Source/Fluxor/Fluxor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFrameworks>netstandard2.1;netcoreapp3.1</TargetFrameworks>
<Version>3.6.0</Version>
<Version>3.7.0</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Peter Morris</Authors>
<Company />
Expand All @@ -16,8 +16,8 @@
<PackageLicenseFile></PackageLicenseFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<AssemblyVersion>3.6.0.0</AssemblyVersion>
<FileVersion>3.6.0.0</FileVersion>
<AssemblyVersion>3.7.0.0</AssemblyVersion>
<FileVersion>3.7.0.0</FileVersion>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>MrPMorris.snk</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
Expand Down
10 changes: 1 addition & 9 deletions Source/Fluxor/Store.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,13 @@ public class Store : IStore, IDispatcher, IActionSubscriber
private volatile int BeginMiddlewareChangeCount;
private volatile bool HasActivatedStore;
private bool IsInsideMiddlewareChange => BeginMiddlewareChangeCount > 0;
private Action<IFeature, object> IFeatureReceiveDispatchNotificationFromStore;

/// <summary>
/// Creates an instance of the store
/// </summary>
public Store()
{
ActionSubscriber = new ActionSubscriber();

MethodInfo dispatchNotifictionFromStoreMethodInfo =
typeof(IFeature)
.GetMethod(nameof(IFeature.ReceiveDispatchNotificationFromStore));
IFeatureReceiveDispatchNotificationFromStore = (Action<IFeature, object>)
Delegate.CreateDelegate(typeof(Action<IFeature, object>), dispatchNotifictionFromStoreMethodInfo);

Dispatch(new StoreInitializedAction());
}

Expand Down Expand Up @@ -283,7 +275,7 @@ private void DequeueActions()

// Notify all features of this action
foreach (var featureInstance in FeaturesByName.Values)
IFeatureReceiveDispatchNotificationFromStore(featureInstance, nextActionToProcess);
featureInstance.ReceiveDispatchNotificationFromStore(nextActionToProcess);

ExecuteMiddlewareAfterDispatch(nextActionToProcess);
TriggerEffects(nextActionToProcess);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,58 @@
using Fluxor.Blazor.Web.UnitTests.SupportFiles;
using System;
using Xunit;

namespace Fluxor.Blazor.Web.UnitTests.Components.FluxorComponentTests
{
public class DisposeTests
{
private readonly FluxorComponentWithStateProperties Subject;
private readonly FluxorComponentWithStateProperties StateSubject;
private readonly MockState<int> MockState1;
private readonly MockState<int> MockState2;

[Fact]
public void UnsubscribesFromStateProperties()
{
Subject.ExecuteOnInitialized();
Subject.Dispose();
StateSubject.ExecuteOnInitialized();
StateSubject.Dispose();

Assert.Equal(1, MockState1.UnsubscribeCount);
Assert.Equal(1, MockState2.UnsubscribeCount);
}

[Fact]
public void WhenBaseOnInitializedWasNotCalled_ThenThrowsNullReferenceException()
{
string errorMessage = null;
var component = new FluxorComponentThatOptionallyCallsBaseOnInitialized();
try
{
component.Test_OnInitialized();
component.Dispose();
}
catch (NullReferenceException e)
{
errorMessage = e.Message;
}
Assert.Equal("Have you forgotten to call base.OnInitialized() in your component?", errorMessage);
}

[Fact]
public void WhenBaseOnInitializedWasCalled_ThenDoesNotThrowAnException()
{
var component = new FluxorComponentThatOptionallyCallsBaseOnInitialized
{
CallBaseOnInitialized = true
};
component.Test_OnInitialized();
component.Dispose();
}

public DisposeTests()
{
MockState1 = new MockState<int>();
MockState2 = new MockState<int>();
Subject = new FluxorComponentWithStateProperties
StateSubject = new FluxorComponentWithStateProperties
{
State1 = MockState1,
State2 = MockState2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,57 @@
using Fluxor.Blazor.Web.UnitTests.SupportFiles;
using System;
using Xunit;

namespace Fluxor.Blazor.Web.UnitTests.Components.FluxorLayoutTests
{
public class DisposeTests
{
private readonly FluxorLayoutWithStateProperties Subject;
private readonly FluxorLayoutWithStateProperties StateSubject;
private readonly MockState<int> MockState1;
private readonly MockState<int> MockState2;

[Fact]
public void UnsubscribesFromStateProperties()
{
Subject.ExecuteOnInitialized();
Subject.Dispose();
StateSubject.ExecuteOnInitialized();
StateSubject.Dispose();

Assert.Equal(1, MockState1.UnsubscribeCount);
Assert.Equal(1, MockState2.UnsubscribeCount);
}

[Fact]
public void WhenBaseOnInitializedWasNotCalled_ThenThrowsNullReferenceException()
{
string errorMessage = null;
var layout = new FluxorLayoutThatOptionallyCallsBaseOnInitialized();
try
{
layout.Test_OnInitialized();
layout.Dispose();
}
catch (NullReferenceException e)
{
errorMessage = e.Message;
}
Assert.Equal("Have you forgotten to call base.OnInitialized() in your component?", errorMessage);
}

[Fact]
public void WhenBaseOnInitializedWasCalled_ThenDoesNotThrowAnException()
{
var layout = new FluxorLayoutThatOptionallyCallsBaseOnInitialized
{
CallBaseOnInitialized = true
};
layout.Test_OnInitialized();
layout.Dispose();
}
public DisposeTests()
{
MockState1 = new MockState<int>();
MockState2 = new MockState<int>();
Subject = new FluxorLayoutWithStateProperties
StateSubject = new FluxorLayoutWithStateProperties
{
State1 = MockState1,
State2 = MockState2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Fluxor.Blazor.Web.Components;

namespace Fluxor.Blazor.Web.UnitTests.SupportFiles
{
public class FluxorComponentThatOptionallyCallsBaseOnInitialized : FluxorComponent
{
public bool CallBaseOnInitialized;

protected override void OnInitialized()
{
if (CallBaseOnInitialized)
base.OnInitialized();
}

public void Test_OnInitialized() => OnInitialized();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Fluxor.Blazor.Web.Components;

namespace Fluxor.Blazor.Web.UnitTests.SupportFiles
{
public class FluxorLayoutThatOptionallyCallsBaseOnInitialized : FluxorLayout
{
public bool CallBaseOnInitialized;

protected override void OnInitialized()
{
if (CallBaseOnInitialized)
base.OnInitialized();
}

public void Test_OnInitialized() => OnInitialized();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,24 @@ public void WhenExecuted_ThenOnlyUnsubscribesTheSpecifiedSubscriber()
Assert.Null(actionReceivedBySubscriber1);
Assert.Same(dispatchedAction, actionReceivedBySubscriber2);
}

[Fact]
public void WhenExecutedFromSubscriptionCallback_ThenDoesNotThrowAnError()
{
int executionCount = 0;
var subscriber = new object();
var dispatchedAction = new TestAction();

Subject.SubscribeToAction<TestAction>(subscriber, x =>
{
executionCount++;
Subject.UnsubscribeFromAllActions(subscriber);
});

Subject.Dispatch(dispatchedAction);
Subject.Dispatch(dispatchedAction);

Assert.Equal(1, executionCount);
}
}
}
Loading

0 comments on commit 800dee9

Please sign in to comment.