Ruby: Hashmap have value as function and its evaluating the function - ruby

I have a hashmap as below
func_hash = {
"update" => update(),
"delete" => delete(),
"get" => get()
}
The behaviour that I am observing is , while ruby trying to read this line, its also calling the function update().
How can I make it work like, call the function only when I call it with specific key like
func_hash["delete"]
Any help, much appreciated.
Thank you.

you could do something like
func_hash = {
"update" => lambda { update() },
"delete" => lambda { delete() },
"get" => lambda { get() }
}
func_hash["delete"].call

Use a lambda like Ursus suggested to just store the name of the function in the hash and use public_send:
func_hash = {
"update" => [:update],
"delete" => [:delete],
"get" => [:get],
"complex" => [:method_name, 'argument', 'another_argument']
}
public_send(*func_hash['complex'])
#=> would call `method_name('argument', 'another_argument')`

Related

using arrow function into the return block of page.evaluate of puppeter script

It probably is a matter of Promise:
Look at the since field of my return block,
using an arrow function it doesn't return any result:
{
link: 'www.xxxxxx.com/1234',
name: 'jhon doe',
since: {}
},
instead to return directly the value, it works as expected!
Since i need to perform complex operations with selectors, I'd like to use an inline arrow function in that point, how can I fix to get the result out?
let rawMembers = await page.evaluate(() => new Promise((resolve) => {
....
//captute all the link
const anchors = Array.from(document.querySelectorAll('a'));
let result = anchors.map(x => {
return {
link: x.getAttribute("href"),
name: x.innerText,
//since : x.parentNode.parentNode.parentNode.parentNode.getAttribute("class") <--- this works
since: x => { <---using an arrow function, it returns and empty objecy `{}`
// i need a function here to do complex and multiline operations
return x.parentNode.parentNode.parentNode.parentNode.getAttribute("class");
}
....
resolve(results);
i've tried this as well but with the same result
since: x => new Promise((resolve) => {
// i need a function here to do complex and multiline operations
resolve(x.parentNode.parentNode.parentNode.parentNode.getAttribute("class"));
})
In since you have a reference to the arrow function itself, not to its result. Functions are not serialaizable, so you get an empty object. You need to call the function, i.e. to use IIFE:
since: (() => {
return x.parentNode.parentNode.parentNode.parentNode.getAttribute("class");
})()

i want simple array not [__ob__: Observer]

i call for the api to fetch data, i test it with postman and laravel send it as an array correctly, but vue turn my array into [ob: Observer] and i cant extract my array from it thanks to #Stockafisso it has been solved but
Edit: now my problem is, programming_languages array is only accesible inside getLanguages() method not anywhere else
data(){
return{
answer: '',
maxWrong: '',
programming_languages : []
}
},
methods:{
getLanguages(){
axios.get('/api/toController').then((response)=> {
this.programming_languages = JSON.parse(JSON.stringify(response.data)); //works fine
this.answer = this.programming_languages[Math.floor(Math.random() * this.programming_languages.length)].name; //works fine
this.maxWrong = this.answer.length;// works fine here, i dont want to initiaize answer variable in this method
});
},
randWord(){
this.answer = this.programming_languages[Math.floor(Math.random() * this.programming_languages.length)].name;// it is not working here
//Error in created hook: "TypeError: Cannot read property 'name' of undefined"
this.maxWrong = this.answer.length;// doesn't work here
}
},
created(){
this.getLanguages();
this.randWord();
}
what can i do?
thank you
most probable the error(s) is here:
this.programming_languages.push(JSON.parse(JSON.stringify(response.data)));
you are inserting the Laravel returned array inside the declared programming_languages, while you should override or concat it.
To explain better, a little example:
consider this:
let arr1 = [];
let arr2 = [1,2,3];
if you do arr1.push(arr2) your arr1 will be [[1,2,3]] while you want it to be [1,2,3].
Going back to your problem, to fix it you have two ways:
a)
.then((response)=> {
this.programming_languages = JSON.parse(JSON.stringify(response.data));
b)
.then((response)=> {
let swapArray = [...this.programming_languages];
this.programming_languages = swapArray.concat(JSON.parse(JSON.stringify(response.data)));
If this does not fix the error,
it could be the problem is in the array returned from Laravel. If it has not numeric or not sorted keys, JS will "fill" the missing spaces with Ob. Observer to try to keep the keys.
thank you all, finally i managed to solve it
first i extract array from vue's [ob: Observer]
this.programming_languages = JSON.parse(JSON.stringify(response.data));
special thanks to #stockafisso
and second was that i couldn't get this programming_languages array out of axios method, that happens due to nature of asynchronous call, see this informative link
How do I return the response from an asynchronous call?
i managed to solve it this way, async and await
methods: {
async getPosts(){
await axios.get('/api/hangman').then(response => {
this.programming_languages = JSON.parse(JSON.stringify(response.data));
}).catch(error => console.log(error));
},
randWord(){
this.answer = this.programming_languages[Math.floor(Math.random() * this.programming_languages.length)].name;
this.maxWrong = this.answer.length;
},
},
created(){
this.getPosts().then(() => this.randWord());
}

Alter data returned by http using map method in rxjs

I have written code like below
getCalculatedValuesViaGet = (myData): Observable < RmdResponse > => {
return this._http.get('https://jsonplaceholder.typicode.com/posts',
{ headers: this.getHeaders })
.map((response: Response) => response.json())
.map(x => { x.title = x.title + "hello world" })
.catch(this.handleError);
}
Basically i want to append some extra info to each object returned from http method. My understanding is that first map converts it to JSON object. Second object should pass each object inside array and make the updation. But whole object is passed to second map method rather than each individual object. Looks like my understanding is wrong.
Since your service is actually returning an array, you should use either .forEach or .map to transform each individual object:
getCalculatedValuesViaGet = (myData): Observable<RmdResponse> => {
return this._http.get('https://jsonplaceholder.typicode.com/posts',
{headers: this.getHeaders})
.map((response: Response) => response.json())
//the following x contains an array of objects, map to transform the data
.map(x => {
//this map is the function of array, not Observable.
//iterate through each object, and update the value of `title`
return x.map(y => Object.assign(y, {title: y.title + 'hello world'}))
}
)
.catch(this.handleError);
}

Proper way to complete an rxjs observable interval?

My scenario is that I add a record set to a host zone via aws sdk. When adding a record set, the aws sdk has a GetChange call that can be used to get that status. Here is the code I am currently doing:
this._adminService.registerDomain(caseWebsiteUrl.Url).
subscribe(id => {
return Observable.interval(5000).flatMap(() => {
return this._adminService.getChange(id);
}).
takeWhile((s) => s.ChangeInfo.Status.Value !== 'INSYNC').subscribe(
() => {
},
() => {
},
() => this.urlStatus = 'fa fa-check');
});
In the above code, I want to call registerDomain and after that has been successful, I want to call getChange every 5 seconds until the Status.Value !== 'INSYNC'
A few questions:
What is flatMap doing?
Is it possible to do this without 2 subscribe calls?
If I don't need the next or error callbacks, but I need the complete, is it necessary to declare empty bodies?
Flatmap aka MergeMap will flatten higher order observables. Thus Observable<Observable<T>> => Observable<T>.
The subscribe inside subscribe is a code smell and can and should be refactored. If you do not need the error/complete handlers you do not need to pass those. For instance:
function registerDomain(caseWebsiteUrl) {
return this._adminService.registerDomain(caseWebsiteUrl.Url)
.concatMap(registerId => Observable.interval(5000)
.mergeMap(() => this._adminService.getChange(registerId))
.takeWhile((info) => info.ChangeInfo.Status.Value !== 'INSYNC')
)
}
registerDomain.subscribe(res => console.log('res:'+res));
This works based on the assumption and limitations that:
registerDomain() returns an Observable which completes
getChange() will eventually return 'INSYNC'
No error handling has been added (for instance a timeout after 30 seconds? Retry if registerDomain() fails?)

Is It good to subscribe to an observable within another subscription handler?

I'm using Angular2 and I have a question about what is the best way to do if I have many observables.
Can I put subscriptions inside each other or put each one in a different method and put the results in class properties?
Example :
ngOnInit() {
this.route.params**.subscribe**(params => {
if (params['id']) {
this.load = true;
this.batchService.getPagesOfCurrentObject(params['id'], "10", "0")
**.subscribe**(result => {
this.stream = result;
if (this.stream.length > 0) {
this.stream.forEach(page => { this.batchService.getPageStreamById
(page.pageId)**.subscribe**(pageStream => {
let base64 = btoa(new Uint8Array(pageStream.data)
.reduce((data, byte)
=> data + String.fromCharCode(byte), ''));
this.pages.push(base64 );
})
return;
});
}
},
error => this.errorService.setError(<any>error),
() => this.load = false
);
}
});
try {
this.customer = this.sharedService.processSelect.subscription.customer;
} catch (err) {
return;
}
}
Having multiple observables is totally fine, this is what reactive programming is about :)
But here your problem is having too much subscribe. Keep in mind that subscribe is a way to create side effect. To have an easy to read code, you should try to use the least possible subscribe.
Your use case is the perfect use case for the mergeMap operator, that allows you to flatten nested observables.
Here what your code would look like
const response$ = this.route.params
.mergeMap(params => {
return this.batchService.getPagesOfCurrentObject(params['id'])
})
.mergeMap(stream => {
return Rx.Observable.merge(stream.map(page => this.batchService.getPageStreamById(page.pageId))
})
.map(pageStream => /* do your stuff with pageStream, base64 ... */)
response$.subscribe(pageStreamData => pages.push(pageStreamData))
See how there is a single subscription that triggers the side-effect that will modify your app's state
Note that I voluntarily simplified the code (removed error handling and checks) for you to get the idea of how to do that.
I hope it will help you thinking in reactive programming :)

Resources