Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Operators for IObservable<Optional<T>> #741

Conversation

dwcullop
Copy link
Member

@dwcullop dwcullop commented Oct 22, 2023

Description

This PR adds some extension methods to IObservable<Optional<T>> to enable the same fluent syntax for IObservable<Optional<T>> that exists for Optional<T>. The operations are the identical to the single Optional versions except they apply to every value that comes through the observable event stream.

Examples Usage

Some basic usage shown below:

// Assume These
IObservable<Optional<string>> GetOptionalStringObservable();
Optional<int> ParseInt(string input) =>
    int.TryParse(input, out var result) ? Optional.Some(result) : Optional.None<int>();
Optional<int> GetFallbackValue();

IObservable<int> intObservable = GetOptionalStringObservable()
                                       .Convert(ParseInt)
                                       .OnHasValue(i => Console.WriteLine("Parsed: " + i.ToString()))
                                       .OrElse(GetFallbackValue);

Combine them with other Observable Optional functionality, such as that from #740.

const int MagicId = 37;
var sourceCache = new SourceCache<Person, int>(p => p.Id);

using var cleanup1 = sourceCache.ToOptionalObservable(MagicId , initialOptionalWhenMissing: true)
                        .OnHasNoValue(() => Console.WriteLine("Person is not present"))
                        .Convert(p => p.FullName)
                        .SelectValues()
                        .Subscribe(name => Console.WriteLine("Person's Full Name: " + name));

using var cleanup2 = sourceCache.ToOptionalObservable(MagicId , initialOptionalWhenMissing: false)
                        .OnHasValue(p => Console.WriteLine("Person Added: " + p.FullName))
                        .ValueOrThrow(() => new Exception("Person was removed!"))
                        .Subscribe();

It can also be used with the extension methods added in #739 to create this advanced helper functions that syncs a value between an observable cache and a source cache:

(There are better ways to implement such a thing, this is just an example.)

IDisposable SyncValue<TSource, TKey, TDest, TDestKey>(IObservableCache<TSource, TKey> source, ISourceCache<TDest, TDestKey> destination, TKey syncValueKey, Func<TSource, TDest> converter) =>
    source.Connect()
        .ToOptionalObservable(syncValueKey)  // Create Observable Optional
        .Convert(converter)                  // Convert to Destination Type
        .EditDiff(destination.KeySelector)   // Convert to Change Set
        .PopulateInto(destination);          // Merge into Destination

@codecov
Copy link

codecov bot commented Oct 22, 2023

Codecov Report

Merging #741 (3005431) into main (61900ef) will decrease coverage by 8.19%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main     #741      +/-   ##
==========================================
- Coverage   72.48%   64.29%   -8.19%     
==========================================
  Files         221      222       +1     
  Lines       11176    11224      +48     
  Branches        0     2271    +2271     
==========================================
- Hits         8101     7217     -884     
+ Misses       3075     3062      -13     
- Partials        0      945     +945     
Files Coverage Δ
...c/DynamicData/Kernel/OptionObservableExtensions.cs 100.00% <100.00%> (ø)

... and 142 files with indirect coverage changes

@JakenVeina
Copy link
Collaborator

JakenVeina commented Oct 24, 2023

All tests are passing on both dwcullop:feature/observable-optional-extensions' and reactivemarbles:main, but dwcullop:feature/observable-optional-extensions' is five commits out-of-date from reactivemarbles:main. I don't know what kinda conflicts ought to be introduced from you only adding files, but that's where the failures have to be coming from, conflicts that the auto-merge isn't smart enough to resolve correctly. Either run a merge from to dwcullop:feature/observable-optional-extensions', or re-base dwcullop:feature/observable-optional-extensions' into reactivemarbles:main to resolve whatever conflicts are causing the failures.

@dwcullop
Copy link
Member Author

dwcullop commented Oct 25, 2023

Thanks for looking at this.

I think it is up to date. 2 days ago I updated it. I tried to sync again just now and it already has all the commits. This branch is 5 commits ahead, but that's what I'm trying to merge.

Screenshot_20231025_003942_Chrome

The issue isn't a merge conflict. It is saying the coverage level of the unit tests drops below an acceptable level, which is odd because the one file I'm adding has 100% coverage.

@RolandPheasant
Copy link
Collaborator

The issue isn't a merge conflict. It is saying the coverage level of the unit tests drops below an acceptable level, which is odd because the one file I'm adding has 100% coverage.

This is odd and looks like the code coverage tool is confused.

@RolandPheasant RolandPheasant self-requested a review October 25, 2023 09:01
@glennawatson
Copy link
Member

Codecov recently had an upgrade to its algorithm so suspect it is that. If you check it in it'll just be at that level.

@RolandPheasant RolandPheasant merged commit b01fe12 into reactivemarbles:main Oct 25, 2023
@JakenVeina
Copy link
Collaborator

Thanks for looking at this.

I think it is up to date. 2 days ago I updated it. I tried to sync again just now and it already has all the commits. This branch is 5 commits ahead, but that's what I'm trying to merge.

Screenshot_20231025_003942_Chrome

The issue isn't a merge conflict. It is saying the coverage level of the unit tests drops below an acceptable level, which is odd because the one file I'm adding has 100% coverage.

Ah, my mistake, I misread it. Test coverage, not test results.

Copy link

github-actions bot commented Nov 9, 2023

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 9, 2023
@dwcullop dwcullop deleted the feature/observable-optional-extensions branch November 23, 2023 00:57
@dwcullop dwcullop self-assigned this Dec 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants