I'm playing with NSpec and I'm confused with the before example:
void they_are_loud_and_emphatic()
{
//act runs after all the befores, and before each spec
//declares a common act (arrange, act, assert) for all subcontexts
act = () => sound = sound.ToUpper() + "!!!";
context["given bam"] = () =>
{
before = () => sound = "bam";
it["should be BAM!!!"] =
() => sound.should_be("BAM!!!");
};
}
string sound;
It works, but when I make the next change:
void they_are_loud_and_emphatic()
{
//act runs after all the befores, and before each spec
//declares a common act (arrange, act, assert) for all subcontexts
act = () => sound = sound.ToUpper() + "!!!";
context["given bam"] = () =>
{
before = () => sound = "b";
before = () => sound += "a";
before = () => sound += "m";
it["should be BAM!!!"] =
() => sound.should_be("BAM!!!");
};
}
string sound;
the string sound only has "M!!!". When I debug the code, it only calls the last before. Perhaps I don't understand the theory, but I believed that all befores lambdas run 'before' the 'act' and the 'it'. What is it wrong?
I use the next syntax and works (external before method and internal in the context):
void they_are_loud_and_emphatic()
{
act = () => sound = sound.ToUpper() + "!!!";
context["given bam"] = () =>
{
before = () =>
{
sound = "b";
sound += "a";
sound += "m";
};
it["should be BAM!!!"] = () => sound.should_be("BAM!!!");
};
}
string sound;
even though it was incremented in the previous example
the before runs again for each spec will be rested.
void they_are_loud_and_emphatic(){
act = () => sound = sound.ToUpper() + "!!!";
context["given bam"] = () =>
{
before = () => sound = "b"; //now sound is B!!!
before = () => sound += "a"; //now sound is A!!!
before = () => sound += "m"; //now sound is M!!!
it["should be BAM!!!"] =
() => sound.should_be("BAM!!!"); // when this line is runing ,sound is"M!!!"
};
}
string sound;
Related
I have the next code, and it was working properly. to execute a request to my method fetchDropdownDataByFederationId, but now I have a requirement to execute the same method x number of times.
fetchInProgress(queryString?): Observable<IPerson[]> {
let PersonList: IPerson[] = [];
return this.getItems<IPerson[]>('', queryString).pipe(
take(1),
switchMap((wls: IPerson[]) => {
PersonList = [...wls];
//const createdbyIds = [...new Set(wls.map((f) => f.createdBy))];
return this.teamPageService.getInformation(wls.createdBy);
}),
map((teams:any) => {
console.log('> teams', teams);
for (let i = 0; i < PersonList.length; i++) {
//update information
}
//console.log('> Final value: ', PersonList);
return PersonList;
})
);
}
But, I'm not finding a way to execute my SwitchMap x number of times and get the results back to use them in my map method to parse the information.
I just moved my SwitchMap to mergeMap, something like this:
mergeMap((wls: IWalklist[]) => {
//let allIds = wls.contact.map(id => this.getSingleData(id._id) );
let drops: Dropdown[] = [];
walklistList = [...wls];
const allIds = [...new Set(wls.map((f) => f.createdBy))];
return forkJoin(...allIds).pipe(
map((idDataArray) => {
drops.push(
this.teamPageService.getInformation('');
);
return drops;
})
)
}),
But still no luck.
Can some help me? how can I fix it?
I'm trying to filter a collection by a multireference field before the function does its job.
I used this wix example but I don't want it to filter the whole collection https://www.wix.com/corvid/example/filter-with-multiple-options
I'm new at this and probably doing it wrong this is what i managed to figure out
import wixData from 'wix-data';
const collectionName = 'Blog/Posts'
//const collectionName = wixData.query('Blog/Posts').contains("categories", ["O -Fitness"]);
const fieldToFilterByInCollection = 'hashtags';
$w.onReady(function () {
setRepeatedItemsInRepeater()
loadDataToRepeater()
$w('#tags').onChange((event) => {
const selectedTags = $w('#tags').value
loadDataToRepeater(selectedTags)
})
});
function loadDataToRepeater(selectedCategories = []) {
let dataQuery = wixData.query(collectionName)//.contains("categories", ["O -Fitness"]);
if (selectedCategories.length > 0) {
dataQuery = dataQuery.hasAll(fieldToFilterByInCollection, selectedCategories)
}
dataQuery
.find()
.then(results => {
const itemsReadyForRepeater = results.items
$w('#Stories').data = itemsReadyForRepeater;
const isRepeaterEmpty = itemsReadyForRepeater.length === 0
if (isRepeaterEmpty) {
$w('#noResultsFound').show()
} else {
$w('#noResultsFound').hide()
}
})
}
function setRepeatedItemsInRepeater() {
$w('#Stories').onItemReady(($item, itemData) => {
$item('#image').src = itemData.coverImage;
$item('#title').text = itemData.title;
if ($item("#title").text.length > 40){
$item("#title").text =$item("#title").text.slice(0, 40) + '...' ;}
$item('#excerpt').text = itemData.excerpt;
if ($item('#excerpt').text.length > 100){
$item('#excerpt').text =$item('#excerpt').text.slice(0, 100) + '...' ;}
})
}
its this commented bit I'm trying to add
const collectionName = wixData.query('Blog/Posts').contains("categories", ["O -Fitness"]);
Thanks in advance
You used 'hasAll' to filter multi-refernce field. 'hasSome' working on multi-refernce but 'hasAll' isnt working on this field type.
you can use:
selectedCategories.map(category => {
dataQuery = dataQuery.hasSome(fieldToFilterByInCollection, category)
})
which is the same as hasAll - hasSome(x) & hasSome(Y) = hasAll(x,y) - but beacuse 'hasSome' is working on multirefernce it will work:)
Declared like this:
public panel$: BehaviorSubject<any> = new BehaviorSubject<any>(false);
Used like this
togglepanel() {
this.panel$.subscribe(
(x) => {
if (x) {
this.panel$.next(false);
} else {
this.panel$.next(true);
}
});
}
It creates an endless cycle trying to update self.
You can update it by taking only one(latest) value from the panel$ Observable:
this.panel$.take(1).subscribe(...)
But it is better to model your state a bit differently, like this:
// const onToggle$ = new Rx.Subject();
var toggle$ = Rx.Observable.fromEvent(document, 'click');
const initialValue = true;
const state$ = toggle$
.scan(state => !state, initialValue)
.startWith(initialValue);
const htmlSubscription = state$.subscribe(state => {
document.getElementById('toggle').innerText = 'toggle: ' + state;
});
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>
<button id="toggle">loading...</button>
EDIT:
Angular version of this code is:
public toggle$ = new Subject();
public state$ = this.toggle$.scan(state => !state, true)
.startWith(true)
.subscribe((x) => console.log('x:' + x));
togglepanel() {
this.toggle$.next(null);
}
I'm pretty new to Reactive Programming but already in love. However it is still hard to switch my brain to it. I'm trying to follow all recommendations as "Avoid using subjects" and "Avoid impure functions" and of course "Avoid imperative code."
What I'm finding hard to achieve is simple cross modules communications where one module can register "action"/observable and the other could subscribe and react to it. A simple message bus will probably work but this will enforce the usage of Subjects and imperative code style which I'm trying to avoid.
So here is a simple starting point I'm playing with:
// some sandbox
class Api {
constructor() {
this.actions = {};
}
registerAction(actionName, action) {
// I guess this part will have to be changed
this.actions[actionName] = action.publishReplay(10).refCount();
//this.actions[actionName].connect();
}
getAction(actionName) {
return this.actions[actionName];
}
}
const api = new Api();
// -------------------------------------------------------------------
// module 1
let myAction = Rx.Observable.create((obs) => {
console.log("EXECUTING");
obs.next("42 " + Date.now());
obs.complete();
});
api.registerAction("myAction", myAction);
let myTrigger = Rx.Observable.interval(1000).take(2);
let executedAction = myTrigger
.flatMap(x => api.getAction("myAction"))
.subscribe(
(x) => { console.log(`executed action: ${x}`); },
(e) => {},
() => { console.log("completed");});
// -------------------------------------------------------------------
// module 2
api.getAction("myAction")
.subscribe(
(x) => { console.log(`SECOND executed action: ${x}`); },
(e) => {},
() => { console.log("SECOND completed");});
So currently at the moment the second module subscribes it "triggers" the "myAction" Observable. And in a real life scenario that could be an ajax call. Is there any way to make all subscribers delay/wait until "myAction" is called properly from module1? And again - its easy to do it using subjects but I'm trying to do it following recommended practices.
If I understand you correctly, you want to make the sure that, if you call the api.getAction, you want next values in that observable to wait till the call to the getAction completes. Before handling other values.
This is something you can achieve quite easily using the concatMap. ConcatMap will take a function that returns an observable (in your case the call to the getAction). ConcatMap will wait to start handling the next value, until the observable returned in the function completes.
So if you change your code like this, it should work (if I understood correctly).
let executedAction = myTrigger
.concatMap(x => api.getAction("myAction"))
.subscribe(
(x) => { console.log(`executed action: ${x}`); },
(e) => {},
() => { console.log("completed");});
If myTrigger has a new value, it will not be handled until the observable returned from api.getAction completes.
So here is a much simpler solution than the one I thought. With simply using 2 observables. Similar effect could be achieved with schedulers and subscribeOn.
// some sandbox
class Action {
constructor(name, observable) {
this.name = name;
this.observable = observable;
this.replay = new Rx.ReplaySubject(10);
}
}
function actionFactory(action, param) {
return Rx.Observable.create(obs => {
action.observable
.subscribe(x => {
obs.next(x);
action.replay.next(x);
}, (e) => {}, () => obs.complete);
});
}
class Api {
constructor() {
this.actions = {};
}
registerAction(actionName, action) {
let generatedAction = new Action(actionName, action);
this.actions[actionName] = generatedAction;
return actionFactory.bind(null, generatedAction);
}
getAction(actionName) {
return this.actions[actionName].replay;
}
}
const api = new Api();
// -------------------------------------------------------------------
// module 1
let myAction = Rx.Observable.create((obs) => {
obs.next("42 " + Date.now());
obs.complete();
});
let myRegisteredAction$ = api.registerAction("myAction", myAction);
let myTrigger = Rx.Observable.interval(1000).take(1).delay(1000);
let executedAction = myTrigger
.map(x => { return { someValue: x} })
.concatMap(x => myRegisteredAction$(x))
.subscribe(
(x) => { console.log(`MAIN: ${x}`); },
(e) => { console.log("error", e)},
() => { console.log("MAIN: completed");});
// -------------------------------------------------------------------
// module 2
var sub = api.getAction("myAction")
.subscribe(
(x) => { console.log(`SECOND: ${x}`); },
(e) => {console.log("error : " + e)},
() => { console.log("SECOND: completed");});
I am attempting to see if the results of a view model are performing the correct actions.
My observables are setup as follows:
public FilterBoxViewModel()
{
var asyncFilterResults = this.filterItemsCommand.RegisterAsyncTask(x =>
this.PerformFilter(x as string));
this.filteredItems = new ObservableAsPropertyHelper<IEnumerable<IFilterBoxItem>>(
asyncFilterResults, _ => this.RaisePropertyChanged("FilteredItems"));
this.WhenAnyValue(x => x.SearchTerm)
.Throttle(TimeSpan.FromMilliseconds(50))
.Skip(1)
.Subscribe(this.filterItemsCommand.Execute);
}
Then further down I have
private async Task<IEnumerable<IFilterBoxItem>> PerformFilter(string searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm))
{
return Enumerable.Empty<IFilterBoxItem>();
}
// Perform getting the items on the main thread and async await the results.
// This is provide a immutable version of the results so we don't cause
// threading issues.
var items = await Observable.Start(
() => this.FilterBoxManager.RootElements.GetAllItemsEnumerable()
.ToList().Select(x => new { Name = x.Name, Item = x }),
RxApp.MainThreadScheduler);
return
items.Where(x =>
x.Name.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0)
.Select(x => x.Item);
}
In my test, I am running the test schedular and advancing it, however, I am getting the PerformFilter performing at different times than I expect
eg my test is:
(new TestScheduler()).With(scheduler =>
{
var viewModel = new FilterBoxViewModel();
var testManager = new TestManager { RootElements = this.sampleItems };
viewModel.FilterBoxManager = testManager;
viewModel.SearchTerm = "folder";
scheduler.AdvanceBy(TimeSpan.FromMilliseconds(51).Ticks);
Assert.AreEqual(viewModel.FilteredItems.Select(x => x.Name), folderSearchResults);
viewModel.SearchTerm = "apple";
Assert.AreEqual(viewModel.FilteredItems.Select(x => x.Name), appleSearchResults);
});
How do I make the tester more predictable?
I am running ReactiveUI 5.5.1 and in a XAML application.
Your Throttle doesn't set a scheduler, this is a classic TestScheduler mistake